Java反序列化漏洞利用链深度解析:从CC1到CC5,攻防实战全攻略

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

Java反序列化漏洞利用链深度解析:从CC1到CC5,攻防实战全攻略


什么是Java反序列化漏洞?

Java的ObjectInputStream.readObject()方法用于将字节流还原为对象。然而,当目标系统在反序列化过程中未对输入数据进行严格校验时,攻击者便可精心构造包含恶意逻辑的序列化对象,诱导JVM在还原过程中自动执行任意代码。

这类漏洞的本质是 “不安全的反序列化”,而其中的关键在于找到可被串联起来形成完整攻击路径的“小工具”——即 Gadget Chain(利用链)


经典利用链:CommonsCollections1(CC1)

作为最广为人知的反序列化链,CC1依赖于commons-collections:3.1/3.2.1库,在JDK 8u71之前版本有效。

▶ 利用链调用路径:

1ObjectInputStream.readObject()
2    AnnotationInvocationHandler.readObject()
3        MapEntry.setValue()
4            TransformedMap.checkSetValue()
5                ChainedTransformer.transform()
6                    InvokerTransformer.transform()
7                        Method.invoke()
8                            Runtime.exec()

▶ 核心组件解析:

  1. InvokerTransformer
    实现反射调用的核心类,通过传入方法名、参数类型和实际参数,动态执行任意方法。

    1new InvokerTransformer("exec", 
    2    new Class[]{String.class}, 
    3    new Object[]{"calc"});
  2. TransformedMap
    装饰器模式下的Map实现,当其值被修改时会触发checkSetValue(),进而调用绑定的valueTransformer.transform()

  3. ChainedTransformer
    将多个Transformer串联成链式调用,确保执行顺序可控。

  4. AnnotationInvocationHandler
    JDK内置代理处理器,在反序列化时会调用entrySet(),间接触发Map中元素的setValue()操作,成为理想的入口点。

适用场景:存在commons-collections旧版本且使用了readObject()处理用户输入的系统。


进阶变种:CommonsCollections3(CC3)——基于字节码加载的隐蔽攻击

CC3利用TemplatesImpl类的特性绕过部分检测机制,不再直接调用Runtime.exec(),而是通过加载恶意类字节码来执行命令,更具隐蔽性。

▶ 利用链结构:

1AnnotationInvocationHandler.readObject()
2    -> map.entrySet()
3        -> TransformedMap.get()
4            -> ChainedTransformer.transform()
5                -> ConstantTransformer → 返回 TemplatesImpl 实例
6                -> InvokerTransformer → 调用 newTransformer()
7                    -> defineTransletClasses() → 加载 _bytecodes 中的恶意类
8                        -> 静态代码块执行(如 Runtime.exec("calc"))

▶ 关键技术亮点:

  • TemplatesImpl.newTransformer() 的副作用:该方法本用于XSLT模板转换,但在初始化过程中会动态定义并加载_bytecodes中的类。

  • 静态代码块执行:攻击者编译一个带有恶意静态初始化块的.class文件(如打开计算器),将其注入_bytecodes字段。

  • 绕过白名单检测:由于最终执行的是类加载而非直接命令调用,传统基于RuntimeProcessBuilder的WAF难以拦截。

⚠️ 前提条件:需具备xalan库支持(通常随JDK自带),适用于JDK 6/7/8环境。


新型突破:CommonsCollections5(CC5)——适配更高JDK版本

随着Java安全更新逐步修复旧有缺陷(如AnnotationInvocationHandler限制),CC5应运而生,采用BadAttributeValueExpException作为新入口点,兼容至JDK 8u76。

▶ 利用链分析:

1ObjectInputStream.readObject()
2    BadAttributeValueExpException.readObject()
3        TiedMapEntry.toString()
4            LazyMap.get()
5                ChainedTransformer.transform()
6                    ... 后续同CC1 ...

▶ 核心触发逻辑:

  • BadAttributeValueExpException.readObject() 中存在一处逻辑漏洞:

    1if (valObj instanceof String || /* 其他基本类型 */ ) {
    2    val = valObj.toString(); // 若 val 是 TiedMapEntry,则触发 toString()
    3}
  • 攻击者将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. 启用反序列化白名单

  • 使用SerialKillerysoserial的黑名单补丁或SecurityManager限制危险类加载

  • 自定义ObjectInputStream,重写resolveClass()方法,仅允许特定包名下的类反序列化

✅ 3. 输入验证与最小权限原则

  • 禁止接收不可信来源的序列化数据(如Redis、RMI、JMS等通道)

  • 应用程序以非root权限运行,降低RCE后的危害范围

✅ 4. 使用现代替代方案

  • 优先采用JSON、Protobuf等安全的数据交换格式

  • 如必须使用Java原生序列化,请结合数字签名验证完整性


反序列化攻防仍在持续演进

从CC1到CC7,再到Fastjson、Jackson等第三方库的反序列化问题,Java生态中的“反序列化战争”从未停歇。作为开发者或安全人员,我们不仅要理解这些经典利用链的技术细节,更要建立起“默认不信任输入”的安全思维。

未来,随着Project Panama、Valhalla等JVM新特性的引入,以及模块化系统的普及,Java序列化的使用场景或将逐渐减少。但在那之前,加强代码审计、及时打补丁、部署运行时防护(RASP),仍是保障系统安全的最后一道防线。

发表评论

评论列表

还没有评论,快来说点什么吧~