一个”为了方便”的自动类型解析功能,让 Fastjson 成了黑灰产和 APT 组织的头号目标。
它的漏洞修复史,是一场长达数年的”修复 vs 绕过”军备竞赛——黑名单加了一条又一条,绕过却从未停止。
引言:库的”自动类型”之罪
在 Java 的 JSON 解析世界里,Fastjson 是一个传奇——它由阿里巴巴开源,以”极快”著称,被全球海量 Java 应用(尤其电商、互联网、政务系统)广泛使用。
但成也”快”,败也”快”。Fastjson 为了性能和便利,提供了一个叫 autoType 的功能,而这个功能,成了它挥之不去的噩梦:
一个
@type字段,就能让 Fastjson “自动加载并实例化任意类”——而这,几乎等同于把”执行任意代码”的能力交给了攻击者。
一句话定义它:
Fastjson 的反序列化漏洞 = “自动类型解析”这个便捷功能,被证明是一把”交给攻击者的钥匙”。
一、autoType 是什么?为什么它如此危险?
1.1 正常的 JSON 解析
正常情况下,JSON 解析是把 JSON 文本转成对象:
1 | // 正常用法:把 JSON 转成指定类型的对象 |
1.2 autoType 的”便捷”设计
但 Fastjson 为了支持”多态“(即 JSON 里声明了具体子类型),允许 JSON 里带一个 @type 字段,Fastjson 会根据这个字段自动加载对应的类:
1 | // 带 @type 的 JSON:Fastjson 会尝试加载 com.example.Evil 类 |
1.3 危险何在?
关键问题:如果 @type 的值能被攻击者控制,那么 Fastjson 就会:
- 读取
@type指定的类名; - 通过
Class.forName()加载这个类; - 实例化它,并调用其 setter / 构造方法。
而 Java 生态里,存在大量”一旦被实例化或调用 setter,就会执行危险操作“的类(即 gadget)。攻击者只需在 JSON 里指定这些类的名字,就能触发任意代码执行。
1 | 攻击者提交 {"@type":"恶意gadget类","...":"..."} |
二、代码级攻击链:从恶意 JSON 到 RCE
下面还原一条完整的 Fastjson 攻击链(脱敏,仅供安全教育):
2.1 第一步:寻找可用的 gadget 类
Fastjson 的攻击,关键在于找到一个”在 setter 或构造方法中执行危险操作“的类。历史上被利用的 gadget 类包括:
com.sun.rowset.JdbcRowSetImpl(早期经典)org.springframework.context.support.ClassPathXmlApplicationContext(Spring 环境)com.mchange.v2.c3p0(数据库连接池)org.apache.ibatis.datasource.jndi.JndiDataSourceFactory(MyBatis 环境)
2.2 第二步:构造恶意 JSON(以 JdbcRowSetImpl 为例)
1 | // 概念示例:利用 JdbcRowSetImpl 触发 JNDI 注入(脱敏,仅供安全教育) |
攻击链原理:
- Fastjson 反序列化
JdbcRowSetImpl时,会调用其setDataSourceName()和setAutoCommit()。 setAutoCommit(true)内部会触发connect(),进而用dataSourceName的值发起 JNDI lookup。- 于是
ldap://attacker.com/Exploit被加载 → 远程恶意类被执行 → RCE。
注意:这条链最终指向 JNDI 注入,与 Log4Shell(
${jndi:...})殊途同归——JNDI 是 Java 生态的”万能跳板”。
2.3 第三步:发送恶意请求
1 | # 概念示例:向存在 Fastjson 反序列化点的接口发送恶意 JSON(脱敏) |
2.4 第四步:获得 RCE
1 | Fastjson 解析 JSON → 加载 JdbcRowSetImpl |
三、”修复 vs 绕过”拉锯战:Fastjson 的漏洞史
Fastjson 的修复历史,是 Java 生态最著名的”攻防军备竞赛“:
1 | 阶段1(2017):autoType 默认开启,漏洞爆发 |
关键漏洞年表
| 时间 | 版本 | 关键点 |
|---|---|---|
| 2017 | fastjson 1.2.24 | 首次公开 autoType 漏洞(JdbcRowSetImpl) |
| 2019 | 1.2.47 | 历史性绕过:通过 java.lang.Class 缓存绕过黑名单 |
| 2020 | 1.2.68 | autoType 默认关闭,但 expectClass 等新绕过出现 |
| 2022 | 1.2.83 | 安全模式 safeMode 推出 |
| 至今 | 持续更新 | 黑名单不断扩充,但绕过仍在继续 |
1.2.47 的经典绕过(值得深挖)
2019 年的 1.2.47 绕过是 Fastjson 漏洞史上最有名的案例之一:
- Fastjson 修复 1.2.24 后,用黑名单拦截了已知的恶意类名。
- 但攻击者发现:通过
@type指定java.lang.Class,再配合@type指向恶意类,可以利用 Fastjson 的类缓存机制绕过黑名单。
1 | // 概念示例:1.2.47 绕过手法(脱敏,仅供安全教育) |
这次绕过的意义在于:它证明了**”黑名单”这种防御方式,本质上是被动的、注定会被绕过的**。这也是 Fastjson 最终引入”白名单”和”安全模式”的根本原因。
四、为什么 Fastjson 这么难修复?
4.1 黑名单的”天生缺陷”
- 黑名单只能拦截”已知”的恶意类,而 Java 生态的 gadget 类源源不断地被发现。
- 只要有一个”漏网之鱼”的类存在于 classpath,黑名单就形同虚设。
4.2 autoType 的”刚需”矛盾
- 很多业务确实需要 autoType 来支持多态反序列化。
- 完全禁用会影响功能,于是开发者往往”睁一只眼闭一只眼”地开启它。
4.3 依赖环境的”因人而异”
- Fastjson 的漏洞能否利用,取决于目标应用的 classpath 里有哪些 gadget 类。
- 同一个 Fastjson 版本,在不同环境下的可利用性完全不同——这增加了修复和检测的复杂度。
五、检测与防御:如何守住 Fastjson?
5.1 检测维度
| 检测点 | 方法 |
|---|---|
请求体是否含 @type 字段 |
WAF 规则、日志审计 |
是否出现 Class.forName 的异常调用 |
RASP |
| 是否出站到 LDAP/RMI(1389/1099/389) | 网络流量监控 |
| Fastjson 版本是否老旧 | 依赖扫描(SCA) |
5.2 防御建议
- 升级到安全版本:使用最新版 fastjson,或迁移到 fastjson2。
- 开启安全模式:
1 | // 推荐:全局开启安全模式,彻底禁用 autoType(脱敏示例) |
- 使用白名单:如确需 autoType,配置白名单,只允许可信类。
- 依赖最小化:移除不需要的 gadget 库(如 c3p0、MyBatis JNDI 等)。
- 运行时防护(RASP):监控并阻断
Class.forName、JNDI lookup 等敏感调用。 - WAF 拦截:拦截含
@type的可疑 JSON 请求。
5.3 一个实用的检测命令
1 | # 概念示例:检索日志中可疑的 @type 请求(脱敏) |
六、结语:便捷的代价
Fastjson 的故事,是一个典型的”便捷功能反噬“的故事:
autoType 为了”多态反序列化”的便利,把”加载任意类”的能力交给了输入数据。而一旦”任意类”能被外部控制,”任意代码执行”就只是时间问题。
对开发者而言,Fastjson 最深刻的教训是:任何”自动解析外部输入类型”的功能,都必须以”默认不信任”为前提——宁可牺牲一点便利,也不要把”加载任意类”的权力交出去。
参考资料
- Fastjson 官方关于 autoType 安全机制的说明与安全公告
- 关于 fastjson 1.2.47 绕过、autoType 黑名单机制的公开研究
- 各安全厂商关于 Fastjson 漏洞利用的归因报告
本文基于公开资料整理撰写,仅供网络安全研究与教育目的使用,不构成任何安全或法律建议。
相关文章
本文是「Java 反序列化攻击面」系列的第二篇:
- 《Java 反序列化攻击面(一):WebLogic 的 T3 协议与”漏洞打地鼠”史》 —— 系列第一篇
- 《Java 反序列化攻击面(三):Shiro 的 rememberMe 与”默认密钥”之罪》 —— 系列第三篇
- 《Log4Shell:一次震动全球 Java 生态的”完美漏洞”》 —— Java 生态的另一高危漏洞
- 《CVE-2025-24813 Tomcat PUT漏洞POC——反序列化RCE的完整利用链》 —— Java 中间件的漏洞全景
评论