本文最后更新于 2026-06-18T16:50:05+08:00
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); String jsonString1 = JSON.toJSONString(student); System.out.println(jsonString1); String jsonString2 = JSON.toJSONString(student, SerializerFeature.WriteClassName); System.out.println(jsonString2); } }
|

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

跟进到JSON.toJSONString(),这里是又调用了一个toJSONString()方法
还有就是在进入到JSON类中后会多一个static变量,写的是members of JSON
其中有个需要特别注意的是DEFAULT_TYPE_KEY,值为@type,这个很重要的

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

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

创建 JSONSerializer

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

生成 JSON 字符串

这就是序列化的过程,回头看一下 demo 代码,这里写了两种序列化的,区别就是在调用JSON.toJSONString时,是否添加第二个参数SerializerFeature.WriteClassName
从运行结果可以看到,加上SerializerFeature.WriteClassName就会多了"@type":"demo.Student",这个是被序列化的类名

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

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()); } }
|

传给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()); } }
|

然后注释掉Student类中私有属性age的setAge()函数
一般没人会给私有属性加 setter 方法,加了就没必要声明为 private 了

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

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

所以,如果序列化后的 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 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()调用了两次

再加上指定类型的参数
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,Student.class); System.out.println(obj); System.out.println(obj.getClass().getName()); } }
|
成功反序列化了

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),所以在处理过程中会调用反序列化目标类的所有 setter 和 getter 方法
也就是说,我们用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()); } }
|

Fastjson反序列化漏洞
Fastjson 在进行反序列化的时候,会去找我们在@type中规定的那个类,然后会自动调用这些setter和getter方法,但并不是所有的setter和getter方法
Fastjson只会调用满足下面这些要求的setter和getter方法:
- setter方法
- 非静态函数
- 参数个数为1个
- 返回类型为 void 或当前类
- getter方法
- 非静态方法
- 无参数
- 返回值要继承
Collection、Map、AtomicBoolean、AtomicInteger、AtomicLong其中一个
漏洞原理
Fastjson 是自己实现的序列化和反序列化的机制,没有使用 Java 原生的序列化和反序列化机制
不论是哪一个版本,Fastjson 反序列化漏洞原理都是一样的,只不过是针对不同版本中的黑名单采用不同的利用方式或者利用不同的链子罢了
Fastjson 反序列化漏洞的核心原理是:攻击者通过精心构造的 JSON 数据,利用 Fastjson 在反序列化过程中自动创建对象并调用getter、setter、构造函数等机制,触发非预期的 Java 代码执行或造成拒绝服务
如何反序列化出恶意类
从前面的学习可知,Fastjson 是使用parseObject或者parse()方法进行反序列化,并且可以指定类型,如果指定的类型很大,就会包含很多的子类,利用空间有了
比如,如果指定类型为Object或者JSONObject,那么就可以反序列化出其任意类或者任意子类,而Object是任意类的父类,所以就可以反序列化出所有类
如何触发反序列化得到的恶意类的恶意函数
在进行反序列化的时候会将反序列化得到的类的构造函数、满足条件的setter和getter方法执行一遍,如果三种方法中存在危险操作,就会导致反序列化漏洞
如果只要从最终触发构造函数、setter、getter 的话,分为两种情况:JSON 字符串是嵌套,还是非嵌套
非嵌套
1
| String jsonString = "{\"@type\":\"demo.Student\",\"age\":20,\"name\":\"Suzen\"}";
|
调试一下代码,在JSON.parseObject()下断点

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

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

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

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



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

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

嵌套
1
| String jsonString = "{\"user\":{\"@type\":\"demo.Student\",\"age\":20,\"name\":\"Suzen\",\"address\":\"China\",\"properties\":{}}}";
|
前面和嵌套的一样,都是调用到JavaBeanDeserializer.deserialze
非嵌套的到这里就是可以直接调用构造函数那些了,而嵌套的要先进行字段解析

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

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

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

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


来到DefultJSONParser.parse()

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

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

239行这里会获取key的值

拿到的key的值是@type

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

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

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

也就是JavaBeanDeserializer.deserialze()方法

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

小结
若反序列化指定类型的类如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 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);
System.out.println(obj); System.out.println(obj.getClass().getName()); } }
|
这里反序列化的类是 Object 类,是任意类的父类,Student 类是 Object 类的子类,并且 Student 类存在 Fastjson 反序列化漏洞,所以当@type指定的是 Student 类时就会触发漏洞

还有一种场景是类本身就有漏洞,将 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\":{}}";
Student obj = JSON.parseObject(jsonString, Student.class); System.out.println(obj); System.out.println(obj.getClass().getName()); } }
|

命令执行的地方:

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

参考学习文章
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/