0. 引言
2130706433 = 127.0.0.1。Java 的 URL 解析接受十进制整数形式的 IP,这个行为源自 4.2BSD 的 inet_aton,距今四十年。
在 fastjson 1.2.83 的利用链里,checkAutoType 会把 @type 值中的 . 替换为 /,点分 IP 当场被毁,整数 IP 原样存活。于是 jar:http://2130706433:18081/x!/poc 成了一个可用的远程类地址。
一个四十年前的解析器行为,被一个防御措施激活,变成了利用链上的关键一环。
上篇里我们把这条链从头到尾拆了一遍。这条链上有一串 trick:整数 IP、/proc/self/fd、polyglot JAR、ASM 非法类名、unicode @type 编码。
漏洞分析里到处是"trick"这个词,但它从来不是一个可以照着执行的清单。"这里需要一个 trick"——然后呢?去哪找,找到了怎么验证,验证了怎么留给下一个人复用?这个词一个都不回答。
其他领域有现成的答案。二进制安全把 gadget 做成了工业:ROPgadget 这类工具机械扫描代码段,找 gadget 不需要灵感。行为层能不能做同样的事?这篇文章用五个 trick 当样本,拆开看它们分别利用了什么、有什么共同结构、能不能系统地找。
1. 五个 Trick
1. 整数 IP
Java 的 InetAddress 和 URL 解析接受十进制整数形式的 IP。2130706433 = 0x7F000001 = 127.0.0.1。这个行为继承自 BSD inet_aton,规范除了点分四段还允许点分两段、点分三段、单个十进制数、十六进制数等写法,存在四十年了。
上篇的利用链里,checkAutoType 把 @type 值的 . 全替换成 /,点分 IP 当场被摧毁,整数 IP 原样存活。于是 jar:http://2130706433:18081/x!/poc 成了一个可用的远程类地址。
它的 Gap 夹在 URL 解析的规范行为和开发者"IP 就是点分四段"的默认假设之间。输入经过了一个会破坏点号的字符串变换,但你仍然需要表达含点的标识符——这时候该想起它。
2. /proc/self/fd
Linux 把进程打开的文件描述符暴露在文件系统里,/proc/self/fd/N 是 fd N 的路径化身。这个机制本来是为 lsof 等诊断工具设计的,但任何接受本地文件路径的 API 都能用它。
fastjson 案中,JDK 的 URLJarFile.retrieve 把远程 JAR 落盘为匿名临时文件(jar_cache*),路径不可知,但 fd 号可以枚举。jar:file:/proc/self/fd/N!/poc 绕过了"路径必须可知"的限制。
Gap 出在同一个接口的两种用法之间:诊断通道变成了寻址通道。这个 trick 出身很老——PHP LFI 时代就是社区知识,CTF 常规武器,最近被组合进 Java jar:file: 链。当你需要把一个刚写入的、匿名的、路径不可知的临时文件变成可寻址的路径时,该想起它。
3. polyglot JAR
zip 格式从文件尾部定位中央目录,JDK 的 ZipFile 实现容忍前缀垃圾。PNG、GIF 等图片格式则从头部解析。两种解析方向相反,一个文件可以同时是合法图片和合法 JAR——这就是 GIFAR 的思路(2008 年前后)。
利用链里,上传通道只验图片头,攻击者送入的却是带恶意 class 的 JAR。无出网场景下,上传 polyglot JAR 加 jar:file: 本地路径可以构成 staging 链(这个方向还没有完整实测,见上篇 E1/E2)。
Gap 在两个 parser 对同一字节流的解析方向不同:zip 从尾部读,图片从头读。需要把受控字节塞进一个类型受检的通道时,该想起它。
4. ASM 非法类名
Java 语言的标识符语法(javac 强制)远窄于 classfile 里非限定名的规则。JVMS §4.2 的禁止集只有 . ; [ /(以及方法名的 < >)。含 :、! 的类名任何 javac 都编译不出来,但 JVM 接受,ASM 可以直接写这样的字节码。
fastjson 案中,恶意类的名字本身就是一个 URL(jar:http://2130706433:18081/x!/poc),必须用 ASM 生成。
这个 trick 的 Gap 甚至不在两个组件之间,而在同一厂商的两份文档之间:Java 语言规范和 JVM 规范,两份不相洽的权威文本。五个 trick 里最纯粹的一个。需要构造语言语法不允许、但运行时接受的标识符时,该想起它。
5. unicode @type
JSON 字符串允许 \uXXXX 转义。fastjson 案中,WAF 按字面特征匹配 "@type",fastjson 在解析时把 "@type" 解码为 "@type"。检查发生在解码前,生效发生在解码后。
Gap 在规范化的时点:WAF 看到的是字面,fastjson 看到的是解码结果。通道上有基于字面/模式的检查、而下游 parser 存在解码或规范化步骤时,该想起它。
2. 共同结构
把五个 trick 的 Gap 列在一起看:
| trick | 语义 A | 语义 B |
|---|---|---|
| 整数 IP | URL spec(inet_aton 遗产) | 开发者假设(IP=点分四段) |
| /proc/self/fd | 意图用途(诊断接口) | 可达用途(路径化身) |
| polyglot JAR | zip spec(从尾部定位) | JDK 实现(容忍前缀垃圾) |
| ASM 非法类名 | Java 语言规范 | JVM 规范 |
| unicode @type | WAF 的 JSON | fastjson 的 JSON |
没有一例是代码写错了。每一例都是两份各自成立的语义,在某个交叉点上不兼容。
这个概念在二进制安全里已经有成熟的对应用:gadget。ROP/JOP 里,gadget 是从已有代码中回收、按功能索引、跨场景复用的指令片段。ROPgadget 这类工具可以机械扫描代码段找 gadget,不需要灵感。
trick 就是行为层的 gadget。跟二进制 gadget 的区别是:ROPgadget 扫的是代码段(静态的),行为层扫的是组件的行为特征(要跑起来才知道)。但思路是一样的:从已有的东西里回收可复用的资产,按场景索引。
换句话说:trick 是一段被武器化的语义分歧,从原始上下文中抽象出来、命名、按约束模式索引,成为可跨场景复用的资产。
3. Trick 是怎么来的
把五个 trick 的产生过程考古一遍,最明显的发现是时间错位:使 trick 成立的行为事实,远早于 trick 本身。
3.1 行为先于 trick
| trick | 行为事实存在 | 被武器化 |
|---|---|---|
| 整数 IP | inet_aton 接受整数形式,4.2BSD(约 1983) | SSRF 过滤绕过(2010s) |
| /proc/self/fd | Linux procfs 暴露 fd(九十年代初) | PHP LFI(2000s);Java 链(2026) |
| polyglot JAR | zip 尾部目录(1989)+ JDK 容忍前缀垃圾(1997) | GIFAR(2008) |
| ASM 非法类名 | JVMS §4.2 与 JLS 标识符规则分歧(1996) | fastjson 链(2026) |
| unicode @type | JSON \uXXXX 转义(自有规范起) |
WAF 字面匹配绕过(2010s) |
行为事实先于武器化存在,多数早十年以上。攻击者造不出 inet_aton 的宽容,也造不出 JVM 对非法类名的接受。这些行为是出厂即有的,和任何攻击意图无关。本文把它们叫做 feat。
feat 层是可以机械扫描的。二进制世界已经示范过了:ROPgadget 不模拟黑客找 gadget 的认知过程,它绕开认知直接扫代码段。feat 扫描是同一性质的操作:读配置、起一次服务、枚举 handler,不需要理解攻击意图。
3.2 约束催生 trick
feat 一直在那里,但变成 trick 需要一个触发条件:约束。
整数 IP 当了四十年解析器冷知识。它变成 trick,是因为"输入经过摧毁点号的变换、但仍需表达含点标识符"这个约束出现了。约束从哪来?
| trick | 约束 | 约束的制造者 |
|---|---|---|
| 整数 IP | 点号被 replace 摧毁,但需表达含点标识符 | fastjson checkAutoType,防御机制的组成部分 |
| unicode @type | 通道上有字面匹配检查 | WAF,防御 |
| polyglot JAR | 通道只放行图片 | 上传校验,防御 |
| ASM 非法类名 | 类名必须是一个 URL | 链内构造:攻击者自造的中段需求 |
| /proc/self/fd | 匿名临时文件需变为可寻址路径 | 链内构造:JDK 落盘行为 × 攻击者的路径选择 |
约束的最大来源是防御本身。防御每上线一次,就在客观上批量催生了一批潜在的配对。checkAutoType 上线那天,"整数 IP 活过点号摧毁"就客观成立了,只是还没有人完成它。第二来源是链内状态:组合链走到中途,会自造中段需求。
3.3 配对:feat + 约束 = trick
trick 诞生的过程可以拆成四步:
- 约束出现:防御部署或链内状态制造了新约束
- 候选生成:在 feat 库存里找满足约束的候选
- 实证验证:跑一次。整数 IP 活过变换了吗?活了
- 著录:命名、抽象、按场景索引,进入可复用库存
候选生成的四个来源,机械化的程度不一样:
| 来源 | 机制 | 例子 | 可机械化程度 |
|---|---|---|---|
| feat 注册表读取 | 读组件的扩展点清单 | transport 槽的完整候选 = 运行时注册的 URLStreamHandler 集合 | 完全 |
| 差分挖掘 | 两个实现互测,找分歧点 | desync 研究、parser differential fuzzing | 高,但需人工做相关性分诊 |
| 社区知识检索 | writeup/CTF/漏洞史按场景重索引 | 整数 IP、polyglot 的跨场景迁移 | 中,本质是老约束下的配对迁移 |
| 中段约束生产 | 链内自造需求并求解 | /proc/self/fd 两阶段 | 低,真正的发明前沿 |
这解释了"涌现"到底是怎么回事:一个新约束来了,在既有索引里查不到哪个 feat 满足它,配对只能靠人的识别。涌现不是 trick 的神秘属性,而是约束的新颖性暂时击败了索引。老约束(过滤器、类型检查、出口管控)是个反复出现的小家族,所以老配对能跨场景迁移。这也是社区知识在新约束面前必然失灵的原因。
这也解释了为什么"我想要一个 trick"是个没办法回答的问题。没有约束,就没有成功标准(第 3 步无从验证)、没有检索键(第 2 步无从定向)。约束把许愿变成了可解的问题:它同时给出成功标准、检索方向和配对成立的判定条件。上篇的格子矩阵回头看正是一排等待配对的约束:格子提问,feat 作答。
3.4 人的判断用在了哪
上面说的四步里,有两个子类型当前还依赖人的判断,暂时没有完备的机械方法。
一是链内自造需求。组合链走到中途,产生了"需要不含点的标识符""需要匿名文件可寻址"这样的中段需求——这些需求从哪来、怎么定义,当前没有机械方法。利用链的构造,本质上是把"我要 RCE"逐级分解成一串定义清楚的子目标,每个子目标再去找配对的 feat。fastjson 案就是例子:三个中段约束一旦被分解出来,求解反而便宜;贵的是分解本身。
二是新约束下的候选配对。老约束(过滤器、类型检查、出口管控)是个反复出现的小家族,既有索引能覆盖;新约束第一次出现时,索引里查不到哪个 feat 满足它,只能靠人的启发式搜索。
这两处都没有完备的机械方法。但这不说明"原则上不可自动化"——二进制世界的 gadget 扫描一开始也没有。边界在哪应该实测,不由言辞划定。
4. 能不能自动化
"trick 能不能被完整枚举"需要实测,不是嘴上说说。按上面的层次拆开:
| 层 | 可枚举性 | 依据 |
|---|---|---|
| feat 层(单组件行为存量) | 可机械化扫描 | 差分测试、spec 挖掘、注册表读取均有成功先例 |
| 约束层 | 可枚举,但持续新生 | 从部署中提取约束是机械操作;新约束主要来自防御部署,一直在长 |
| 跨系统组合 Gap | 退化为环境枚举问题 | 按行为等价类采样,而非版本枚举 |
| 候选配对搜索(无基底处) | 启发式搜索,无完备性保证 | 空间可枚举,但无基底时完备枚举不可行 |
| 配对检验(给定 feat + 约束) | 机械、便宜 | 跑一次实验就知道 |
feat 层里,一部分 feat 枚举可以降级为配置枚举。fastjson 案 transport 槽位的完整候选清单,不在任何 writeup 里,而在目标运行时实际注册的 URL handler 集合里。读注册表是机械操作。行为层缺的对应物,原因要拆成两半:扫描本身没人做,配对验证做得起的人少。feat 扫描廉价(读配置、起一次服务),但候选配对的作用域实测要起环境,单条成本远高于 gadget 的静态校验。
一个可以跑的测量方案:对 Java 资源加载 transport 槽跑一条 feat 扫描流水线。枚举 JDK 内建 handler 和常见容器注册的 handler,逐个实测 jar:<proto>: 资源名行为,对照已知库存(http/https/file/ftp 等)算召回率。收回已知项说明机械扫描有效;漏掉的精确定位问题出在哪个连接层;发现新项直接扩充库存。三类结果都有信息量,实验说了算。
5. 参考资料
5.1 原始披露
| 来源 | URL | 日期 |
|---|---|---|
| Kirill Firsov 首次披露推文(另见同作者推文 2078872296476057713,奇安信通告引用) | https://x.com/k_firsov/status/2078872293745570032 | 2026-07-19 |
| 原作者完整研究文档(fearsoff.org) | https://fearsoff.org/cn/research/fastjson-1-2-83-rce | 2026-07-22 |
5.2 官方与厂商通告
| 来源 | URL |
|---|---|
| 官方 Security Advisory(alibaba/fastjson2 Wiki) | https://github.com/alibaba/fastjson2/wiki/Security-Advisory:-Remote-Code-Execution-in-fastjson-1.2.68%E2%80%931.2.83 |
| CVE-2026-16723(NVD) | https://nvd.nist.gov/vuln/detail/CVE-2026-16723 |
| 腾讯云早期通告 | https://cloud.tencent.com/announce/detail/2375 |
| 腾讯云 CVE-2026-16723 正式通告 | https://cloud.tencent.com/announce/detail/2381 |
| 奇安信 CERT 首次通告(QVD-2026-43021) | https://www.secrss.com/articles/92316 |
| 奇安信 CERT 复现更新(gm7.org 转载) | https://www.gm7.org/archives/132224 |
| fastjson 1.2.83 Release 页 | https://github.com/alibaba/fastjson/releases |
| 猎户攻防实验室预警 | https://www.gm7.org/archives/131981 |
| 大连理工大学安全公告 | https://wlaq.dlut.edu.cn/info/1097/8405.htm |
5.3 技术分析
| 来源 | URL |
|---|---|
| 9eek 源码级分析(技术栈 / 博客园) | https://jishuzhan.net/article/2079870411771879426 ;https://www.cnblogs.com/9eek/p/21793398 |
| LoRexxar 技术分析 | https://lorexxar.cn/2026/07/21/fs1-2-83rce/ |
| GCSA / Foresight 深度分析 | https://foresightnews.pro/article/detail/98990 |
| Detect-DefenseLab 全版本通杀研究 | https://github.com/Detect-DefenseLab/fastjson-1.2.83-rce-detection |
| FreeBuf 完整分析复现 | https://www.freebuf.com/articles/web/491546.html |
| Mrxn 默认配置 RCE 分析 | https://mrxn.net/jswz/fastjson-1-2-83-default-config-rce.html |
| CSDN PoC 综合分析 | https://blog.csdn.net/lingggggaaaa/article/details/163064101 |
| CSDN 实战复现与企业排查 | https://blog.csdn.net/weixin_42376192/article/details/163121562 |
5.4 PoC 仓库
| 仓库 | URL | 备注 |
|---|---|---|
| wouijvziqy/Fastjson-JsonType-RCE-PoC | https://github.com/wouijvziqy/Fastjson-JsonType-RCE-PoC | 最早公开,已删除 |
| ThanatosXingYu/2026FastjsonPoC | https://github.com/ThanatosXingYu/2026FastjsonPoC | 完整构建脚本与复现步骤 |
| seklure/fastjson-jsontype-rce-poc | https://github.com/seklure/fastjson-jsontype-rce-poc | 轻量 PoC |
| dinosn/fastjson-jsontype-rce-lab | https://github.com/dinosn/fastjson-jsontype-rce-lab | Docker 靶场 |
| fzypl/Fastjson-JsonType-RCE-Demo | https://github.com/fzypl/Fastjson-JsonType-RCE-Demo | 演示 Demo |
| zg-sec/fastjson-json-type-rce | https://github.com/zg-sec/fastjson-json-type-rce | 诸葛安全版 |
| YoungBear/FastjsonPoc | https://github.com/YoungBear/FastjsonPoc | 通用 PoC |
| Detect-DefenseLab/fastjson-1.2.83-rce-detection | https://github.com/Detect-DefenseLab/fastjson-1.2.83-rce-detection | 全版本通杀 + 三层防御 |
5.5 工具与文档
- ASM 官网与文档:https://asm.ow2.io/(标本 5 的字节码生成工具)
- fastjson safeMode 文档:https://github.com/alibaba/fastjson/wiki/enable_autotype
- fastjson 1.x → 2.x 升级指南:https://github.com/alibaba/fastjson2/wiki/fastjson_1_upgrade_cn