返回 TI 主页

背景

2021年10月12日,卡巴斯基公开披露了其在同年8月下旬捕获到的Windows 内核提权0 day 漏洞的相关信息,漏洞编号为CVE-2021-40449。

CVE-2021-40449 是卡巴斯基实验室于2021年8月下旬至9月上旬在Windows 服务器上捕获到相关恶意样本并进行分析后发现的在野提权0day漏洞。

该漏洞与Windows 窗口管理和图形化设备接口相关(Win32kfull.sys),可以被利用于将Windows 下较低级别权限的用户权限提升为系统权限(user->system)。截至本文完成时,已有相关POC被公开。

奇安信威胁情报中心红雨滴团队 通过对该漏洞进行详细分析,编写了可以针对大部分受影响Windows系统(Windows10 32位及64位版本)从users到system进行权限提升的Exploit程序,重现了整个攻击过程,确认了该漏洞的危害性。

由于该漏洞已经被用于真实的APT攻击,因此极有可能被利用来执行大规模的攻击并构成现实的威胁,奇安信威胁情报中心红雨滴团队对该漏洞利用的相关技术细节进行了分析,以便安全厂商可以增加相应的防护措施。

https://twitter.com/RedDrip7/status/1450743401641431048


漏洞利用演示视频

红雨滴团队复现的CVE-2021-40449漏洞利用视频如下:


分析环境

本文涉及的所有漏洞分析及Exploit代码均在 Windows 10 64位1809版本下进行。


漏洞成因

该漏洞产生的原因是win32kfull!GreResetDCInternal 函数存在缺陷:当在用户模式下执行ReserDC 函数并执行系统调用NtGdiResetDC 及其内部函数GreResetDCInternal,此函数获取指向PDC 对象的指针,但在函数内部未检查该对象是否已经被释放,导致可以对一个格式错误的PDC 对象以某种方式进行利用实现堆任意内核函数的调用。


补丁对比分析

对打补丁后的Win32kfull.sys和打补丁前通过bindiff进行对比,发现触发的漏洞函数为win32kfull!GreResetDCInternal,流程图如下:

通过对这张流程图前后对比中可以发现,补丁后关于GreResetDCInternal函数在逻辑执行流程上有一定的变化。

通过该处可知,补丁后对DCO 对象调用次数进行了一次判定,判断本次DCO对象使用次数是否大于1。

当使用次数大于1,则说明该对象已经使用过,以此确定该对象结构是否异常。

通过补丁分析和类似的漏洞分析利用经验,我们在hdcOpenDCW函数进行回调的过程中回到R3层调用ResetDC inside钩子,从而达到破坏之前设备的上下文,回调完成后,仍然会返回GreResetDCInternal函数,该函数会调用被销毁的指针,从而达到use after free。


POC - 如何触发漏洞

通过分析,整个POC流程如下图所示:

下面来简要介绍一下如何通过上图描述的执行流程来触发该漏洞。

第一步: 首先我们需要HOOK KernelCallbackTable 中的一个回调,供后面我们触发漏洞使用。

第二步: 创建一个全局的DC 对象并保存其句柄。

第三步: 直接调用ResetDC 函数,并传向其递全局DC 的句柄。ResetDC 函数执行系统调用NtDgiResetDC 及其内部函数GreResetDCInternal并取得传入的HDC 所对应的PDC对象,然后会调用hdcOpenDCW函数。

第四步: 在调用win32kfull! GreResetDCInternal 函数内部调用hdcOpenDCW 函数后,该函数会执行用户模式回调,由于此前我们已经将对应的回调HOOK,所以代码流程进入HOOK 函数。

第五步: 在回调内,再次执行ResetDC 函数并传入全局的HDC 值,在GreResetDCInternal 函数内会对该值对应的PDC 对象进行释放,造成该对象结构的错误。在完成执行后,程序回到第三步所执行到的hdcOpenDCW 的函数继续向下执行,此时,所对应的PDC 对象已经在第四步的过程中被释放掉了,但是由于未对该对象进行判断,导致异常。


漏洞详细分析

调试环境:

  • OS:Windows 10 1809 x64
  • Debugger:WinDbg Preview

通过BinDiff对更新前后Win32kfull.sys文件对比以及对poc程序的调试,我们知道触发漏洞的位置在函数win32kfull!GreResetDCInternal 内,并且查看其堆栈回溯得到以下的流程:

通过上面流程,我们大致了解到POC程序内代码执行的流程,这对于理解漏洞产生的原因有较大帮助。

回到话题,知道了漏洞触发的位置,我们可以使用IDA 工具对Win32kfull.sys 文件进行分析,并且找到漏洞函数,分析改函数内部。

第一次从hdc创建dco。

因为hook回调的原因,再次执行ResetDC函数,从hdc创建dco,并将其关联到同一个DC,次数修改为2。

通过hdcOpenDCW函数从hdcNew中创建dcoNew(并分配一个新的DC)。

进行句柄的交换操作。

通过函数DeleteDCInternal来删除dco.pdc。

当从callback回来后,我们定位到win32kfull!GreResetDCInternal + 0x1BC的位置(造成系统蓝屏的地方),发现此处是一个函数调用,call cs:__guard_dispatch_icall_fptr,在该函数内部是通过向RAX 跳转进入回调,之所以错误是RAX 值异常,而RAX 的值来源于RBX+0AB8h。

而RBX 寄存器的值则是使用由用户层传入的HDC 创建的DCOBJ对象的指针,并且这个值在执行到蓝屏触发的位置时,一直驻留在RBX 寄存器中,并且之后也没对这个对象的有效性进行校验,因为jmp rax执行了操作,但RAX为异常值,导致了不可控,从导致了蓝屏的问题。

通过终止代码号FC,可知尝试执行不可执行的内存,导致了蓝屏的问题

漏洞利用分析

通过获取TheadName从而来泄露其内核地址空间,进而调用RtlSetAllBits函数实现对Fake_RtlBitMapAddr中BitMapHeader buffer的设置。我们可以通过设置token的0x40位置的Privileges,从而给自身进程添加SE_DEBUG_PRIVILEGE 权限。

通过申请堆的大小和UAF中堆的大小相同,那么就可能申请到我们的这块内存,从而进行堆喷射,来正好获取到构造好了这块内存中的数据,最终实现指针的利用,从而达到提权的目的。

Rbx+AB8中的地址跳转已经修改为了RtlSetAllBits。

调用到RtlSetAllBits函数,通过Fake_RtlBitMapAddr将token.Privileges.Present以及Enabled都设置为了0xffffffffffffffff。

由于已经获取到SE_DEBUG_PRIVILEGE权限,注入进程即可。


总结

奇安信威胁情报中心红雨滴团队的安全研究人员编写了Windows 10 pro v1809 x64在截止2021年10月补丁日之前版本的POC,并进行了利用测试,都能提权成功。实现其它版本的利用仅仅需要更改相应结构的偏移即可,比如_EPROCESS的Token等等。


参考链接

[1]. https://securelist.com/mysterysnail-attacks-with-windows-zero-day/104509/

[2]. https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-40449

[3]. https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-nternals/how-kernel-exploits-abuse-tokens-for-privilege-escalation

[4]. https://github.com/ly4k/CallbackHell

CVE VULN