在本文中,我们将以CVE-2020-17382漏洞为例来介绍内核漏洞是如何被武器化的。
在上一篇文章中,我们首先解释了攻击者为什么会以驱动程序为攻击目标,然后,分析了驱动程序的组成部分,最后,讲解了驱动程序的逆向分析和调试方法。在本文中,我们将以CVE-2020-17382漏洞为例为读者介绍内核漏洞的利用过程。
攻击MSIO64.sys
现在,我们知道了如何在Ring0中获得指令指针的控制权,接下来,我们将为读者演示如何开发不同的POC。我们将要使用的shellcode是一个标准的Token Stealing,它的目的是通过窃取SYSTEM进程的token来提升当前(或另一个)进程的权限。正如我所说过的,本文不会对Windows 7进行太多的讨论,但尽管如此,它还是帮我认识到了内核模式中进程恢复是多么的至关重要。然后,在讨论进入Windows 10平台之前,我还是要简单聊一聊这个话题。
Windows 7
在Windows 7平台上玩弄了一段时间这个漏洞的利用代码之后,我终于让shellcode正常运行了起来,但问题是:我始终无法找到一种稳定的方法来恢复原来的执行流并释放调用堆栈的其余部分,而每一次尝试都会导致残酷的Bug Check。
然后,我们继续提出了几个“what if”式的问题。如果我们能找到一种方法,它能在冻结内核线程的同时提升另一个进程的权限,那会怎么样呢?问题是,我们不能让这个进程在内核中直接崩溃,否则整个系统都会挂掉。我们必须找到一种方法,要么恢复执行,要么让这个线程无害化。
然后,我开始调整shellcode/exploit,让它把偷来的SYSTEM令牌复制到任意进程,比如另一个打开的cmd提示符,它可以作为命令行参数传递。这样一来,我们就可以不那么关心初始进程的命运了,因为当bug check发生时,我们已经提升了目标进程的权限。但是,我们如何防止BSOD呢?突然,我想起Matt Miller写了一篇关于Ring0 payload的文章,仔细拜读之后,很快就得到了回报。在众多的payload中,我获得了一个相当令人惊讶的发现:我发现的解决方案仅由两个字节组成,即\xEB\xFE ,或JMP 0x0。这一招很老套,并且使用范围不限于内核shellcode。在提升目标进程权限后,通过在shellcode的末尾附加这些指令,我们就能让原来的线程永远运行下去,这样就不会把系统弄崩溃了。当然,这绝对算不上一个优雅的解决方案,但它的确是行之有效的,至少在Windows 7上是如此(由于Windows 10提供了更强大的检测机制,最终会检测到这个浪费资源的内核线程,并引发bug check)。
为了将这个漏洞武器化,我们只需要在用户空间的缓冲区中为实现令牌窃取的shellcode分配空间即可,由于Windows 7基本没有提供内核防御措施,我们的exploit只需要用shellcode的指针覆盖返回地址即可。
下面给出了该exploit的运行效果:
Windows 10 1709
绕过SMEP和其他内核漏洞利用缓解措施
与Windows7相比,Windows 10采用了几种内核级别的漏洞利用缓解措施,例如:
· 内核模式代码签名(KMCS)
· 管理模式执行保护(SMEP)
· 内核地址空间布局随机化(KASLR)
· 内核补丁保护(KPP,也称为Patch Guard)
· 控制流防护(CFG)
· 基于虚拟化的安全性(VBS)
除了最新的两项(CFG和VBS)防御措施外,我们还将研究如何绕过其他四项缓解措施,其中SMEP似乎是嘴硬的一块骨头。
在对最新、最出色的Windows 10版本进行测试之前,我首先决定在1709(也称为Redstone 3)版本上进行尝试,因为人们已经对其缓解措施和绕过技术进行了充分的研究。
对于这些缓解措施,我们将逐一进行阐述,同时,我们也会给出相应的绕过方法。
KMCS
好吧,由于驱动程序已经被签名了,所以,实际上并没有真正的绕过方法。但是,需要指出的是,阻止加载未签名的驱动程序只是阻止“普通”恶意驱动程序的一道屏障,但无法组织攻击者加载已经签名但是含有安全漏洞的驱动程序。
SMEP
目前,已经有很多关于SMEP的文章和文档,所以,我们可以把它概括为一种缓解措施,目的是阻止用户模式shellcode在内核模式上下文中执行。在典型的内核攻击场景中,一旦通过Ring0漏洞(即在驱动程序中的漏洞)获得指令指针的控制权,就可以轻松地跳转到用户模式shellcode(如上面的令牌窃取代码),而不必创建复杂的内核模式payload了。
SMEP是通过CR4控制寄存器的第20位实现的,每当从内核上下文执行驻留在用户模式下的代码时,都会对其进行检查。
如果出现这种情况,就会同时触发错误检查0x000000fc会和“ATTEMPTED TO EXECUTE NOEXECUTE MEMORY”,以阻止任何利用漏洞的尝试。对于这种缓解措施,实际上有一种克星,那就是Economou/Nissim发明的绕过技术;在本文中,我们将继续沿用这种绕过技术。
我们的计划是在跳转到我们的shellcode之前,使用ntoskrnl的一些ROP Gadget来禁用SMEP位。事实上,我们只需要两个gadget就能达到我们的目的:一个gadget用于将所需的CR4值加载到通用寄存器中,第二个gadget负责将这个值加载到cr4寄存器本身。
pop rcx ; ret # store the desired value into rcx [target cr4 value] mov cr4, ecx ; ret # load the new value into cr4
当您动手实验时,原始cr4值可能会有所不同,具体取决于您使用的主机系统/管理程序以及支持的CPU功能。在我们的例子中,这个值为1506f8:
0: kd> .formats cr4 Evaluate expression: Hex: 00000000`001506f8 Decimal: 1378040 Octal: 0000000000000005203370 Binary: 00000000 00000000 00000000 00000000 00000000 00010101 00000110 11111000 Chars: ........ Time: Fri Jan 16 23:47:20 1970 Float: low 1.93105e-039 high 0 Double: 6.80842e-318
为了获得可以绕过SMEP的寄存器值,我们只需要翻转第20位,也就是将这个二进制位的值设为1。然后,得到的值就变成了506f8。接下来,我们需要寻找所需的ROP gadget,为此,我们可以借助于rp++从当前测试的ntoskrnl.exe中找到它们。这样,我们就大功告成了。您可能会问:绕过SMEP有那么容易吗?答案是肯定的,至少在Redstone 3上是这样的。
KASLR
从一个中等完整性的进程(如非特权用户的命令shell)中,我们可以通过Psapi提供的EnumDeviceDrivers API来获取nt的内核基地址。我们需要这个基地址来计算绕过SMEP所需的ROP Gadget的地址。当然,这算不上真正的绕过,因为KASLR的目的主要是保护低完整性的进程,比如浏览器:在这种情况下,就无法从这种完整性的进程中查询Psapi或NtQuerySystemInformation。尽管如此,在像我们这样的标准本地EoP场景中,KASLR是可以被忽略的。
KPP
当内核补丁保护(Kernel Patch Protection,又称Patch Guard)检测到关键的内核结构体被篡改时,它就会向我们发出警告,然后就会蓝屏。所以,是的,由于我们正在对CR4寄存器进行乱来,所以,很可能会触发KPP防御机制。然而,我们知道KPP是随机触发的,所以,如果我们足够快地恢复CR4的原始值,我们就可以避免出现蓝屏现象。
好了,说的已经够多了,下面看看完整的exploit的运行情况:
Windows 10 2004版本——未来的新障碍?
作为最后的练习,我想把这个exploit移植到最新的官方Windows 10版本中,根据2020年9月的情况,应该是2004版本。起初,我以为只需要重新计算gadget的偏移量就能轻松搞定了,但是事情远没有这么简单:在没有启用VBS的情况下,我遇到了0xfc错误检查,尽管当时还没有修改CR4第20位的值。相当有趣的是,我发现它可以在其他CPU/Hypervisor上正常工作。也就是说,这个exploit有时能正常工作,有时候却不行,这主要取决于运行环境中的hypervisor或硬件设置,不过,我决定不在这件事上花费更多的时间了。不过,欢迎大家把实验结果反馈给我。
Update 29.09.2020版本
后来,受到Alex Ionescu启发,事情才开始走上正轨:他指出,我在2004版本上遇到的情况,是因为微软在2018年3月推出的Meltdown KVA Shadow缓解措施所致。随后,我迅速拜读了Blue Frost Security团队的这篇优秀文章,才发现我从一开始就被SMEP盯上了。微软利用该缓解措施实现的两个PML4,如果从内核上下文调用的话,则将用户模式PML4设置为Non-Executable。这个缓解措施也被称为软件型SMEP,因为它是由操作系统的内存分页结构(PML4)而不是CPU组件(CR4寄存器)来执行的,具有讽刺意味的是,我竟然设法绕过了WinDBG的这个缓解措施,尽管不知道它背后的技术实现和动机:通过比较1709版本的PTE条目和2004版本的PTE条目,我注意到它们是相同的,只有PML4标志(WinDBG术语中的PXE)例外。下面展示的是2004版本的用户模式shellcode的内存页:
1: kd> !pte 000001f3fe2a0000 VA 000001f3fe2a0000 PXE at FFFFF57ABD5EA018 PPE at FFFFF57ABD403E78 PDE at FFFFF57A807CFF88 PTE at FFFFF500F9FF1500 contains 8A00000012F1B867 contains 0A00000057D9C867 contains 0A0000000E01D867 contains 0100000017504847 pfn 12f1b ---DA--UW-V pfn 57d9c ---DA--UWEV pfn e01d ---DA--UWEV pfn 17504 ---D---UWEV
而且我们可以从PXE/PML4E中看到缺失的Executable标志,这意味着NX位被设置为1。Meltdown的缓解措施旨在保护任何低度或中度完整性进程,这就解释了为什么从Administrator shell中可以成功运行该exploit了。然后,我编写了一个粗糙的PoC(它是建立在Blue Frost Security提供的示例代码的基础上的),通过利用Meltdown漏洞泄露PML4 VA并清除PML4E上的NX标志,成功绕过了基于硬件和软件的SMEP。大家不要着急,这个PoC很快就会上线了(感谢Rui,他是一位伟大的陪练)。
同时,我们可以列出绕过Software SMEP所需的步骤,其中包括禁用通过shellcode用户模式PML4E设置的NX位:
· 通过Meltdown漏洞泄露PML4 VA,详情见Blue Frost Security团队的相关文章。
· 获取PML4之后,我们就可以通过下面的公式计算出我们的shellcode PML4E VA偏移量:
INT64 getPML4EfromVA(INT64 ua_va) { int pml4e_offset = ((ua_va >> 39) & 0x1ff) * 8; return pml4e_offset;
这个偏移值必须加上之前得到的PML4基地址。
· 最后一步是通过类似于下面的gadget扩展ROP链,以禁用shellcode PML4条目上的NX位:
pop rcx; ret; $hellcode_PML4E pop rdx ; ret 0x0FFFFFFFFFFFFFF and qword [rcx], rdx ; ret
· 通过在加载到RCX中的PML4条目和掩码0x0FFFFFFFFFFFFFFFF之间执行ANDing运算,我们最终将包括NX位在内的四个最高有效位全部清零:
值得注意的是,Meltdown的缓解措施只是暂时的,因为该漏洞不会影响最新的CPU。作为一个快速测试,通过使用Alex Ionescu的SpecuCheck工具,我们可以比较两个相同的Windows 10版本在不同的CPU上运行情况:
受到Meltdown漏洞的影响
不受Meltdown漏洞的影响
我们可以在最后边的机器上注意到,Kernel VA Shadowing被标记为非必要:也就是说,一旦在启动时检测到CPU并将其标记为不易受到Meltdown漏洞攻击,就不会启用KVA缓解功能,也不会运行任何软件型SMEP,这就意味着,只要最新版本CPU版本大规模部署,我们就只需绕过硬件型SMEP防御机制了。
小结
事实证明,WHQL机制无法保证相关的驱动程序中不含安全漏洞:正如Eclypsium所声称的那样,现实中存在大量未打补丁的驱动程序,它们多得令人瞠目结舌。从好的一面来看,HVCI和VBS可以阻止所有此类攻击,因为它们能够检测到寄存器级别的任何篡改,例如,篡改CR4控制寄存器。而从最新的Windows 10 20H1版本开始,VMWare开始提供基于虚拟化的安全支持:我们可以预测,由于VBS将通过检测任何实时变化的CR4位来阻止任何禁用SMEP的企图,因此,攻击面将很快变得更小。
参考文献
在此,我们诚挚感谢所有在这些实验过程中给予我启发和提供宝贵支持的朋友,特别是Rui和h0mbre。
更多信息,可以访问下面的链接:
https://eclypsium.com/2019/08/10/screwed-drivers-signed-sealed-delivered/
https://eclypsium.com/2019/11/12/mother-of-all-drivers/
https://www.microsoft.com/security/blog/2017/03/27/detecting-and-mitigating-elevation-of-privilege-exploit-for-cve-2017-0005/?source=mmpc
https://www.coresecurity.com/sites/default/files/private-files/publications/2016/05/Windows%20SMEP%20bypass%20U%3DS.pdf
http://lallouslab.net/2016/01/11/introduction-to-writing-x64-assembly-in-visual-studio/
https://posts.specterops.io/methodology-for-static-reverse-engineering-of-windows-kernel-drivers-3115b2efed83
https://sizzop.github.io/2016/07/07/kernel-hacking-with-hevd-part-3.html
http://www.uninformed.org/?v=3&a=4&t=sumry
https://h0mbre.github.io/atillk64_exploit/#
http://wrogn.com/tag/driver-signing/
本文翻译自:https://www.matteomalvica.com/blog/2020/09/24/weaponizing-cve-2020-17382/如若转载,请注明原文地址: