本文讲的是HEVD 内核攻击:漏洞攻击的完全实现及SMEP绕过(四),
到目前为止,我们还没有完全实现漏洞的攻击。让我们回顾一下第2篇里为DoS PoC创建的漏洞利用步骤,现在我们可以修改其中一些步骤,来实现SYSTEM shell。
第一步,生成cmd.exe进程
虽然这一步非常简单,但是要在Python中实现,还是需要一些额外的代码,与C语言代码相比,我们将使用Kernel32.dll中的CreateProcess API来启动shell。查看函数原型,该函数需要为调用设置两个结构体,其中一个将使用我们启动的cmd.exe进程的PID返回给我们。稍后我们需要使用shellcode来设置我们的结构体。
第一个是STARTUPINFO结构体(STARTUPINFO是用于指定新进程的主窗口特性的一个结构)。使用Python ctypes可以在我们的脚本中重新创建STARTUPINFO结构体,如下所示:
我们可以在脚本中引用这个结构体,如下所示:
我们需要的是一个PROCESS_INFORMATION结构,因为这个结构更易于管理,在ctypes中看起来是这样:
这里包含了dwProcessId dword中创建的进程PID。通过创建这两个结构,我们可以参考CreateProcess函数原型并将我们的API调用放在一起:
第二步和第三步和第2篇里的过程一模一样,大家可以爬楼。
第四步,使用shellcode分配缓冲区
首先将shellcode翻译成Python。这也包括将cmd.exe进程的PID动态插入到shellcode中,这样我们就创建了一个可以接收我们需要的PID并创建shellcode的函数:
利用VirtualAlloc函数复制我们的shellcode区域,当然前提是确保指定的分配缓冲区是可执行的。假设一切顺利,我们就可以将shellcode复制到缓冲区(ctypes提供一个memmove()函数),然后返回shellcode所在的地址:
第五步,创建shellcode的缓冲区
这一步又与第2章提到的DoS PoC非常相似。不过,这一次,我们的函数还需要收到shellcode所在的分配地址,以便我们可以将它添加到我们的缓冲区。我们的DoS PoC缓冲区由2048“A”组成,其后是8“B”和8“C”。 “C”是在rip寄存器中结束的,所以我们要用我们的shellcode地址来替换它:
第六步,触发漏洞
首先生成cmd.exe并获取它的PID,然后分配shellcode并将该地址插入到我们创建的缓冲区。
如果一切顺利的话,会出现以下的情景:
至此,漏洞就可以完全实现了。不过,我们在实际的测试中发现,这些漏洞攻击只是在Windows 8以前的系统版本中运行的很好,由于Windows 8及更高版本上已经配置了一些新的缓解措施,所以这些漏洞利用就会被禁止。
主管模式执行保护(SMEP)有点像ring 0模式下的DEP,是内核的一种缓解措施,我们可以将它简单的理解成禁止在内核态下执行用户空间的代码。换句话说,当以内核模式运行时,处理器将不会运行映射到用户模式存储器的指令。
在Windows 7版本中,我们是将shellcode复制到内存中。一旦我们劫持了内核中的执行流程,我们就会将其指向带有shellcode的用户内存缓冲区,但如果我们在启用了SMEP的系统上也这么做,那么一旦处理器试图执行用户内存缓冲区的任何指令,则运行就会中断。
正如开始的时候我说SMEP有点像DEP,绕过DEP比较常见的方法就是ROP链,因此绕过SMEP的常见方法也是比较简单的方法也是ROP链,在DEP中构造ROP链需要dll的基址,其实在SMEP中构造ROP链需要动态链接库内核的地址,而获取内核地址的一种非常好用的方法就是NtQuerySystemInformation。
那么我们如何绕过SMEP呢?最简单的途径就是简化CR4寄存器,以便让Windows认为是处理器不支持SMEP。这与我们的Windows 7 HEVD堆栈溢出漏洞大致相同,但在我们触发溢出之前,我们必须首先执行ROP链以禁用SMEP。为了构建我们的ROP链,我们必须找到内核的基址,总共分8步:
1.Spawn cmd.exe进程 2.使用shellcode分配缓冲区 3.获取内核基地址 4.构建ROP链 5.获取容易受到攻击的设备句柄 6.获取正确的IOCTL的堆栈溢出功能 7.创建一个将执行重定向到ROP链及shellcode的缓冲区 8.触发漏洞
以上只有第3步和第4步是新步骤,所以我们就直接来叙述这两个步骤,其余的步骤,请参看前文。
第三步,获取内核基址
就像绕过DEP比较常见的方法就是ROP链一样,我们可以使用ASLR,使用ROP来破坏内核中的SMEP可能会受到KASLR的阻碍。当然有各种方法来击败KASLR,但这不在本文的讨论范围之内。
简单来说,利用psapi.dll的EnumDeviceDrivers功能就可以实现这一目标,EnumDeviceDrivers可以检索系统中每个设备驱动程序的加载地址,其中包括返回结果中的实际内核:
第四步,建立ROP链
使用这一步的前提是,假设大家已经很熟悉ROP技术了。
现在我们已经得到了内核基地址,接下来我们将继续构建绕过SMEP的ROP链。这不是很难,因为这是一个非常小的ROP链,只需要把ntoskrnl.exe解析为几个字节数组。
我们的目的就是将控制值弹出到CR4寄存器中。在搜索到可用的ROP后,我们发现没有很多与CR4进行交互的指令,这时 ntoskrnl.exe中的KiFlushCurrentTbWorker函数实际上可以被利用起来:
接下来,我们只需要在内核的地址空间中找到POP RCX就可以了。最后,我们可以构建一个函数,该函数采用内核的基地址和shellcode的用户地址,将CR4值放在该函数中,最后在shellcode缓冲区得到以下代码:
至此,在Windows 8及更高版本上我们也实现了最终的漏洞利用:
原文发布时间为:2017年3月30日
本文作者:xiaohui
本文来自合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。