热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

浅析CVE20211647的漏洞利用技巧

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

 

概要

近期爆出的 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. 样本中其他为了实现稳定的内存布局而进行的模拟调用的特征。

 

总结

整个样本的利用过程,可以用精妙绝伦来形容。可以看出,漏洞作者对于 defender 模拟执行的过程已经研究的十分深入。因此,无论是从漏洞挖掘还是从漏洞利用的角度来说,该样本以及 Windows Defender 都还有非常多的地方值得笔者继续深入分析探索,也欢迎有兴趣的读者一同交流探讨。

关于微步情报局

微步情报局,即微步在线研究响应团队,负责微步在线安全分析与安全服务业务,主要研究内容包括威胁情报自动化研发、高级 APT 组织&黑产研究与追踪、恶意代码与自动化分析技术、重大事件应急响应等。


推荐阅读
  • 本文总结了一些开发中常见的问题及其解决方案,包括特性过滤器的使用、NuGet程序集版本冲突、线程存储、溢出检查、ThreadPool的最大线程数设置、Redis使用中的问题以及Task.Result和Task.GetAwaiter().GetResult()的区别。 ... [详细]
  • 高端存储技术演进与趋势
    本文探讨了高端存储技术的发展趋势,包括松耦合架构、虚拟化、高性能、高安全性和智能化等方面。同时,分析了全闪存阵列和中端存储集群对高端存储市场的冲击,以及高端存储在不同应用场景中的发展趋势。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • Java 中的等时日期(int,int)方法,示例 ... [详细]
  • 使用HTML和JavaScript实现视频截图功能
    本文介绍了如何利用HTML和JavaScript实现从远程MP4、本地摄像头及本地上传的MP4文件中截取视频帧,并展示了具体的实现步骤和示例代码。 ... [详细]
  • PHP 5.5.31 和 PHP 5.6.17 安全更新发布
    PHP 5.5.31 和 PHP 5.6.17 已正式发布,主要包含多个安全修复。强烈建议所有用户尽快升级至最新版本以确保系统安全。 ... [详细]
  • 使用方法:将要控制的角色拖到TargetBody,将相机的焦点拖到CamerPivot,,建议CameraPivot是一个放在TargetBody下的子物体,并且位置应该是在Tar ... [详细]
  • 目录预备知识导包构建数据集神经网络结构训练测试精度可视化计算模型精度损失可视化输出网络结构信息训练神经网络定义参数载入数据载入神经网络结构、损失及优化训练及测试损失、精度可视化qu ... [详细]
  • VB.net 进程通信中FindWindow、FindWindowEX、SendMessage函数的理解
    目录一、代码背景二、主要工具三、函数解析1、FindWindow:2、FindWindowEx:3、SendMessage: ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • ARM汇编基础基于Keil创建STM32汇编程序的编写
    文章目录一、新建项目(1)工具介绍(2)创建项目:二、配置环境(1)配置芯片&#x ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
author-avatar
速度coinmer
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有