保姆级讲解CC1-7(跟踪代码调试+讲解)
字数 4258
更新时间 2026-03-12 13:53:13

Java反序列化漏洞利用链(CC1-7)保姆级教学文档

1. Java原生序列化与反序列化

1.1 核心概念

序列化:将一个Java对象转换成字节流、字符串或二进制文件的形式,用于对象的传输与持久化。
反序列化:将字节、文本数据还原成Java对象。

1.2 应用场景

  1. 对象持久化保存到本地文件
  2. 网络通信中传输对象数据
  3. Java的序列化方案包括:
    • 原生实现
    • 基于XML、SOAP、JSON
    • Protocol Buffers (Protobuf)
    • Thrift
    • Hessian

1.3 简单示例

// Person类必须实现Serializable接口
class Person implements Serializable {
    private String name;
    private int age;
    // 构造方法、getter/setter、toString()省略
}

// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"));
oos.writeObject(person);
oos.close();

// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"));
Person p = (Person) ois.readObject();
ois.close();

1.4 关键特性

  1. Serializable接口:标记接口,无方法需要实现
  2. 静态成员变量:不能被序列化(属于类本身,而非对象实例)
  3. transient关键字:标记的成员变量不参与序列化
  4. 自定义序列化:可重写writeObject()readObject()方法控制序列化逻辑

1.5 安全风险

服务端对恶意序列化对象执行反序列化时,会自动执行readObject()方法中的代码,为攻击者提供了在服务器上执行任意代码的能力。

2. 反序列化漏洞利用原理

2.1 利用条件

要实现完整的反序列化攻击,需要三部分:

  1. 入口类(Gadget):

    • 实现Serializable接口
    • 重写readObject()方法
    • 方法中调用某个通用方法(如hashCode()equals()toString()
    • 最好是JDK自带类,通用性强
  2. 调用链(Chain):

    • 从入口类的通用方法开始
    • 通过"相同方法名+兼容类型"层层传递
    • 最终调用到危险方法
  3. 执行点(Sink):

    • 最终执行恶意操作的代码位置
    • 如RCE、SSRF、文件写入等

2.2 常见入口类

  • HashMapreadObject()中调用hash()key.hashCode()
  • HashtablereadObject()中调用key.equals()
  • PriorityQueuereadObject()中调用比较器的compare()
  • BadAttributeValueExpExceptionreadObject()中调用val.toString()

3. 核心技术详解

3.1 反射(Reflection)

核心:反向获取与操作类的所有内容,包括私有成员。

常用操作

  1. 通过Class对象实例化对象
  2. 获取/修改类属性(包括私有)
  3. 获取/调用类方法(包括私有)

在反序列化中的作用

  1. 修改对象属性值,构造利用链
  2. 动态调用任意方法
  3. 绕过WAF过滤(方法名拆分拼接)

关键方法

  • Class.forName("类全限定名"):获取Class对象
  • getDeclaredField():获取所有属性(包括私有)
  • setAccessible(true):突破访问限制
  • getMethod()/invoke():获取并调用方法

3.2 动态代理

核心模式:不直接访问原对象,通过代理对象间接访问,在不修改原代码前提下扩展功能。

JDK动态代理实现

InvocationHandler handler = new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 增强逻辑
        return method.invoke(target, args);
    }
};
Object proxy = Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    handler
);

在反序列化中的作用

  1. 隐式自动执行:代理对象任何方法被调用都会进入invoke()
  2. 灵活拼接调用链:无论入口类调用什么方法,都能通过代理走到目标逻辑

3.3 类动态加载

核心:从字节码动态加载类并执行代码。

类加载流程

loadClass() → 双亲委派 → findClass() → defineClass()(native)

常用类加载器

  1. Bootstrap ClassLoader:加载JDK核心库
  2. Extension ClassLoader:加载JRE扩展库
  3. Application ClassLoader:加载项目classpath

动态加载方法

  1. URLClassLoader:从本地/远程路径加载类
  2. ClassLoader.defineClass():直接从字节码数组加载(需反射调用)
  3. Unsafe.defineClass():底层方法(需反射获取Unsafe实例)

关键类TemplatesImpl

  • 实现Serializable接口
  • newTransformer()方法会加载字节码并实例化类
  • 用于动态加载恶意类执行代码

4. Commons Collections利用链详解

4.1 Commons Collections核心接口

Transformer接口

public interface Transformer {
    Object transform(Object input);
}

关键实现类

  1. ConstantTransformer:无论输入什么,都返回固定常量
  2. InvokerTransformer:封装反射调用,可调用任意对象的任意方法
  3. ChainedTransformer:将多个Transformer链式调用
  4. InstantiateTransformer:调用构造方法实例化对象

4.2 CC1链(TransformedMap版)

完整调用链

AnnotationInvocationHandler.readObject()
    → Map.Entry.setValue()
        → TransformedMap.checkSetValue()
            → ChainedTransformer.transform()
                → ConstantTransformer.transform()  // 返回Runtime.class
                → InvokerTransformer.transform()   // 反射调用getRuntime()
                → InvokerTransformer.transform()   // 反射调用getRuntime()
                → InvokerTransformer.transform()   // 反射调用exec()

构造要点

  1. TransformedMap.decorate()装饰Map,valueTransformer设为恶意ChainedTransformer
  2. AnnotationInvocationHandler构造时传入恶意Map
  3. Map的key必须与注解实际属性名匹配
  4. 用ConstantTransformer固定输入为Runtime.class

4.3 CC1链(LazyMap版/ysoserial原版)

调用链

AnnotationInvocationHandler.readObject()
    → memberValues.get()  // 动态代理触发invoke()
        → LazyMap.get()
            → factory.transform()  // 即ChainedTransformer

特点

  1. 使用LazyMap,只有key不存在时才调用transform
  2. 两层AnnotationInvocationHandler嵌套
  3. 内层作为动态代理的InvocationHandler
  4. 代理Map接口,外层接收代理对象

JDK 8u71修复readObject()中创建新LinkedHashMap操作,不直接操作传入的Map

4.4 CC6链(通用性强)

调用链

HashMap.readObject()
    → key.hashCode()  // key为TiedMapEntry
        → TiedMapEntry.hashCode()
            → TiedMapEntry.getValue()
                → map.get()  // map为LazyMap
                    → factory.transform()

优势

  1. 不受JDK版本限制
  2. 入口类为HashMap,极为常见
  3. 兼容性极强

构造要点

  1. 构造LazyMap时先传无害Transformer
  2. 创建TiedMapEntry(key为LazyMap中不存在的值)
  3. 将TiedMapEntry作为HashMap的key
  4. put完成后,反射修改LazyMap.factory为恶意Transformer
  5. 调用lazyMap.remove(key)删除测试key

4.5 CC3链(动态类加载版)

核心:用TemplatesImpl动态加载字节码执行代码,绕过Runtime黑名单检测。

TemplatesImpl关键属性

  • _bytecodes:恶意类字节码数组(byte[][])
  • _name:任意非空字符串
  • _tfactory:transient修饰,反序列化时会自动赋值
  • 恶意类必须继承AbstractTranslet

两种实现方式

  1. 简化版:直接用InvokerTransformer调用newTransformer()
  2. 原版:用InstantiateTransformer实例化TrAXFilter,其构造方法调用templates.newTransformer()

4.6 CC4链(Commons Collections 4)

调用链

PriorityQueue.readObject()
    → heapify() → siftDown() → siftDownUsingComparator()
        → comparator.compare()  // comparator为TransformingComparator
            → transformer.transform()

特点

  1. 针对Commons Collections 4.x
  2. TransformingComparator在CC4中实现了Serializable
  3. 需向PriorityQueue添加至少2个元素(size≥2)
  4. 构造时先传无害Transformer,add完成后反射修改

4.7 CC2链(无数组版)

特点

  1. 针对Commons Collections 4.x
  2. 不依赖ChainedTransformer数组
  3. 直接用InvokerTransformer调用newTransformer()
  4. 在某些环境(如重写类加载)中更有优势

4.8 CC5链

调用链

BadAttributeValueExpException.readObject()
    → val.toString()  // val为TiedMapEntry
        → TiedMapEntry.toString()
            → getValue() → map.get()

特点:toString()触发,与CC6类似

4.9 CC7链

调用链

Hashtable.readObject()
    → reconstitutionPut()
        → key.equals()  // key为AbstractMapDecorator
            → AbstractMap.equals()
                → map.get()

特点:equals()触发,涉及哈希碰撞技巧

5. URLDNS链(漏洞验证)

最简单利用链,用于验证反序列化漏洞存在。

调用链

HashMap.readObject()
    → hash() → key.hashCode()  // key为URL对象
        → URL.hashCode()
            → URLStreamHandler.hashCode()
                → getHostAddress()  // 发起DNS解析

构造要点

  1. 反射修改URL对象的hashCode属性(先改非-1,put后再改回-1)
  2. 避免put时本地触发DNS请求
  3. 纯JDK类,通用性极强

6. 防御与绕过

6.1 常见防护

  1. 输入过滤:检查反序列化数据
  2. 黑名单:过滤危险类(如InvokerTransformer、TemplatesImpl)
  3. 白名单:只允许特定类反序列化
  4. JEP 290:Java的反序列化过滤器
  5. 升级依赖:修复已知漏洞版本

6.2 绕过思路

  1. 多态调用:利用接口/父类类型调用子类方法
  2. 动态代理:统一入口,灵活跳转
  3. 类加载绕过:动态加载字节码,不依赖已有类
  4. 非数组链:如CC2,避免数组处理问题
  5. 新入口类:寻找未防护的入口点

7. 实践建议

7.1 学习路径

  1. 掌握Java序列化基础
  2. 深入理解反射、动态代理、类加载
  3. 调试CC6链(兼容性最好)
  4. 掌握CC3链(动态加载绕过)
  5. 了解其他链变种

7.2 调试技巧

  1. 手敲代码,避免复制粘贴
  2. 在关键方法设断点(readObject、transform、invoke等)
  3. 关注if判断条件,满足执行路径
  4. 注意transient变量和静态变量
  5. 理解每个类在链中的作用

7.3 实际应用

  1. 漏洞验证:URLDNS链简单有效
  2. 命令执行:CC1、CC6 + Runtime.exec
  3. 代码执行:CC3 + TemplatesImpl(绕过能力强)
  4. 特殊环境:CC2(无数组)、CC4(CC4版本)

8. 总结

Java反序列化漏洞的核心在于控制数据、控制代码执行。通过精心构造的序列化对象,在反序列化过程中触发预设的调用链,最终实现任意代码执行。

关键技术栈

  1. 序列化机制 → 数据载体
  2. 反射 → 动态操作
  3. 动态代理 → 灵活跳转
  4. 类加载 → 代码执行
  5. Commons Collections → 调用链组件

实用链选择

  • 学习入门:CC1(理解完整流程)
  • 实际测试:CC6(兼容性最佳)
  • 绕过防护:CC3(动态加载)
  • 特殊场景:CC2(无数组需求)

掌握这些知识不仅有助于理解反序列化漏洞,更能深入理解Java安全机制、类加载原理、设计模式应用等多个层面的知识体系。

相似文章
相似文章
 全屏