文章总结: 本文深入分析fastjson22.0.62远程代码执行漏洞,核心在于业务代码使用Object.class等宽泛类型解析外部JSON并允许@type时,攻击者可通过FNV-1aHash碰撞绕过白名单,结合jar:URL类加载机制实现恶意类加载。漏洞利用条件苛刻,受JDK版本、Web容器及类加载器影响。建议避免使用Object.class解析外部JSON,并开启SafeMode进行缓解。 综合评分: 100 文章分类: 漏洞分析,红队,WEB安全
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() ↓getObjectReaderAutoType() ↓ObjectReaderProvider.getObjectReader() ↓checkAutoType()
也就是说,只要业务代码把外部 JSON 解析成 Object.class,并允许识别 @type,攻击者就可以把解析流程引导到 checkAutoType。
四、checkAutoType 做了什么
checkAutoType 是 fastjson2 类型安全控制的核心函数。它大致承担三件事:
checkAutoType 中首先会进行 SafeMode 检查,这也是为什么 fastjson 1.x 和 fastjson2 都可以通过 SafeMode 做临时缓解 [2]。
逻辑可以简化成:
进入 checkAutoType ↓检查 SafeMode ↓检查 autoTypeSupport / Feature.SupportAutoType ↓执行 FNV-1a Hash 白名单校验 ↓满足条件则进入 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 ↓replace('.', '/') + ”.class” ↓getResourceAsStream ↓@JSONType 作为信任信号 ↓loadClass
而 fastjson2 2.0.62 的核心则更偏向:
用户可控 @type ↓checkAutoType ↓FNV-1a Hash 白名单 ↓构造 Hash 碰撞 ↓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 字符串 ↓既要符合 jar:file / jar:http 加载结构 ↓又要 Hash 命中白名单值
可以使用 Unicode 字符作为附加修正字符来完成碰撞 [2]。这也解释了为什么某些 PoC 里会出现肉眼看起来很奇怪的 Unicode 类名。
九、为什么要修改 Class 字节码中的类名
当 @type 字符串中被加入 Hash 修正字符后,最终要被 JVM 加载的类名也会发生变化。
这带来一个问题:
JAR 包里的 .class 文件内部记录的类名,必须和外部请求加载的类名一致。
Java 类文件常量池中保存了类的内部名称。如果外部请求加载的类叫:
某个包含特殊 Unicode 字符的类名
但 .class 文件常量池里仍然叫:
E
那么 JVM 加载时会因为类名不匹配而失败。
因此需要对 class 字节码常量池中的原始类名进行替换,并重新打包成 JAR,使 JAR 内部类名和 payload 中的类名保持一致 [2]。
这一步本质上不是漏洞根因,而是利用工程化中的必要适配:
Hash 碰撞改变了 @type 类名 ↓JVM 加载要求类名一致 ↓需要同步修改 .class 常量池中的类名 ↓重新打包 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() ↓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 ↓业务代码使用宽泛类型解析 例如 Object.class / List.class / Set.class / JSON.parse / parseArray ↓解析器遇到 @type ↓ObjectReaderImplObject.readObject() ↓JSONReader.Context.getObjectReaderAutoType() ↓ObjectReaderProvider.getObjectReader() ↓ObjectReaderProvider.checkAutoType() ↓SafeMode 未开启 ↓未开启 SupportAutoType 时,需要通过 FNV-1a Hash 白名单校验 ↓构造特殊 @type,使 Hash 命中默认白名单 ↓进入 loadClass ↓ClassLoader 解析 jar:http 或 jar:file 形式资源 ↓加载攻击者控制的 class / jar ↓类实例化或静态初始化触发恶意逻辑 ↓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 碰撞》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论