CommonsCollections4
CC4不再是通过TransformedMap.checkSetValue
方法调用到ChainedTransformer.transform
;而是通过TransformingComparator.compare
调用的ChainedTransformer.transform
;
CC4是在commons-collections4-4.0中的链,因为TransformingComparator
类在commons-collections4-4.0.jar中实现了Serializable接口,而在commons-collections3中TransformingComparator
类没有实现Serializable接口;
可以看到TransformingComparator
中的compare
调用了transform方法,而且transformer是可控的
1 | public TransformingComparator(final Transformer<? super I, ? extends O> transformer, |
CC4的入口类是PriorityQueue
,因为PriorityQueue的readObject
间接调用了compare
;而且调用compare的对象可控;
readObject() == > heapify()
1 | private void readObject(java.io.ObjectInputStream s) |
注意:size >>> 1
是一个位运算操作,用于将一个数值向右位移1位,相当于除以2取整的操作。如果要符合i >= 0
的条件,size最先要为2;也就是说,在创建PriorityQueue
对象时,至少要add两次(其实反射修改size属性值也是可以的),使得size的值为2,才会调用到siftDown,
1 | private void heapify() { |
1 | private void siftDown(int k, E x) { |
最后在siftDownUsingComparator
中调用了comparator.compare
;
1 | private void siftDownUsingComparator(int k, E x) { |
还需要注意一下PriorityQueue的add
方法
1 | public boolean add(E e) { |
当第二次add
数据时,i不等于0;会调用else中的siftUp
1 | public boolean offer(E e) { |
1 | private void siftUp(int k, E x) { |
最后在siftUpUsingComparator
方法中也会调用compare
方法,和CC6一样,如果不做修改的话,会在序列化时就会调用;
所以需要在序列化时修改一下comparator的值,调用完add方法之后,再通过反射修改回来
1 | private void siftUpUsingComparator(int k, E x) { |
poc:
1 | package demo1; |
CommonsCollections2
CC2是在commons-collections4-4.0中的
1 | public int compare(final I obj1, final I obj2) { |
在TransformingComparator
类中,compare
方法的obj1
是可控的,可以在上面的源码中看到,obj1其实就是PriorityQueue
类中的在调用readObject时的heapify
方法的queue[i]
的值,这个值就是我们add的数据,如果我add的是templates
;我们可以直接调用InvokerTransformer
;就不用ChainedTransformer
的数组嵌套调用了
1 | new InvokerTransformer("newTransformer", null, null).transform(templates) |
虽然queue
数组被transient
修饰了,但是在writeObject函数中通过s.writeObject(queue[i]);
来序列化 queue
数组的元素。而不是把queue当作一个Object数组存贮;所以queue的数据还是会被序列化,
1 | transient Object[] queue; |
1 | private void writeObject(java.io.ObjectOutputStream s) |
poc
1 | package demo1; |
CommonsBeanutils
CommonsBeanutils 是应用于 javabean 的工具,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法
所谓javaBean,是指符合如下标准的Java类:
- 类是公共的
- 有一个无参的公共的构造器
- 有私有属性,且须有对应的get、set方法去设置属性
- 对于boolean类型的成员变量,允许使用”is”代替上面的”get”和”set”
创建Javabean类
1 | public class Student { |
创建测试类
1 | import org.apache.commons.beanutils.PropertyUtils; |
TemplatesImpl类下的getOutputProperties
方法也符合Javabean的特点;
而且getOutputProperties方法调用了newTransformer
方法,如果和CC2-4一样,调用恶意的TemplatesImpl实例的的newTransformer方法可以任意类加载并且实例化,就可以任意代码执行了
1 | public synchronized Properties getOutputProperties() { |
接着看看那里调用了getProperty
方法;
其中BeanComparator
类下的compare
方法调用了PropertyUtils.getProperty( o1, property );;而且形参property是可以通过构造函数控制,形参o1也是可控的;
然后就用CC4
和CC2
中的PriorityQueue中readObject会调用compare
方法,这样就串起来了
1 | public int compare( Object o1, Object o2 ) { |
直接执行恶意templates实例的getOutputProperties
方法
1 | TemplatesImpl templates = new TemplatesImpl(); |
pco
1 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; |