fastjson22.0.62RCE漏洞分析:从AutoType到FNVHash碰撞

admin 2026-08-04 05:33:45 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文深入分析fastjson22.0.62远程代码执行漏洞,核心在于业务代码使用Object.class等宽泛类型解析外部JSON并允许@type时,攻击者可通过FNV-1aHash碰撞绕过白名单,结合jar:URL类加载机制实现恶意类加载。漏洞利用条件苛刻,受JDK版本、Web容器及类加载器影响。建议避免使用Object.class解析外部JSON,并开启SafeMode进行缓解。 综合评分: 100 文章分类: 漏洞分析,红队,WEB安全


cover_image

fastjson2 2.0.62 RCE 漏洞分析:从 AutoType 到 FNV Hash 碰撞

原创

海蜃 海蜃

海蜃

2026年7月29日 08:56 河南

在小说阅读器读本章

去阅读

一、前言

最近 fastjson 生态连续曝出高危问题,前有 fastjson 1.2.83 的 getResourceAsStream + @JSONType 链路,后有 fastjson2 2.0.62 的远程代码执行问题。两者在利用形态上有相似之处,都围绕 @type、类加载、jar: URL、Spring Boot fat-jar 等关键点展开,但 fastjson2 2.0.62 这条链又有自己的特殊性。

从已有披露内容看,fastjson2 2.0.62 这个漏洞并不是“任意场景默认秒打”的类型。它对业务代码写法、解析入口、运行容器、JDK 版本、类加载器行为都有较强依赖。但一旦命中可利用条件,攻击者可以通过构造特殊 @type,让 fastjson2 进入类型解析和类加载流程,最终实现远程代码执行 [2]。

本文尝试从以下几个角度梳理该漏洞:


二、漏洞核心结论

先给出几个关键结论,便于理解后文:

如果用一句话概括:

fastjson2 2.0.62 的风险点在于:当业务使用宽泛类型解析外部 JSON 时,攻击者可能通过特殊 @type 进入 AutoType 类型解析链,并借助 FNV-1a Hash 碰撞、jar: URL 类加载等机制绕过限制,最终触发恶意类加载。


三、从最简单的危险写法开始

首先看一个更直接的写法:

JSON.parseObject(json, Object.class, SupportAutoType);

这种写法本质上就是告诉 fastjson2:

我允许输入 JSON 中的 @type 控制实际反序列化类型。

这和很多 Jackson 多态反序列化漏洞有相似之处:必须指定一个宽泛目标类型,同时打开类型识别能力。此时如果输入 JSON 中包含 @type,fastjson2 就会尝试进入 AutoType 解析流程 [2]。

调用栈如下:

ObjectReaderProvider.checkAutoType(String, Class<?>, long)ObjectReaderProvider.getObjectReader(String, Class<?>, long)JSONReader.Context.getObjectReaderAutoType(String, Class)ObjectReaderImplObject.readObject(JSONReader, Type, Object, long)JSON.parseObject(String, Class, Feature...)

这个调用栈非常关键,它说明漏洞入口并不在业务对象 setter,也不在某个第三方 gadget,而是在 fastjson2 解析 Object.class 时调用的通用对象反序列化器:

ObjectReaderImplObject.readObject()&nbsp;↓getObjectReaderAutoType()&nbsp;↓ObjectReaderProvider.getObjectReader()&nbsp;↓checkAutoType()

也就是说,只要业务代码把外部 JSON 解析成 Object.class,并允许识别 @type,攻击者就可以把解析流程引导到 checkAutoType。


四、checkAutoType 做了什么

checkAutoType 是 fastjson2 类型安全控制的核心函数。它大致承担三件事:

checkAutoType 中首先会进行 SafeMode 检查,这也是为什么 fastjson 1.x 和 fastjson2 都可以通过 SafeMode 做临时缓解 [2]。

逻辑可以简化成:

进入 checkAutoType&nbsp;↓检查 SafeMode&nbsp;↓检查 autoTypeSupport / Feature.SupportAutoType&nbsp;↓执行 FNV-1a Hash 白名单校验&nbsp;↓满足条件则进入 loadClass

这里最重要的是两个点:

1. SafeMode 是更靠前的硬拦截

如果 SafeMode 开启,@type 相关逻辑会被更早拒绝。也就是说,这类问题不管后续怎么绕白名单、怎么碰撞 Hash,只要 SafeMode 生效,攻击链就很难继续走下去。

2. SupportAutoType 会显著降低利用门槛

如果业务显式开启:

JSONReader.Feature.SupportAutoType

那么攻击者更容易进入 loadClass 分支。在手动开启 SupportAutoType 的情况下,即便类名不符合默认白名单,也能够进入后续类加载逻辑 [2]。

这也是第一条防护建议:

外部输入场景,不要开启 SupportAutoType。


五、不开 SupportAutoType 为什么也可能出问题

实际开发中,正常情况下很少有人会主动开启 SupportAutoType。因此这个漏洞真正值得关注的地方是:

JSON.parseObject(json, Object.class);

也就是:

  • 指定目标类型为 Object.class;
  • 没有开启 SupportAutoType;
  • 仍然可能被构造绕过。

在这种情况下,流程会走 FNV-1a Hash 白名单校验,而且默认只有一个白名单 Hash 值:

-6293031534589903644

也就是说,fastjson2 并不是直接按明文类名做白名单匹配,而是对类型名计算 FNV-1a Hash,再判断是否命中允许列表 [2]。

攻击思路就从“找一个白名单类名”变成了:

构造一个特殊 @type 字符串,使它的 FNV-1a Hash 结果等于白名单 Hash。

这就是 fastjson2 2.0.62 这条链区别于 fastjson 1.2.83 的关键点之一。

fastjson 1.2.83 的核心是:

用户可控 typeName&nbsp;↓replace('.', '/') + ”.class”&nbsp;↓getResourceAsStream&nbsp;↓@JSONType 作为信任信号&nbsp;↓loadClass

而 fastjson2 2.0.62 的核心则更偏向:

用户可控 @type&nbsp;↓checkAutoType&nbsp;↓FNV-1a Hash 白名单&nbsp;↓构造 Hash 碰撞&nbsp;↓loadClass

六、为什么是 jar:http: 和 jar:file:

payload 形式和 fastjson 1.2.83 比较接近,主要仍然围绕 jar:http: 和 jar:file: 两类形式 [2]。

这里的关键在于 Java URL 体系对 jar: 协议的支持。

典型格式是:

jar:!/

它的含义是:

从某个外部 URL 或本地文件中打开一个 JAR 包,再从 JAR 包内部读取指定 class。

例如概念上可以表示为:

jar:http://host/file.jar!/E.classjar:file:/path/file.jar!/E.class

在 fastjson2 的利用语境中,攻击者并不一定直接写出标准 URL,而是构造一种经过 fastjson / ClassLoader 转换后能够被识别为 jar: URL 的类型名。

这和 fastjson 1.2.83 的思路非常像:不是把 @type 当传统 Java 类名,而是把它当成一个能够影响资源加载路径的“URL 编码载体”。


七、Tomcat、Undertow 与 JDK 版本的差异

在 java -jar springbootDemo.jar 启动的情况下,不同 Web 容器使用的 ClassLoader 不同:

  • Tomcat:TomcatEmbeddedWebappClassLoader
  • Undertow:LaunchedURLClassLoader

这会影响 jar:http: 这类远程加载形式是否能走通 [2]。

规律是:

如果要通过 HTTP 加载,仍然需要 JDK8 + Undertow;如果是 Tomcat 或 JDK9+,通常只能考虑 file 方向 [2]。

原因可以从类加载器行为理解:

1. LaunchedURLClassLoader 更接近 URL 资源加载语义

Spring Boot fat-jar 环境下,LaunchedURLClassLoader 对 URL 类型资源的解析能力更强,因此更容易把构造出来的 jar:http: 资源路径带入底层 URL 处理逻辑。

2. Tomcat 的 ClassLoader 会带来额外限制

虽然某些地方 JSON.class.getClassLoader() 可能拿到 LaunchedURLClassLoader,看起来似乎 Tomcat 也能进入加载逻辑,但后续在临时类 ORG_1_0_E.readObject() 中实例化恶意类时,用的是 DynamicClassLoader.loadClass,其 parent 仍然是 TomcatEmbeddedWebappClassLoader,最终还要走 Tomcat 的类加载流程,因此无法避免相关报错 [2]。

这说明 fastjson2 这条链不是只要前面 checkAutoType 放行就一定成功。真正能不能 RCE,还取决于后续实例化阶段实际使用的 ClassLoader。


八、FNV-1a Hash 碰撞为什么重要

在不开 SupportAutoType 的情况下,默认白名单 Hash 是绕不过去的硬门槛。

可以通过本地计算,让构造出来的 @type 字符串的 FNV-1a Hash 命中:

-6293031534589903644

并且可以选择文件名、路径或类名部分作为变量进行碰撞。作者进一步提到,考虑到后续 fd 打法,使用类名部分做 Hash 修正更合适 [2]。

这里不展开具体碰撞脚本,只讲原理。

FNV-1a 是一种非密码学 Hash。它设计目标是快速、分布均匀,并不是抗碰撞。对于安全边界而言,如果只把一个固定 Hash 值作为白名单判断依据,那么攻击者理论上可以寻找不同字符串,使其 Hash 结果等于该白名单值。

在这个漏洞链中,攻击者需要同时满足两个条件:

这就产生了一个“约束求解”问题:

构造 @type 字符串&nbsp;↓既要符合 jar:file / jar:http 加载结构&nbsp;↓又要 Hash 命中白名单值

可以使用 Unicode 字符作为附加修正字符来完成碰撞 [2]。这也解释了为什么某些 PoC 里会出现肉眼看起来很奇怪的 Unicode 类名。


九、为什么要修改 Class 字节码中的类名

当 @type 字符串中被加入 Hash 修正字符后,最终要被 JVM 加载的类名也会发生变化。

这带来一个问题:

JAR 包里的 .class 文件内部记录的类名,必须和外部请求加载的类名一致。

Java 类文件常量池中保存了类的内部名称。如果外部请求加载的类叫:

某个包含特殊 Unicode 字符的类名

但 .class 文件常量池里仍然叫:

E

那么 JVM 加载时会因为类名不匹配而失败。

因此需要对 class 字节码常量池中的原始类名进行替换,并重新打包成 JAR,使 JAR 内部类名和 payload 中的类名保持一致 [2]。

这一步本质上不是漏洞根因,而是利用工程化中的必要适配:

Hash 碰撞改变了 @type 类名&nbsp;↓JVM 加载要求类名一致&nbsp;↓需要同步修改 .class 常量池中的类名&nbsp;↓重新打包 JAR

十、哪些业务写法可利用,哪些不可利用

这部分对真实风险评估非常重要。

1. 高风险:Object.class

JSON.parseObject(json, Object.class);

这是重点关注入口。因为目标类型足够宽泛,解析过程中会进入 ObjectReaderImplObject.readObject(),从而有机会解析 @type 并进入 AutoType 检查链 [2]。


2. 显式高风险:Object.class + SupportAutoType

JSON.parseObject(json, Object.class, SupportAutoType);

这种写法风险更高,因为显式启用了 AutoType,绕过门槛明显降低 [2]。


3. 可关注:List.class、Set.class

JSON.parseObject(json, List.class);JSON.parseObject(json, Set.class);

测试结果显示 List 和 Set 可以触发,它们都会走:

ObjectReaderImplList.readObject()&nbsp;↓ObjectReaderImplObject.readObject()

因此如果数组元素中包含特殊 @type,仍然可能进入危险链 [2]。


4. Map 不可直接触发

JSON.parseObject(json, Map.class);

测试结果是不行 [2]。

这说明 fastjson2 对 Map 的解析路径和 Object / List / Set 不一样,不会进入同样的 AutoType 对象解析链。


5. DTO 类一般不可触发,但 Object 字段例外

常见业务写法是:

JSON.parseObject(json, User.class);

如果 User 的字段都是:

StringintLongBoolean其他明确 DTO

通常不会触发这条链。

但如果 DTO 中存在:

Object data;List list;Set set;

这类宽泛字段,那么攻击者可以把 payload 嵌入这些字段中,仍然可能进入 ObjectReaderImplObject 路径。

因此,DTO 并不是绝对安全,关键要看内部字段是否存在泛型宽口。


6. 不指定类:JSON.parseObject(json)

JSON.parseObject(json);

这种写法走的是 JSONObject 解析,只会把 @type 当普通文本处理,因此不触发该漏洞链 [2]。


7. JSON.parse(json) 和 parseArray()

另一种可触发写法:

JSON.parse(json);

它和 parseObject 不同,多了一个 read array 的分支,因此可以通过数组形式 payload 进入危险路径。同理,parseArray() 也需要关注 [2]。

这说明实际审计时不能只 grep parseObject(json, Object.class),还要关注:

JSON.parse(...)JSON.parseArray(...)JSON.parseObject(..., List.class)JSON.parseObject(..., Set.class)DTO 中的 Object / List / Set 字段

十一、完整漏洞链路抽象

综合上下文,可以把 fastjson2 2.0.62 的利用流程抽象为:

外部传入 JSON&nbsp;↓业务代码使用宽泛类型解析&nbsp;例如 Object.class / List.class / Set.class / JSON.parse / parseArray&nbsp;↓解析器遇到 @type&nbsp;↓ObjectReaderImplObject.readObject()&nbsp;↓JSONReader.Context.getObjectReaderAutoType()&nbsp;↓ObjectReaderProvider.getObjectReader()&nbsp;↓ObjectReaderProvider.checkAutoType()&nbsp;↓SafeMode 未开启&nbsp;↓未开启 SupportAutoType 时,需要通过 FNV-1a Hash 白名单校验&nbsp;↓构造特殊 @type,使 Hash 命中默认白名单&nbsp;↓进入 loadClass&nbsp;↓ClassLoader 解析 jar:http 或 jar:file 形式资源&nbsp;↓加载攻击者控制的 class / jar&nbsp;↓类实例化或静态初始化触发恶意逻辑&nbsp;↓RCE

这里面有四个关键门槛:

任何一个条件不满足,攻击链都会中断。


十二、与 fastjson 1.2.83 的对比

这次 fastjson2 2.0.62 与 fastjson 1.2.83 有明显相似点,也有关键差异。

相似点

差异点

这也说明 fastjson2 的风险不能简单等同于 fastjson 1.x。它更像是:

在更严格的类型安全体系中,某个白名单 Hash 判断被特殊构造字符串绕过后,引发的类型加载问题。


十三、防护建议

1. 升级 fastjson2

如果官方已经发布修复版本,应优先升级。不要长期停留在 2.0.62。


2. 开启 SafeMode

SafeMode 是最直接的临时缓解手段。fastjson1 和 fastjson2 都可以用 SafeMode 进行临时缓解 [2]。

建议:

JSONFactory.setDefaultReaderFeatures(JSONReader.Feature.SafeMode);

具体配置方式请以 fastjson2 官方文档和当前版本 API 为准。


3. 不要开启 SupportAutoType

外部输入场景下,禁止使用:

JSONReader.Feature.SupportAutoType

如果业务确实需要多态反序列化,也应该使用严格白名单,不要允许用户输入任意类名。


4. 避免解析外部输入到宽泛类型

重点排查:

JSON.parseObject(input, Object.class)JSON.parseObject(input, List.class)JSON.parseObject(input, Set.class)JSON.parse(input)JSON.parseArray(input)

如果业务上可以确定结构,应改成强类型 DTO。


5. DTO 中避免使用 Object 字段

即使外层是 DTO,如果内部存在:

Object extra;List items;Set values;Map ext;

也要谨慎处理。

建议将其改成明确类型,或者在解析前对字段结构进行 schema 校验。


6. 禁止不可信 JSON 中出现 @type

对于所有外部接口,可以在网关层或业务层做基础过滤:

  • 拒绝包含 @type 的请求;
  • 拒绝异常 Unicode 类名;
  • 拒绝 jar:、file:、http: 等可疑片段;
  • 对 JSON 深度、字段长度、数组长度做限制。

这不是根治措施,但可以降低攻击面。


7. 限制 JVM 出网与本地文件访问

如果攻击链依赖远程加载或本地 jar:file:,那么出网限制和文件权限控制可以有效降低成功率。

建议:

  • 禁止业务 JVM 任意访问外网;
  • 容器内最小权限运行;
  • 限制敏感目录读权限;
  • 避免把可写目录暴露为可被 ClassLoader 读取的位置。

8. 监控异常请求

可以重点监控:

@typejar:jar:http:jar:file:proc/self/fd大量 Unicode 异常字符包含 !. 的类型字符串

这类请求不一定都是攻击,但在业务 JSON 中通常非常异常,适合作为告警规则。


十四、总结

fastjson2 2.0.62 这条 RCE 链路的精妙之处,在于它并不是简单依赖“开启 AutoType”这种显性错误配置。更值得关注的是,在默认不开启 SupportAutoType 的情况下,如果业务使用了 Object.class、List.class、Set.class、JSON.parse()、parseArray() 这类宽泛解析入口,攻击者仍可能通过构造特殊 @type,让其 FNV-1a Hash 命中 fastjson2 的默认白名单值,进而进入类加载流程 [2]。

相比 fastjson 1.2.83,fastjson2 2.0.62 的利用条件更工程化,也更依赖业务写法和环境。但也正因为它不像传统 AutoType 漏洞那样一眼可见,实际排查中更容易被忽略。

对防守方来说,最重要的不是记住某个 payload,而是建立三条原则:

fastjson2 已经比 fastjson 1.x 做了大量安全收敛,但只要 JSON 解析器还支持动态类型、多态绑定、泛型容器和复杂类加载,攻击面就不会彻底消失。真正稳妥的做法,仍然是升级、收口类型、关闭危险特性,并把输入结构约束在业务真正需要的范围内。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:海蜃 海蜃 海蜃《fastjson2 2.0.62 RCE 漏洞分析:从 AutoType 到 FNV Hash 碰撞》

评论:0   参与:  0