Fastjson反序列化漏洞
一、Fastjson 基础
1.1 Fastjson 序列化与反序列化
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
1.2 结论
- Fastjson 序列化行为总结
- 序列化时调用 Getter 方法
- Fastjson 默认通过 JavaBean 规范来识别字段,即调用对应的
getXxx()
方法取值 * 没有公开的getter
方法的private
成员变量,默认不会被序列化 - 但如果直接用
@JSONField
注解标记字段,即使没有 getter 也能被序列化 transient
修饰的字段默认不会被序列化
- Fastjson 默认通过 JavaBean 规范来识别字段,即调用对应的
- 特殊情况
- 如果字段是
static
,不会被序列化 - 如果字段是
null
,默认会跳过,但可以通过SerializerFeature.WriteMapNullValue
来输出null
值 - 如果 Getter 方法名不规范(如
geturl
而不是getUrl
),Fastjson 可能无法识别 - 如果在 Getter 方法中加入自定义逻辑(如打印日志、计算值),Fastjson 序列化时会执行该逻辑
- 如果字段是
- 序列化时调用 Getter 方法
- Fastjson 反序列化行为总结
- 反序列化时调用 Setter 方法
- Fastjson 解析 JSON 时,会按字段名匹配 JavaBean 的
setXxx()
方法,调用 setter 来赋值 - 如果字段是
public
且无 setter 方法,Fastjson 会直接对该字段赋值
- Fastjson 解析 JSON 时,会按字段名匹配 JavaBean 的
- 特殊情况
- 如果字段是
private
且无 setter 方法,Fastjson 默认无法赋值,但可以通过@JSONField
注解或FieldBased
特性让其直接操作字段 - JSON 中包含未知字段时
- 默认会被忽略
- 如果开启
Feature.IgnoreNotMatch
,不会报错;如果关闭可能抛异常
- JSON 中缺失某个字段时
- 如果 JavaBean 有默认值(如构造函数赋值),则保留默认值
- 如果 Setter 方法中带有逻辑(比如日志打印、参数检查),反序列化时会实际执行该逻辑
- 如果字段是
- 反序列化时调用 Setter 方法
二、Fastjson 漏洞原理与机制分析
2.1 漏洞产生原因
2.1.1 设计缺陷
过度的灵活性设计
- Fastjson 为了提供便利的序列化体验,引入了
@type
字段来指定反序列化的目标类型 - 这种设计允许在 JSON 数据中动态指定要实例化的 Java 类,为攻击者提供了控制程序执行流程的入口
- 缺乏对用户输入类型信息的严格验证和限制机制
任意类实例化风险
1 |
|
2.1.2 自动方法调用机制
Setter 方法自动调用
- 反序列化过程中,Fastjson 会自动调用对象的 setter 方法进行属性赋值
- 如果 setter 方法内部包含危险操作(如网络请求、文件操作、系统命令执行),将被无条件触发
- 攻击者可以通过构造特定的 JSON 数据来控制 setter 方法的参数
Getter 方法意外触发
- 在特定场景下(如
$ref
引用解析、JSONObject 包装转换),getter 方法会被意外调用 - 某些类的 getter 方法内部存在副作用操作,成为攻击利用点
- 这种隐式的方法调用往往被开发者忽略
初始化逻辑执行
- 类的构造函数、静态代码块、初始化方法在对象创建时自动执行
- 恶意类可以在这些初始化过程中嵌入危险代码
- 即使不进行属性赋值,仅实例化就可能触发攻击
2.1.3 安全机制
黑白名单绕过
- 早期版本缺乏有效的类型校验机制
- 后期版本的黑白名单实现存在绕过可能:
- 类名处理逻辑缺陷(如
L;
、LL;;
、[
等前后缀绕过) - 哈希比对机制存在冲突风险
- 缓存机制被恶意利用
- expectClass 参数校验逻辑漏洞
- 类名处理逻辑缺陷(如
智能特性带来的风险
- smartMatch 智能匹配:可能触发意外的方法调用
- $ref 引用机制:延迟解析可能导致额外的计算和方法执行
- 容器类型处理:集合、Map 等容器在元素处理过程中可能触发危险方法
2.2 反序列化基本流程
2.2.1 解析入口与初始化
解析入口点
1 |
|
流程初始化
- 创建
DefaultJSONParser
实例 - 初始化
ParserConfig
配置(包含安全策略、黑白名单等) - 准备词法分析器
JSONLexer
进行 Token 解析 - 开始逐字符解析 JSON 文本
2.2.2 核心解析流程
第一阶段:词法分析
- JSONLexer 将 JSON 文本分解为 Token 流
- 识别关键字段,特别是
@type
字段 - 处理字符串、数字、布尔值等基本数据类型
第二阶段:类型确定
1 |
|
第三阶段:对象构造
- 类加载验证:通过反射机制加载指定类
- 实例创建:调用类的构造函数创建对象实例
- 反序列化器定位:寻找适合的
Deserializer
(通常是JavaBeanDeserializer
)
第四阶段:属性绑定
- 字段映射:将 JSON 键与 Java 对象属性进行映射
- Setter 调用:逐个调用 setter 方法进行属性赋值
- 类型转换:处理数据类型转换(String → Date、Number 转换等)
- 嵌套对象处理:递归处理嵌套的复杂对象
2.2.3 特殊处理机制
引用解析机制
$ref
引用在解析时会延迟计算- 可能触发目标对象的 getter 方法
- 循环引用检测可能被绕过或利用
容器类型处理
- 集合类(List、Set):添加元素时可能触发元素对象的方法
- Map 类:键值对操作可能触发 key 的
hashCode()
、equals()
方法 - 数组类型:数组元素赋值和类型转换过程
JSONObject/JSONArray 包装
- 在类型转换过程中会调用目标对象的方法
toJavaObject()
方法可能触发额外的方法调用- 包装和解包装过程存在安全风险
2.2.4 关键节点与风险点
类型校验节点(checkAutoType)
1 |
|
- 黑白名单匹配
- 哈希值快速比对
- expectClass 参数影响校验逻辑
- 可能的绕过点和安全漏洞
方法调用节点
- 构造函数调用:对象实例化时
- Setter 方法调用:属性赋值时
- Getter 方法调用:引用解析、类型转换时
- 特殊方法调用:hashCode、equals、toString 等
2.3 攻击向量与利用链
2.3.1 经典攻击向量
JNDI 注入攻击
- 支持协议:RMI、LDAP、DNS
- 攻击流程:@type 指定 JndiDataSourceLookup 等类 → setter 方法触发 JNDI 查询 → 远程加载恶意类
- 环境限制:高版本 JDK 对远程类加载有限制(如 trustURLCodebase=false)
模板类利用
- TemplatesImpl 攻击链:通过模板引擎加载恶意字节码
- ScriptEngineManager:脚本引擎执行恶意代码
- 适用场景:不出网环境下的本地代码执行
第三方组件利用
- 数据库连接池:c3p0、Tomcat-DBCP、HikariCP
- ORM 框架组件:MyBatis、Hibernate 相关类
- Web 框架组件:Spring、XBean 等框架类
2.3.2 环境依赖分析
JDK 版本差异
JDK 版本 | 限制措施 | 影响 |
---|---|---|
JDK 8u121+ | 默认限制 RMI 远程加载 | RMI 攻击向量失效 |
JDK 8u191+ | 默认限制 LDAP 远程加载 | LDAP 攻击向量失效 |
JDK 11+ | 移除部分内部类 | BCEL 等利用链失效 |
网络环境影响
- 出网环境:可利用远程 JNDI 服务
- 不出网环境:依赖本地类库和二次反序列化
- 内网环境:可能存在内网 RMI/LDAP 服务
2.4 安全机制与防护
2.4.1 版本演进与安全改进
1.2.24 及之前版本
- autoType 默认开启
- 缺乏有效的安全校验机制
- 安全风险最高
1.2.25 - 1.2.67 版本
- autoType 默认关闭
- 引入黑白名单机制
- 但存在多种绕过方法
1.2.68+ 版本
- 引入 safeMode 安全模式
- 加强类型校验逻辑
- 持续修复绕过漏洞
2.4.2 关键安全配置
autoType 控制
1 |
|
Feature 特性控制
SupportNonPublicField
:支持私有字段序列化FieldBased
:基于字段而非 setter/getterDisableCircularReferenceDetect
:禁用循环引用检测IgnoreNotMatch
:忽略不匹配字段
2.4.3 攻击面评估
解析方法风险对比
方法 | 风险级别 | 原因 |
---|---|---|
JSON.parse() |
高 | 完全依赖 JSON 中的 @type |
JSON.parseObject(json, Class) |
中 | 仍可能通过嵌套 @type 触发 |
JSON.parseArray() |
中 | 数组元素中的 @type 风险 |
典型利用场景
- Web 接口:接受 JSON 数据的 HTTP 接口
- 消息队列:处理序列化消息的消费者
- 缓存系统:从缓存中反序列化数据
- 配置文件:解析 JSON 格式的配置文件
- 日志系统:处理包含 JSON 数据的日志
2.5 总结
Fastjson 漏洞的根本原因在于其设计理念中对灵活性的过度追求,以及对用户输入的过度信任。通过 @type
字段实现的动态类型指定功能,虽然提供了强大的序列化能力,但同时也为攻击者提供了控制程序执行流程的入口。
理解 Fastjson 的反序列化流程和关键安全机制,对于识别和防护相关安全风险至关重要。在实际应用中,应该采用最新版本的 Fastjson,启用安全模式,并严格控制可序列化的类型范围。
三、Fastjson反序列化漏洞利用分析
3.1 Fastjson漏洞时间线概
Fastjson反序列化漏洞主要围绕@type
字段autoType
机制展开。从最初的1.2.24版本到后续版本,阿里巴巴不断修补漏洞,而安全研究者也在持续寻找绕过方法,形成了一攻防对抗”的演进过程
3.2 早期漏洞利用 1.2.24及之前版本
3.2.1 JdbcRowSetImpl利用
影响版本 Fastjson 1.2.24
漏洞原理
com.sun.rowset.JdbcRowSetImpl
类在调用setAutoCommit()
方法时,会触connect()
方法connect()
方法会尝试连接dataSourceName
指定的数据源- 通过控制
dataSourceName
为JNDI服务地址,可触发JNDI注入
Payload
1 |
|
攻击流程
- Fastjson解析JSON时识别
@type
为JdbcRowSetImpl
- 实例化
JdbcRowSetImpl
对象 - 调用
setDataSourceName()
设置数据源 - 调用
setAutoCommit(true)
触发connect()
方法 connect()
方法中调用lookup(dataSourceName)
导致JNDI注入
3.2.2 TemplatesImpl利用
影响版本 Fastjson 1.2.24
漏洞原理
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
类可加载字节码- 通过
_bytecodes
字段注入恶意类的字节码 - 调用
getOutputProperties()
方法时会触发newTransformer()
newTransformer()
中调用defineTransletClasses()
实例化字节码中的类
限制条件
- 需要开启
Feature.SupportNonPublicField
特性 - 因为
_bytecodes
等字段是私有字段,正常情况下无法设置
Payload示例
1 |
|
3.3 黑名单绕过(1.2.25-1.2.47)
3.3.1 checkAutoType机制引入
版本变化 Fastjson 1.2.25
Fastjson 1.2.25版本引入checkAutoType()
函数,通过黑白名单机制来防御反序列化攻击:
denyList
(黑名单):禁止的类名列表acceptList
(白名单):允许的类名列表autoTypeSupport
:默认为false
,是否开启autoType支持
黑名单包含的关键类:
com.sun.rowset.JdbcRowSetImpl
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase
3.3.2 L;绕过方法
影响版本 Fastjson 1.2.25 - 1.2.41
绕过原理
- Fastjson在处理类名时,会去除开头的
L
和结尾的;
- 通过在类名前添加
L
,结尾添加;
来绕过黑名单检测
Payload
1 |
|
前提条件 需要autoTypeSupport
属性为true
,1.2.25+默认为false
3.3.3 LL;;绕过方法
影响版本 Fastjson 1.2.25 - 1.2.42
绕过原理
- 双重
L
和;
符号绕过,利用多次处理的逻辑缺陷
Payload
1 |
|
3.3.4 [字符绕过方法
影响版本 Fastjson 1.2.25 - 1.2.43
绕过原理
- 利用数组类型的处理逻辑,在类名前添加
[
字符
Payload
1 |
|
3.3.5 其他利用
Mybatis JndiDataSourceFactory利用链:
影响版本 Fastjson 1.2.25 - 1.2.45
1 |
|
3.4 缓存绕过(1.2.25-1.2.47)
3.4.1 java.lang.Class缓存绕过
影响版本 Fastjson 1.2.33 - 1.2.47
绕过原理
- 通过
java.lang.Class
将目标类加载到缓存中 - 缓存中的类在后续反序列化时不会再次经过
checkAutoType
检测 - 1.2.48之后缓存机制被改为默认关闭
Payload
1 |
|
3.5 高版本绕过技术(1.2.48+)
3.5.1 expectClass绕过(1.2.68)
影响版本 Fastjson 1.2.68
绕过原理
- 利用
expectClass
参数绕过checkAutoType()
检测 - 主要使用
Throwable
和AutoCloseable
接口进行绕过
3.5.2 BCEL ClassLoader利用
影响版本 适用于多个版本(Java 8u251之前)
绕过原理
com.sun.org.apache.bcel.internal.util.ClassLoader
在加载类时,如果类名包含$$BCEL$$
- 会对
$$BCEL$$
后的字符串进行BCEL解码,作为类的字节码 - 通过
defineClass()
创建Class对象,实现任意代码执行
Tomcat DBCP利用链:
1 |
|
Mybatis利用链:
1 |
|
注意 Java 8u251之后,BCEL相关类被移除,此利用方法失效
3.5.3 XBean JNDI利用
XBean JndiConverter
1 |
|
Cocoon JMSContentInterceptor
1 |
|
3.6 safeMode绕过(1.2.68+)
3.6.1 safeMode机制
版本引入 Fastjson 1.2.68+
安全机制
safeMode
开启后,完全禁用autoType
功能- 即使配置了白名单,在
safeMode
模式下也会失效 - 这是Fastjson最严格的安全模式
配置方式
- 代码配置
ParserConfig.getGlobalInstance().setSafeMode(true)
- JVM参数
-Dfastjson.parser.safeMode=true
- 配置文件:在
fastjson.properties
中设置
3.6.2 1.2.68-1.2.80版本绕过机制
绕过原理 1.2.68版本将java.lang.Runnable
、java.lang.Readable
、java.lang.AutoCloseable
加入黑名单,但可以通过异常Throwable
作为期望类进行绕过
利用条件
- 目标类必须继承自
Throwable
- 应用未开启
safeMode
模式 - 使用
JSON.parse
或JSON.parseObject
且未指定具体类型
示例Payload
1 |
|
3.7 CVE-2022-25845 (1.2.80)
3.7.1 漏洞概述
CVE编号 CVE-2022-25845
CVSS评分 8.1(高危)
影响版本 Fastjson 1.2.80及以下版本
修复版本 1.2.83+
3.7.2 漏洞原理
核心机制 该漏洞利用了Fastjson在处理Throwable
类型时的特殊逻辑,绕过了checkAutoType
安全检查
关键代码路径
com.alibaba.fastjson.parser.ParserConfig#getDeserializer
检测目标类是否继承Throwable
- 如果是,则调用
ThrowableDeserializer.deserialize()
ThrowableDeserializer#createException
中实现代码执行
3.7.3 利用条件与限
利用条件
- Fastjson版本 ≤ 1.2.80
- 未开启
safeMode
模式 - 应用调用
JSON.parse
或JSON.parseObject
且未指定具体类型 - 传入用户可控的JSON数据
利用限制
- 只能调用继承
Throwable
类的反序列化gadget - 实际可利用的gadget类相对有限
3.7.4 利用Payload
基础绕过Payload
1 |
|
BCEL字节码注入:
1 |
|
组合利用链:
1 |
|
3.8 1.2.83版本修复与后续发展
3.8.1 1.2.83版本修复
修复措施 2022年3月3日,阿里巴巴发布Fastjson 1.2.83版本,修复了CVE-2022-25845漏洞
主要改进
- 加强了对
Throwable
类型的安全检测 - 完善了
checkAutoType
机制 - 增强了
safeMode
的防护能力
3.8.2 Fastjson项目状态
项目归档 2024年3月3日,Fastjson 1.x项目被官方归档,不再维护
官方建议
- 推荐升级到Fastjson 2.0版本
- Fastjson 2.0性能更好,安全性更高
- 提供了兼容包,便于迁移
3.8.3 Fastjson 2.0安全改进
架构重构
- 完全重写了反序列化引擎
- 默认禁用
autoType
功能 - 更严格的类型检查机制
安全特性:
- 内置更完善的黑名单
- 支持更细粒度的安全配置
- 提供了更好的安全审计功能
3.9 最新绕过技术与防护对抗
3.9.1 WAF绕过技术
编码绕过
- Base64编码:在multipart请求中使用
Content-Transfer-Encoding: base64
- QP编码:使用
quoted-printable
编码绕过WAF检测
嵌套绕过
1 |
|
3.9.2 现代防护策略
多层防护
- 版本升级 升级到Fastjson 2.0或最新的1.2.83+版本
- 安全配置 强制开启
safeMode
模式 - 输入验证 严格验证JSON输入,过滤
@type
字段 - 网络隔离 限制应用的外网访问,防止JNDI/RMI攻击
- 运行时防护 使用RASP等运行时应用自我保护技术
监控告警
- 监控包含
@type
的JSON请求 - 检测异常的网络连接(RMI、LDAP等)
- 监控异常的类加载行为
- 记录反序列化异常和错误
3.10 漏洞检测技术
3.10.1 版本探测
无报错探测:
1 |
|
延迟探测
1 |
|
3.10.2 DNSLOG检测
不同版本的DNS探测
1 |
|
3.10.3 safeMode检测
检测是否开启safeMode
1 |
|
3.11 防御建议
3.11.1 版本升级策略
- 首选方案: 升级到Fastjson 2.0版本
- 性能更好,安全性更高
- 提供兼容包,便于迁移
- 官方长期维护支持
- 次选方案: 升级到1.2.83+版本并开启
safeMode
- 注意1.x版本已停止维护
- 仅作为临时过渡方案
3.11.2 安全配置
强制开启safeMode
- ParserConfig.getGlobalInstance().setSafeMode(true);
禁用autoType
- ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
- 严格白名单: 如必须使用autoType,严格配置白名单
3.11.3 应用层防护
- 输入验证 严格验证JSON输入,过滤
@type
字段 - 网络隔离 限制应用的外网访问,防止JNDI/RMI攻击
- 运行时防护 使用RASP等运行时应用自我保护技术
- 监控告警 监控异常的网络连接和类加载行为
3.12 总结
Fastjson反序列化漏洞的演进体现了攻防对抗的典型过程:
- 1.2.24及之前: 无防护,直接利用
- 1.2.25-1.2.47 黑名单防护,各种绕过技术
- 1.2.48-1.2.67 缓存机制改进,利用难度增加
- 1.2.68-1.2.80 safeMode机制,但存在Throwable绕过(CVE-2022-25845)
- 1.2.83+ 修复Throwable绕过,最严格防护
- Fastjson 2.0 架构重构,安全性根本改善
每个版本的修复都会被新的绕过方法突破,直到Fastjson 2.0的架构重构。这提醒开发者需要:
- 及时跟进安全更新,优先升级到Fastjson 2.0
- 采用多层防护策略,不依赖单一安全机制
- 对用户输入保持警惕,严格验证和过滤
- 在安全性和功能性之间找到平衡,避免过度依赖autoType功能
- 建立完善的安全监控和应急响应机制
参考链接
- https://drun1baby.top/2022/08/04/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Fastjson%E7%AF%8701-Fastjson%E5%9F%BA%E7%A1%80/
- https://xz.aliyun.com/t/16547time__1311=GuD%3DY5BK0I8x%2FD0lD2mDumADO%2Bn2xPi%3DPa4D
- https://xz.aliyun.com/t/14872time__1311=GqA2Y50K4IxBqDwqeqBK96DmE9O83x
- https://www.cnblogs.com/nice0e3/p/14601670.html
- https://www.cnblogs.com/LINGX5/p/18797812
- https://rivers.chaitin.cn/blog/cq70jnqp1rhtmlvvdo2g
- https://www.cnblogs.com/LINGX5/p/18802834
- https://github.com/safe6Sec/Fastjson