Java反序列化漏洞自2015年Apache Commons Collections(CC)系列漏洞曝光以来,一直是企业级应用安全的“心头大患”。攻击者无需登录、无需交互,仅通过构造恶意序列化数据即可实现远程代码执行(RCE),危害极大。本文将系统梳理主流的Java反序列化利用链(Gadget Chain),深入剖析其技术原理、构造流程与防御方案,助你全面掌握这一高危漏洞的攻防对抗核心。

什么是Java反序列化漏洞?
Java的ObjectInputStream.readObject()方法用于将字节流还原为对象。然而,当目标系统在反序列化过程中未对输入数据进行严格校验时,攻击者便可精心构造包含恶意逻辑的序列化对象,诱导JVM在还原过程中自动执行任意代码。
这类漏洞的本质是 “不安全的反序列化”,而其中的关键在于找到可被串联起来形成完整攻击路径的“小工具”——即 Gadget Chain(利用链)。
经典利用链:CommonsCollections1(CC1)
作为最广为人知的反序列化链,CC1依赖于commons-collections:3.1/3.2.1库,在JDK 8u71之前版本有效。
▶ 利用链调用路径:
▶ 核心组件解析:
InvokerTransformer
实现反射调用的核心类,通过传入方法名、参数类型和实际参数,动态执行任意方法。TransformedMap
装饰器模式下的Map实现,当其值被修改时会触发checkSetValue(),进而调用绑定的valueTransformer.transform()。ChainedTransformer
将多个Transformer串联成链式调用,确保执行顺序可控。AnnotationInvocationHandler
JDK内置代理处理器,在反序列化时会调用entrySet(),间接触发Map中元素的setValue()操作,成为理想的入口点。
✅ 适用场景:存在
commons-collections旧版本且使用了readObject()处理用户输入的系统。
进阶变种:CommonsCollections3(CC3)——基于字节码加载的隐蔽攻击
CC3利用TemplatesImpl类的特性绕过部分检测机制,不再直接调用Runtime.exec(),而是通过加载恶意类字节码来执行命令,更具隐蔽性。
▶ 利用链结构:
▶ 关键技术亮点:
TemplatesImpl.newTransformer() 的副作用:该方法本用于XSLT模板转换,但在初始化过程中会动态定义并加载
_bytecodes中的类。静态代码块执行:攻击者编译一个带有恶意静态初始化块的
.class文件(如打开计算器),将其注入_bytecodes字段。绕过白名单检测:由于最终执行的是类加载而非直接命令调用,传统基于
Runtime或ProcessBuilder的WAF难以拦截。
⚠️ 前提条件:需具备
xalan库支持(通常随JDK自带),适用于JDK 6/7/8环境。
新型突破:CommonsCollections5(CC5)——适配更高JDK版本
随着Java安全更新逐步修复旧有缺陷(如AnnotationInvocationHandler限制),CC5应运而生,采用BadAttributeValueExpException作为新入口点,兼容至JDK 8u76。
▶ 利用链分析:
▶ 核心触发逻辑:
BadAttributeValueExpException.readObject()中存在一处逻辑漏洞:攻击者将
val设为TiedMapEntry实例,其toString()方法会调用getValue(),进而触发LazyMap.get()。LazyMap在获取不存在的key时,会调用工厂函数factory.transform(key),从而激活Transformer链。
🔍 优势对比: | 特性 | CC1 | CC3 | CC5 | |------|-----|-----|-----| | 是否依赖 AnnotationInvocationHandler | 是 | 是 | 否 | | 是否调用 Runtime.exec 直接 | 是 | 否(通过类加载) | 是 | | 最高支持JDK版本 | 8u71 | 8u76前 | 8u76前 | | 检测绕过能力 | 一般 | 强 | 中等 |
通用防御策略建议
面对层出不穷的反序列化利用链,单一防护手段已不足以应对。以下是企业必须采取的多层次防御措施:
✅ 1. 升级依赖库
升级
commons-collections至4.x版本(API变更,不再兼容旧链)使用最新版JDK(如JDK 8u202+、JDK 11+),已内置多项反序列化加固机制
✅ 2. 启用反序列化白名单
使用
SerialKiller、ysoserial的黑名单补丁或SecurityManager限制危险类加载自定义
ObjectInputStream,重写resolveClass()方法,仅允许特定包名下的类反序列化
✅ 3. 输入验证与最小权限原则
禁止接收不可信来源的序列化数据(如Redis、RMI、JMS等通道)
应用程序以非root权限运行,降低RCE后的危害范围
✅ 4. 使用现代替代方案
优先采用JSON、Protobuf等安全的数据交换格式
如必须使用Java原生序列化,请结合数字签名验证完整性
反序列化攻防仍在持续演进
从CC1到CC7,再到Fastjson、Jackson等第三方库的反序列化问题,Java生态中的“反序列化战争”从未停歇。作为开发者或安全人员,我们不仅要理解这些经典利用链的技术细节,更要建立起“默认不信任输入”的安全思维。
未来,随着Project Panama、Valhalla等JVM新特性的引入,以及模块化系统的普及,Java序列化的使用场景或将逐渐减少。但在那之前,加强代码审计、及时打补丁、部署运行时防护(RASP),仍是保障系统安全的最后一道防线。





















