FastJson调用流程

image-20260721150624100

可视化链接:http://101.200.184.201:7070/fastjson/

一、基本使用

1.Fastjson 详解

image-20260413181343703

2.核心功能

1.序列化(Java 对象 → JSON 字符串)

1
2
3
4
5
6
7
8
import com.alibaba.fastjson.JSON;

User user = new User("Eaton", 20);
String json = JSON.toJSONString(user);
// → {"age":20,"name":"Eaton"}

// 格式化输出
String pretty = JSON.toJSONString(user, true);

2.反序列化(JSON 字符串 → Java 对象)

1
2
3
4
5
6
7
8
9
10
11
String json = "{\"name\":\"Eaton\",\"age\":20}";

// 反序列化为指定类
User user = JSON.parseObject(json, User.class);

// 反序列化为 JSONObject(类似 Map)
JSONObject obj = JSON.parseObject(json);
String name = obj.getString("name");

// 反序列化为数组/List
List<User> list = JSON.parseArray(jsonArr, User.class);

3.JSONObject / JSONArray 操作

1
2
3
4
5
6
7
8
9
10
JSONObject obj = new JSONObject();
obj.put("name", "Eaton");
obj.put("score", 99);

JSONArray arr = new JSONArray();
arr.add(obj);
arr.add("extra");

System.out.println(arr.toJSONString());
// → [{"name":"Eaton","score":99},"extra"]

3.漏洞产生

@type是 Fastjson 在 JSON 中嵌入的一个特殊元字段

image-20260413182534062

具体作用场景

1
2
3
4
5
6
7
8
9
10
// 父类引用,实际存的是子类对象
Object obj = new AdminUser("Eaton", "SUPER_ADMIN");

String json = JSON.toJSONString(obj, SerializerFeature.WriteClassName);
// → {"@type":"com.example.AdminUser","name":"Eaton","role":"SUPER_ADMIN"}

// 反序列化时,没有 @type 就不知道要还原成哪个子类
Object result = JSON.parseObject(json);
// 有 @type → 正确还原为 AdminUser
// 无 @type → 只能得到一个普通 JSONObject

本质用途:解决多态反序列化问题,让父类/接口类型的字段能正确还原为具体子类

二、内部具体执行流程

1.识别Type并加载类

1
2
3
4
5
6
JSON#parseObject
JSON#parse
DefaultJSONParser#parse
DefaultJSONParser#parseObject

就是在这里识别到@Type

JSONObject student = JSON.parseObject(jsonString);

com/alibaba/fastjson/JSON#parseObject

image-20260413183419500

com/alibaba/fastjson/JSON#parse

1
2
3
public static Object parse(String text) {
return parse(text, DEFAULT_PARSER_FEATURE);
}

com/alibaba/fastjson/JSON#parse

image-20260413183713963

com/alibaba/fastjson/parser/DefaultJSONParser#parse

image-20260413185011388

com/alibaba/fastjson/parser/DefaultJSONParser#parseObject

image-20260413185339253

接着继续执行

image-20260413192641799

2.类初始化详解

1
2
3
4
5
6
ParserConfig#getDeserializer
ParserConfig#getDeserializer
ParserConfig#createJavaBeanDeserializer
JavaBeanInfo#build

创建类加载器并且返回
1
2
ObjectDeserializer deserializer = config.getDeserializer(clazz);
return deserializer.deserialze(this, clazz, fieldName);

com/alibaba/fastjson/parser/ParserConfig#getDeserializer

image-20260413204027207

com/alibaba/fastjson/parser/ParserConfig#getDeserializer

image-20260413204423645

com/alibaba/fastjson/parser/ParserConfig#createJavaBeanDeserializer

image-20260413204651618

具体如下

com/alibaba/fastjson/util/JavaBeanInfo#build详解

image-20260413210307581

紧接着回到createJavaBeanDeserializer方法

image-20260413211519273

继续回到

image-20260413212839402

与此同时,发现一个极大的问题,就是这里的临时类加载器无法去在我们的idea中去调试,对于下面的调试造成极大的麻烦

3.内置javabean类初始化

image-20260413213850933

  • setter 方法

    image-20260413214419252

  • getter 方法

    image-20260413214918123

com/alibaba/fastjson/util/JavaBeanInfo#add

1
add(fieldList, new FieldInfo(propertyName, method, null, clazz, type, 0, 0, 0, annotation, null, null));

这里有一个new方法,这里出现问题

image-20260413215755594

因此,只需要,我们创建的类中有一个这个方法

1
2
3
4
public Map getMap(){
System.out.println("getMap");
return map;
}

即可成功实现内置类加载器

image-20260413220918137

4.具体调试过程

接上回,已经到了创建内置初始化器

1
2
3
if (!asmEnable) {
return new JavaBeanDeserializer(this, clazz, type);
}

com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer#JavaBeanDeserializer

1
2
3
public JavaBeanDeserializer(ParserConfig config, Class<?> clazz, Type type){
this(config, JavaBeanInfo.build(clazz, type, config.propertyNamingStrategy));
}

这里还会调用一遍之前的 build 方法,不在赘述

com/alibaba/fastjson/parser/ParserConfig#getDeserializer

image-20260414103757654

com/alibaba/fastjson/parser/DefaultJSONParser#parseObject

image-20260414104147515

接着进行具体的反序列化方法

image-20260414104653101

类实例化

image-20260414200803648

  • image-20260414200845739

类赋值

image-20260414201219329

  • image-20260414201730724

然后就结束啦

后面是栈的返回

image-20260414204557236

image-20260414205930078

5.getter调用流程

调用get具体流程

com/alibaba/fastjson/JSON#parseObject

com/alibaba/fastjson/JSON.#toJSON

image-20260414212408359

com/alibaba/fastjson/JSON#toJSON

image-20260414212512929

com/alibaba/fastjson/serializer/JavaBeanSerializer#getFieldValuesMap

image-20260414212621686

com/alibaba/fastjson/serializer/FieldSerializer#getPropertyValue

image-20260414212721985

com/alibaba/fastjson/util/FieldInfo#get

image-20260414212829266

image-20260414212918224

成功调用

总体如下

整体是一个大状态机循环

1
2
3
JSON Token 流:{ "name":"Eaton", "age":20 }
↑ ↑ ↑
LBRACE KEY:VALUE RBRACE
1
2
3
if (token == JSONToken.NULL)  → 直接返回 null
if (token == JSONToken.RBRACE) → 空对象 {},直接 createInstance
if (token == JSONToken.LBRACKET && isSupportArrayToBean) → 走数组模式
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
for (int fieldIndex = 0; ; fieldIndex++) {
// 阶段1:尝试"快速路径"匹配已知字段
if (fieldIndex < sortedFieldDeserializers.length) {
// 按字段类型分发,直接用 lexer 扫描原始字节
// int/long/String/boolean/float/double 都有专用 scanFieldXxx()
// 匹配成功 → matchField=true, valueParsed=true
// 字段名不匹配 → continue(进入下一个已知字段)
}

// 阶段2:快速路径失败,走通用路径
if (!matchField) {
key = lexer.scanSymbol(parser.symbolTable); // 读取 JSON 的 key
// 处理特殊 key:
// "$ref" → 引用解析(处理循环引用)
// "@type" → 多态类型处理
}

// 阶段3:首次进入,创建对象实例
if (object == null && fieldValues == null) {
object = createInstance(parser, type);
}

// 阶段4:赋值
if (matchField) {
fieldDeser.setValue(object, fieldValue); // 直接赋值
} else {
parseField(parser, key, object, type, fieldValues); // 通用字段解析
}
}

6.恶意 get 方法

image-20260414215004648

image-20260414215150958

三、尝试利用一下

1
2
String s = "{\"@type\":\"Test\",\"cmd\":\"calc\"}";
JSONObject jsonObject = JSON.parseObject(s);
1
2
3
4
5
6
7
public class Test {
public void setCmd(String cmd) throws IOException {
Runtime.getRuntime().exec(cmd);

}
}

image-20260414220454592

FastJSON 在反序列化时,不依赖字段存在,而是扫描目标类的 setXxx() 方法:

1
2
3
4
5
6
7
JSON key: "cmd"

FastJSON 查找: setCmd(String) ← 找到了!

直接调用: testObj.setCmd("calc")

Runtime.getRuntime().exec("calc") 💥

完整执行流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
JSON.parseObject(s)

├─ 发现 "@type": "Test"
│ ↓
│ AutoType 加载 Test.class(反射实例化)

└─ 遍历剩余 key-value

key = "cmd", value = "calc"

反射查找 Test 类的 setCmd(String) 方法

invoke(testInstance, "calc")

Runtime.getRuntime().exec("calc") ← RCE

FastJSON 在 JavaBeanDeserializer 中的匹配逻辑大致是:

1
2
3
4
5
6
7
8
9
10
11
12
// 伪代码还原
for (Map.Entry<String, Object> entry : jsonFields) {
String key = entry.getKey(); // "cmd"
String setterName = "set"
+ Character.toUpperCase(key.charAt(0))
+ key.substring(1); // "setCmd"

Method method = clazz.getMethod(setterName, ...);
if (method != null) {
method.invoke(instance, entry.getValue()); // 直接调用!
}
}