千家信息网

怎么浅析CVE-2021-1647的漏洞利用技巧

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这期内容当中小编将会给大家带来有关怎么浅析CVE-2021-1647的漏洞利用技巧,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。概要近期爆出的 CVE-2021-1
千家信息网最后更新 2026年11月01日怎么浅析CVE-2021-1647的漏洞利用技巧

这期内容当中小编将会给大家带来有关怎么浅析CVE-2021-1647的漏洞利用技巧,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

概要

近期爆出的 CVE-2021-1647 是 Windows Defender MpEngine 模块中的一处 rce 漏洞,下面旨在从漏洞利用层面对相关样本所使用的技巧进行分析,笔者使用的样本哈希为:6e1e9fa0334d8f1f5d0e3a160ba65441f0656d1f1c99f8a9f1ae4b1b1bf7d788。

漏洞原理

Windows Defender 采用模拟执行的策略,对可执行文件进行黑白判定,模拟执行分为以下两个层面:指令模拟和运行环境模拟。指令模拟部分负责将相应平台指令(包括 arm/x86/x64 等)转为中间指令(IL),然后通过 jit/ 解释执行的方式运行相应的中间指令;运行环境模拟则包括内存系统模拟/文件系统模拟/api 模拟/DLL 模拟(与 MpEngine.dll 同目录的 vdm 文件是 dll 模拟文件的压缩集/合),在指令模拟执行的过程中,碰到压缩壳的情况,defender 也会模拟解压过程(详见 UnpackerContext::Unpack 函数),当前 defender 支持的压缩方式有:Upxw64/Upxw/WExtract/NSPacker/Shrinker/PECompact2/Area51/Crypter1337/Aspack/PKLite/SfxCab/Asprotect 等。本次漏洞出现在 Asprotect 模拟解压过程中(CAsprotectDLLAndVersion::RetrieveVersionInfoAndCreateObjects 函数):

上图中,v2+28 代表一个 section 数组的开始,该数组包含四个元素,每个 section 元素 8 字节,前 4 字节用于描述该 section 的虚拟地址,后四个字节用于描述该 section 的大小,上图描述的是这样一个过程:遍历一个包含 4 项的 section 数组,最后获得一个 sectionva 和 sectionsize,申请一片大小为 sectionva+sectionsize 的内存用于存储解压后的 section 内容,但其在计算 sectionva 和 sectionsize 时存在错误,代码中只考虑了 section[i+1].va>section[i].va 的情况,但并没有考虑两者相等的情况,倘若 section 数组中四个元素的值如下:[0,0],[0,0],[0x2000,0],[0x2000,0x3000],按照上述代码的逻辑最终 sectionva=0x2000,sectionsize=0,那最终申请的内存大小为 0x2000+0=0x2000,因此在解压最后一个 section 时,由于其大小为 0x3000,这样便会产生堆溢出问题,这便是本漏洞的产生根源。

利用技巧

1. 确定版本偏移

在样本开头处调用了如下函数:

在刚开始分析该样本时,笔者以为该处地址是 defender 中某个未开启 ASLR 模块的地址或是类似于利用异常处理进行反调试的措施,但其实这时笔者犯了一个巨大的错误,要明确的一点是,当样本在被 defender 模拟执行时,一切内存地址并不是真实的 host 上的地址,而是 defender 模拟内存空间中的一个地址,也就是说 0x7c96c654 其实是 defender 模拟内存空间中的一段地址,该地址其实对应于模拟 dll 模块 --ntdll.dll(模拟的 ntdll.dll 可以通过解压与 mpengine.dll 同目录的 mpasbase.vdm 获得)中的一段代码:

注意看函数最后两个字节:0xf 0xff,这两个字节说明这是一个 native api 调用,其后的四个字节 0x9E9EFDF0 是用于标识最终 native api 函数的一个 crc 校验码,所谓的 native api 即是有 mpengine.dll 提供的一系列功能 api,最终该函数会由 mpengine!NTDLL_DLL_NtControlChannel 实现,也就是说样本中 call 7C96C654 其实最终是调用 mpengine!NTDLL_DLL_NtControlChannel 完成功能,该函数第一个参数代表功能号,样本中的 3 代表的是获取 defender 版本信息,样本就是通过该函数获取版本信息,然后根据不同的版本信息硬编码关键偏移。

2. 内存占位/修改关键字段

在样本中包含了大量的 SuspendThread 和 ResumeThread 的调用代码,这部分代码其实是用来进行内存布局和占位的,在堆溢出发生后,会修改布局在堆内存后的 lfind 对象,lfind 对象中的两个关键字段分别被修改为 2f9b 和 2f9c(原始值为 107e 和 107f),这两个字段在 lfind_switch::switch_in(在模拟执行 ResumeThread 函数时会触发该函数调用)函数中被引用:

上图中的 v20 即是会被引用的 2f9b 和 2f9c,很明显由于被修改之后的值比正常的大,这会造成一个越界写的行为,上图中的 *(v20+*(v16+144))|=3 是漏洞利用过程的关键,该部分代码修改的是 vmmcontrol 中的一个关键字段,我们将在第三部分说明这个字段的用途。

3. 获取任意读写能力

在 defender 进行内存空间模拟的过程中涉及到这样几个比较重要的结构:

该结构用于维持模拟内存地址和真实内存地址之间的映射关系,该结构在内存中以数组的形式存在。

为了实现高效的地址转换,defender 还引入了一个索引数组(每个索引用 2 字节存储),该数组中存储的是 EmuVaddrNode 结构数组的索引,并且是按照 EmuPagenum 从小到大进行排序的,也就是说假设此时 EmuVaddrNode 数组中包含三个元素,且三个元素的 EmuPageNum 字段内容为 0x2000,0x1000,0x5000,那么正常情况下此时索引数组内容为1,0,3(1、0、3 都代表 EmuVaddrNode 数组的索引),并且 EmuVaddrNode 数组和索引数组在真实内存中的布局如下(假设从左到右代表地址从低到高):

也就是说,索引数组后头就是 EmuVaddrNode 数组,EmuVaddrNode 数组紧跟着的是 Page,Page 代表的是 defender 申请的用于映射模拟内存空间的真实内存,即 EmuVaddrNode 中的 Vaddr 都是从 page 中切割出来的。

最后,我们回顾一下第二部分中提到的修改了 vmmcontrol 中的关键字段,这个关键字段描述的是索引数组一共有多少个元素。如果我们将该数值改的尽可能的大,使得索引数组的起始地址+索引数组数目*2>Page 地址,而 Page 中的内容是我们可控的,我们通过让 defender 模拟执行 *p=value 的方式,就能在 Page 中布置我们想要的内容。首先,我们在 Page 中伪造索引数组,并且该索引远大于 EmuVaddrNode 数组的项数,那么当 defender 模拟执行 *p=value 这类指令时,先是从我们在 Page 中伪造的索引数组中取到一个伪造的索引(该索引远大于 EmuVaddrNode 数组的项数);接着,我们在 Page 中继续伪造一个 EmuVaddrNode 结构,那么 defender 通过伪造的索引便会访问到我们在 Page 中伪造的 EmuVaddrNode 结构,这时 EmuVaddrNode 结构的 Vaddr 是我们可控的,那么我们便获得了任意地址读写能力,"写"通过让 defender 模拟执行 *p=value 来实现,"读"通过让 dfender 模拟执行 value=*p 来实现。有浏览器或者内核漏洞相关经验的同事应该很熟悉这种场景,通过修改长度字段来伪造对象及其 pointer 字段最终获得任意地址读写的能力。

4. 获取代码执行能力

在 defender 中会将常用的代码片段进行 jit 处理,通常 jit 内存中存放的是 prolog 和 epilog 片段,在取得任意地址读写能力后,样本中先将通过硬编码偏移获取到了 jit 部分的真实地址,将某个 EmuVaddrNode 的 vaddr 设置为 jit 的真实地址,并且利用模拟执行 memcpy(EmuPageNum,shellcode, sizeof (shellocde))向 jit 地址写入了 shellcode,最终只要 jit 功能一使用便会执行 shellcode。

检测原理

由于样本是在 defender 模拟执行的过程中触发的漏洞,那么,如果我们仅仅简单取样本中的几个特征字串作为匹配规则,显然是十分容易被绕过的。笔者建议可以综合以下几方面信息作为特征:

1. asprotect 壳的特征

2. 上图所示内容是 MpEngine 中对解压后的内容做的一个校验,只有满足条件才会触发堆溢出操作,因此可以对类似于 "*(memory)=0x8d; *(memory+1)=0x85; *(memory+6)=0x50; *(memory+7)=0xc3" 的赋值操作进行特征匹配。

3. 样本通过 NtControlChannel 获得版本信息以确定偏移信息,因此可以把 NtControlChannel 函数的调用特征作为匹配依据。

4. 样本中其他为了实现稳定的内存布局而进行的模拟调用的特征。

上述就是小编为大家分享的怎么浅析CVE-2021-1647的漏洞利用技巧了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

数组 地址 内存 索引 样本 函数 漏洞 字段 内容 代码 关键 字节 指令 结构 过程 代表 信息 元素 特征 上图 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技企业 互联网科技公司简介 科技金融是不是互联网金融 鸠鸠互联网科技 长亮科技 互联网金融 互联网科技宣传片 互联网金融科技委员会 华中科技有哪些互联网大佬 深圳国际航运科技和互联网 佳云科技和互联网广告 福建科技产业互联网发展 互联网科技创新的路径 玉溪新华互联网科技好不好 银行科技岗属于互联网吗 互联网硬科技公司 互联网高科技行业有哪些企业 互联网科技手抄报图片 互联网领先科技活动 北京天晋互联网科技有限公司 广东猫王互联网科技有限公司电话 互联网金融 科技金融 深圳天众互联网科技公司 厦门比较好的科技互联网公司 上海京穆互联网科技有限公司 互联网第三次科技革命特征 电子商务与互联网科技的关系 互联网科技机房 西安左小互联网科技有限公司 易租星互联网科技有限公司 互联网科技创业计划的风险分析
0