- A+
在微软发布MS14-058补丁后,文章中描述的漏洞利用工具流出。在写这篇文章之前,已经有很多对该工具的分析及此漏洞的MSF利用模块,目前利用工具支持除了Win8以外的所有32位和64位版本Windows。
所以我很好奇漏洞是否能被利用在最新版本的Windows。本文介绍了我的分析结果和演示如何成功利用在Win8和Win8.1。
以下的分析是基于在外流传的Win64.exe和已公开的信息。
该漏洞已经在其他地方有详细描述,所以我们只关注相关的细节。
所以Windows8.1这个调用指令不再能被控制。然而,在下一节中我们将看到一个精心构造的win32k!tagWND地址结构,仍然可以被用来成功地利用这个安全漏洞。
要利用这一漏洞我们要在Win8.1用户模式下申请一个设计好的win32k!tagWND地址结构。当漏洞被触发时,win32k!xxxSendTransformableMessageTimeout函数首先在win32k!tagWND地址偏移0×10处读取一个64位的值并和win32k!gptiCurrentkernel指针对比,如果在这个偏移地址提供了一个不支持的值则产生异常。接下来程序从偏移量0处读取一个WORD并用它作为一个内存索引.被这个索引提到的byte会被和0×01对比。
如果我们把win32k!tagWND结构体的第一个DWORD设为0,,就能让检查失败,并让程序调用win32k!xxxInterSendMessageEx并以我们构造好的win32k!tagWND结构体地址作为第一个参数。
win32k!xxxInterSendMessageEx函数会再次读取win32k!tagWND结构体中偏移0×10处的指针,并尝试解引用这个指针来读取新的指针,这个新的指针用来读取偏移0×170处的值,并和ntoskrnl!PsGetCurrentProcessWin32Process返回的值做对比。
构造的win32k!tagWND结构体在双重解引用成功后会在用户模式内存读出值0,然后win32k!xxxInterSendMessageEx会从0x2b0读取一个字节,这个值可以是任意的,除了0×20。
在所有条件都满足后,win32k!xxxInterSendMessageEx最终会调用win32k!IsWindowDesktopComposed通过指针传递我们构造的win32k!tagWND结构体作为一个参数。
win32k!IsWindowDesktopComposed会从构造的win32k!tagWND结构体中读取偏移0×18处的值,如果读出的值为0,函数会返回0,不解引用这个结构的任何成员。
如果所有条件都能达到,win32k!xxxInterSendMessageEx最终代码就会类似下面:
这段代码执行链表插入动作试图将在RDI寄存器中找到的值插入链表。这个寄存器的值是一个我们没法直接控制的内核地址。这段代码一开始会读取win32k!tagWND结构体偏移量为0×60处的链表头并检查是否为空,如果为空则这个值被存在win32k!tagWND结构体中作为新的链表头。
如果win32k!tagWND结构体在偏移0×60处已经存储了一个链表头这段代码会开始遍历链表直到发现尾指针为空并用RDI寄存器中的内存地址覆盖这个指针。这段代码能让我们把任意地址的8个空byte用一个内核地址(RDI寄存器中的值)来覆盖。虽然我们无法控制内核地址但是这已经足够。
在内核内存中能覆盖的东西很多,我们需要的是一个以64位值的0开始并且覆盖后能发到提权效果的内存位置并且我们要能够在用户模式中获取这个内存位置的地址。
我选择了CesarCerrudo在他的论文“EasylocalWindowsKernelexploitation”中提到的技术,利用NtQuerySystemInformation(SystemHandleInformation)API来获取Windows令牌对象的地址。这让我们能够让我们获得嵌入令牌的SEP_TOKEN_PRIVILEDGES结构体的地址我的想法是通过覆盖这个在主令牌中的结构体。然后添加新的权限来提权。这样的好处是可以无视SMEP。
所以现在来看看SEP_TOKEN_PRIVILEDGES这个结构体。
SEP_TOKEN_PRIVILEDGES结构体中前三个区域代表位掩码,每个权限由一个位代表。我们感兴趣的是其中一些我们能被内核允许的高权限。
结构体中8个NUL可能凑不够,可以通过尽量减小权限来凑满。我们打开进程主令牌的句柄然后创建一个最小权限的令牌。
结果如下。
发现还是没办法凑齐8个NUL。于是我用带有DisableAllPrivilegesflag参数的AdjustTokenPrivilegesAPI函数,来禁用所有可能的权限。
可以看到,调用这个函数成功将64位区域清零了。
现在可以覆盖那个区域了,但是依然无法控制被写上去的内核地址。
我们只能保证这个内核地址有两个字节是0xff。但是最有用的特权在Enabledfield的前几位,所以不能直接写,而是通过写PresentField位掩码来保证覆盖到。这样权限就能够得以提升。
为成功利用需要用ZwAllocateVirtualMemory(地址0xfffffffb)在用户模式伪造一个Win32k!tahWND结构体,把这结构体内容按3.1说的填好。
下一步创建一个所有权限都被从Enabledfield移除的令牌,并用NtQuerySystemInformation(SystemHandleInformation)API把这个令牌的内核地址泄露给SEP_TOKEN_PRIVILEGES结构体,为了能指向Presentfield的中间我们给地址增加3,并把加后的地址(减8)存放在构造的win32k!tagWND结构体偏移0×60处。
为了获得新权限我们用ImpersonateLoggedOnUserAPI来模拟这个令牌的安全环境并利用特权,然后用WriteOrocessMemoryAPI将shellcode插入一个以System权限运行的进程(WinLogon.exe)并用CreateRemoteThread来执行他,最终成功获取System权限。
对比原文略有删减
- 我的微信
- 这是我的微信扫一扫
- 我的微信公众号
- 我的微信公众号扫一扫