Fastjson基础

Fastjson简介

Fastjson 是阿里巴巴开源的一个 Java 语言实现的 JSON 处理库,主要用于 JSON 字符串与 Java 对象之间的互相转换

提供两个主要接口来分别实现序列化和反序列化操作:

  • 序列化:JSON.toJSONString将 Java 对象转换为 json 对象
  • 反序列化:JSON.parseObject/JSON.parse 将 json 对象重新变回 Java 对象

代码demo

序列化代码实现

先在pom.xml中导入依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>

定义一个 Student 类

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
package demo;

public class Student {
private String name;
private int age;

public Student() {
System.out.println("构造函数");
}

public String getName() {
System.out.println("getName");
return name;
}

public void setName(String name) {
System.out.println("setName");
this.name = name;
}

public int getAge() {
System.out.println("getAge");
return age;
}

public void setAge(int age) {
System.out.println("setAge");
this.age = age;
}
}

然后是序列化的代码,调用JSON.toJSONString()序列化Student类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class StudentSerialize {
public static void main(String[] args) {
Student student = new Student();
student.setName("Suzen");
student.setAge(20);
/// 未设置SerializerFeature.WriteClassName
String jsonString1 = JSON.toJSONString(student);
System.out.println(jsonString1);
/// 设置了SerializerFeature.WriteClassName
String jsonString2 = JSON.toJSONString(student, SerializerFeature.WriteClassName);
System.out.println(jsonString2);
}
}

image-20260617171505228

调试一下,看一看序列化的逻辑,在调用JSON.toJSONString()方法这里下断点

image-20260617171559847

跟进到JSON.toJSONString(),这里是又调用了一个toJSONString()方法

还有就是在进入到JSON类中后会多一个static变量,写的是members of JSON

其中有个需要特别注意的是DEFAULT_TYPE_KEY,值为@type,这个很重要的

image-20260617172001859

也就是下面这个,开始先 new 了一个SerializeWriter对象,赋值给 out 变量

image-20260617171820398

跟进到SerializeWriter这个类,是定义一些初始的变量值

image-20260617172432042

创建 JSONSerializer

image-20260617205004577

调用JSONSerializer类的write()方法进行序列化

image-20260617205039762

生成 JSON 字符串

image-20260617205214596

这就是序列化的过程,回头看一下 demo 代码,这里写了两种序列化的,区别就是在调用JSON.toJSONString时,是否添加第二个参数SerializerFeature.WriteClassName

从运行结果可以看到,加上SerializerFeature.WriteClassName就会多了"@type":"demo.Student",这个是被序列化的类名

image-20260617205521719

去看一下JSON.toJSONString,这个方法是有两个参数的

  • 第一个是object,就是要序列化的对象
  • 第二个是一个属性值,类似于一个开关,这里填上SerializerFeature.WriteClassName就表明在序列化的时候会加入@type,就是被序列化的类名,type 可以指定反序列化的类,并且调用其 getter/setter/is 方法

image-20260617205928372

Fastjson 接受的 JSON 可以通过@type字段来指定该 JSON 应当还原成何种类型的对象,在反序列化的时候方便操作

反序列化代码实现

调用JSON.parseObject()进行反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class StudentUnserialize {
public static void main(String[] args) {
String jsonString = "{\"@type\":\"demo.Student\",\"age\":20,\"name\":\"Suzen\"}";
Student student = JSON.parseObject(jsonString, Student.class, Feature.SupportNonPublicField);
System.out.println(student);
System.out.println(student.getClass().getName());
}
}

image-20260617210723346

传给JSON.parseObject()的三个参数分别是:

  • 要反序列化的 JSON 字符串
  • 指定反序列化目标类型
  • 反序列化的特性

Feature.SupportNonPublicField

顾名思义,Feature.SupportNonPublicField就是支持不是公共属性的反序列化还原

在调用JSON.parseObject()时,第三个参数加上Feature.SupportNonPublicField

此时调用方法,getName()是可以随意调用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class StudentUnserialize {
public static void main(String[] args) {
String jsonString = "{\"@type\":\"demo.Student\",\"age\":20,\"name\":\"Suzen\"}";
Student student = JSON.parseObject(jsonString, Student.class, Feature.SupportNonPublicField);
System.out.println(student);
System.out.println(student.getClass().getName());
System.out.println(student.getName());
}
}

image-20260618091119588

然后注释掉Student类中私有属性agesetAge()函数

一般没人会给私有属性加 setter 方法,加了就没必要声明为 private 了

image-20260618092603119

然后调用getAge()方法也是可以调用的,可以成功还原出私有属性age的值

image-20260618091334135

但是如果不加上Feature.SupportNonPublicField,此时就无法获取到私有属性age的值了,得到的age的值为0

image-20260618092934527

所以,如果序列化后的 JSON 内容中存在私有变量,要想在传给JSON.parseObject()进行反序列化时可以将私有变量成功还原出来,就需要在调用JSON.parseObject()时加上Feature.SupportNonPublicField这个属性

指定还原的类型

修改Student类,添加俩private成员变量,不设置setter方法

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
package demo;

import java.util.Properties;

public class Student {
private String name;
private int age;
private String address;
private Properties properties;

public Student() {
System.out.println("构造函数");
}

public String getName() {
System.out.println("getName");
return name;
}

public void setName(String name) {
System.out.println("setName");
this.name = name;
}

public int getAge() {
System.out.println("getAge");
return age;
}

// public void setAge(int age) {
// System.out.println("setAge");
// this.age = age;
// }

public String getAddress() {
System.out.println("getAddress");
return address;
}

public Properties getProperties() {
System.out.println("getProperties");
return properties;
}
}

然后是修改发序列化的 demo,先测试不带指定类型的参数,就是只有一个要反序列化的 JSON 字符串

1
Object obj = JSON.parseObject(jsonString);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package demo;

import com.alibaba.fastjson.JSON;

public class StudentUnserialize02 {
public static void main(String[] args) {
String jsonString ="{\"@type\":\"demo.Student\",\"age\":20, \"name\":\"Suzen\",\"address\":\"China\",\"properties\":{}}";
/// 不带指定类型的参数
Object obj = JSON.parseObject(jsonString);
System.out.println(obj);
System.out.println(obj.getClass().getName());
}
}

没有反序列化成功

调用了Student类的构造函数、所有属性的getter方法、JSON里面非私有属性的setter方法,其中getProperties()调用了两次

image-20260618102114709

再加上指定类型的参数

1
Object obj = JSON.parseObject(jsonString,Student.class);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package demo;

import com.alibaba.fastjson.JSON;

public class StudentUnserialize02 {
public static void main(String[] args) {
String jsonString ="{\"@type\":\"demo.Student\",\"age\":20, \"name\":\"Suzen\",\"address\":\"China\",\"properties\":{}}";
/// 不带指定类型的参数
// Object obj = JSON.parseObject(jsonString);
/// 带指定类型的参数
Object obj = JSON.parseObject(jsonString,Student.class);
System.out.println(obj);
System.out.println(obj.getClass().getName());
}
}

成功反序列化了

image-20260618102227006

parse与parseObject区别

这两者的区别就是返回的东西不一样

  • parseObject()返回的是 JSONObject
  • parse()返回的是实际类型的对象

当在没有对应类的定义的情况下,一般情况下都会使用JSON.parseObject()来获取数据

FastJson中的 parse()parseObject() 方法都可以用来将JSON字符串反序列化成Java对象,parseObject() 本质上也是调用 parse() 进行反序列化的。但是 parseObject() 会额外的将 Java 对象转为 JSONObject 对象,即 JSON.toJSON()

所以进行反序列化时的细节区别在于,parse() 会识别并调用目标类的 setter 方法及某些特定条件的 getter 方法,而 parseObject() 由于多执行了 JSON.toJSON(obj),所以在处理过程中会调用反序列化目标类的所有 settergetter 方法

也就是说,我们用parse()反序列化会直接得到特定的类,而无需像parseObject()一样返回的是 JSONObject 类型的对象、还可能需要去设置第二个参数指定返回特定的类

demo 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
package demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class StudentUnserialize03 {
public static void main(String[] args) {
String jsonString ="{\"@type\":\"demo.Student\",\"age\":20,\"name\":\"Suzen\",\"address\":\"China\",\"properties\":{}}";
Object obj = JSON.parse(jsonString, Feature.SupportNonPublicField);
System.out.println(obj);
System.out.println(obj.getClass().getName());
}
}

image-20260618104402173

Fastjson反序列化漏洞

Fastjson 在进行反序列化的时候,会去找我们在@type中规定的那个类,然后会自动调用这些settergetter方法,但并不是所有的settergetter方法

Fastjson只会调用满足下面这些要求的settergetter方法:

  • setter方法
    • 非静态函数
    • 参数个数为1个
    • 返回类型为 void 或当前类
  • getter方法
    • 非静态方法
    • 无参数
    • 返回值要继承CollectionMapAtomicBooleanAtomicIntegerAtomicLong其中一个

漏洞原理

Fastjson 是自己实现的序列化和反序列化的机制,没有使用 Java 原生的序列化和反序列化机制

不论是哪一个版本,Fastjson 反序列化漏洞原理都是一样的,只不过是针对不同版本中的黑名单采用不同的利用方式或者利用不同的链子罢了

Fastjson 反序列化漏洞的核心原理是:攻击者通过精心构造的 JSON 数据,利用 Fastjson 在反序列化过程中自动创建对象并调用gettersetter、构造函数等机制,触发非预期的 Java 代码执行或造成拒绝服务

如何反序列化出恶意类

从前面的学习可知,Fastjson 是使用parseObject或者parse()方法进行反序列化,并且可以指定类型,如果指定的类型很大,就会包含很多的子类,利用空间有了

比如,如果指定类型为Object或者JSONObject,那么就可以反序列化出其任意类或者任意子类,而Object是任意类的父类,所以就可以反序列化出所有类

如何触发反序列化得到的恶意类的恶意函数

在进行反序列化的时候会将反序列化得到的类的构造函数、满足条件的settergetter方法执行一遍,如果三种方法中存在危险操作,就会导致反序列化漏洞

如果只要从最终触发构造函数、setter、getter 的话,分为两种情况:JSON 字符串是嵌套,还是非嵌套

非嵌套

1
String jsonString = "{\"@type\":\"demo.Student\",\"age\":20,\"name\":\"Suzen\"}";

调试一下代码,在JSON.parseObject()下断点

image-20260618144740045

这里在调用JSON.parseObject(json, (Type) clazz, ParserConfig.global, null, DEFAULT_PARSER_FEATURE, features);

image-20260618133040532

直接跟进到下面这里,这里的parseDefaultJSONParser类的一个实例化对象

image-20260618133133360

继续跟进到DefaultJSONParser.parseObject(),下面这里会调用到derializer.deserialze()方法

image-20260618133336121

继续跟进可以发现就是调用的JavaBeanDeserializer.deserialze()方法,然后就是可以调用settergetter、构造函数

image-20260618144903716

image-20260618144912087

image-20260618133550582

到下面这里就调用createInstance()方法

image-20260618160142822

下面这里就会触发构造函数了,setter 和 getter 后续会根据是否满足触发条件触发

image-20260618160739994

嵌套

1
String jsonString = "{\"user\":{\"@type\":\"demo.Student\",\"age\":20,\"name\":\"Suzen\",\"address\":\"China\",\"properties\":{}}}";

前面和嵌套的一样,都是调用到JavaBeanDeserializer.deserialze

非嵌套的到这里就是可以直接调用构造函数那些了,而嵌套的要先进行字段解析

image-20260618145234470

先是第一层,这里拿到key的值是user

image-20260618153828665

然后直接跟进到600行这里,调用parseField()方法,这里传入的key就是user

image-20260618153932439

跟进,继续走到调用parseExtra()方法这里

image-20260618154217051

跟进,下面这里会调用parse()方法

image-20260618154524782

image-20260618154544579

来到DefultJSONParser.parse()

image-20260618154602066

这里会进入到LBRACE这个分支,继续调用parseObject()进行反序列化

image-20260618154711449

跟进会来到DefaultJSONParser.parseObject(final Map object, Object fieldName)

image-20260618154820130

239行这里会获取key的值

image-20260618155028045

拿到的key的值是@type

image-20260618155053460

然后在320行这里会进行一个比较,对比key是否和JSON.DEFAULT_TYPE_KEY相等

image-20260618155138361

JSON.DEFAULT_TYPE_KEY是一个常量,就是@type,这里是满足条件的

image-20260618155205273

所以可以继续跟进到368行这里,再次去调用deserializer.deserialze()方法

image-20260618155301460

也就是JavaBeanDeserializer.deserialze()方法

image-20260618155347879

然后就会调用createInstance()方法,调用构造函数、setter、getter了

image-20260618155840783

小结

若反序列化指定类型的类如Student obj = JSON.parseObject(text, Student.class);,该类本身的构造函数、setter方法、getter方法存在危险操作,则存在 Fastjson 反序列化漏洞

若反序列化未指定类型的类如Object obj = JSON.parseObject(text, Object.class);,该若该类的子类的构造方法、setter方法、getter方法存在危险操作,则存在 Fastjson 反序列化漏洞

POC写法

POC一般写法:

1
2
3
4
5
{
"@type":"xxx.xxx.xxx"
"xxx":"xxx",
...
}

@type指定反序列化得到的类

关键就是要找到一个满足以下条件的类:

  • 在目标环境中存在
  • 该类的构造函数、setter方法、getter方法中的某一个存在危险操作,比如造成命令执行
  • 可以控制该漏洞函数的变量(一般就是该类的属性)

漏洞Demo

直接将Student类中的getProperties()方法进行修改,也就是properties属性的 getter 方法,加入漏洞代码,即执行命令的代码

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
package demo;

import java.util.Properties;

public class Student {
private String name;
private int age;
private String address;
private Properties properties;

public Student() {
System.out.println("构造函数");
}

public String getName() {
System.out.println("getName");
return name;
}

public void setName(String name) {
System.out.println("setName");
this.name = name;
}

public int getAge() {
System.out.println("getAge");
return age;
}

// public void setAge(int age) {
// System.out.println("setAge");
// this.age = age;
// }

public String getAddress() {
System.out.println("getAddress");
return address;
}

public Properties getProperties() throws Exception {
System.out.println("getProperties");
Runtime.getRuntime().exec("calc");
return properties;
}
}

然后是反序列化的 demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class FastjsonPOCDemo {
public static void main(String[] args) {
String jsonString ="{\"@type\":\"demo.Student\",\"age\":20, \"name\":\"Suzen\",\"address\":\"China\",\"properties\":{}}";
Object obj = JSON.parse(jsonString);
// Object obj = JSON.parseObject(jsonString, Object.class);
System.out.println(obj);
System.out.println(obj.getClass().getName());
}
}

这里反序列化的类是 Object 类,是任意类的父类,Student 类是 Object 类的子类,并且 Student 类存在 Fastjson 反序列化漏洞,所以当@type指定的是 Student 类时就会触发漏洞

image-20260618163226296

还有一种场景是类本身就有漏洞,将 Demo 中的 Object 换成 Student

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class FastjsonPOCDemo {
public static void main(String[] args) {
String jsonString ="{\"@type\":\"demo.Student\",\"age\":20, \"name\":\"Suzen\",\"address\":\"China\",\"properties\":{}}";
/// 场景一
// Object obj = JSON.parse(jsonString);
// Object obj = JSON.parseObject(jsonString, Object.class);
/// 场景二
Student obj = JSON.parseObject(jsonString, Student.class);
System.out.println(obj);
System.out.println(obj.getClass().getName());
}
}

image-20260618164320066

命令执行的地方:

image-20260618164604598

如果调用parse()就是在下面这里

image-20260618164700198

参考学习文章

https://drun1baby.top/2022/08/04/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Fastjson%E7%AF%8701-Fastjson%E5%9F%BA%E7%A1%80/


Fastjson基础
https://yschen20.github.io/2026/06/18/Fastjson基础/
作者
Suzen
发布于
2026年6月18日
更新于
2026年6月18日
许可协议