本文总结了目前windows内核攻击的各种攻击技术.描述并演示了一些常见的绕过windows内核防护的方法,并举一反三地介绍了如何通过内核缺陷找到类似的绕过方法。 通过对内核攻击和内存结构的理解将会进一步丰富基于用户模式应用程序的缓冲溢出知识。
通过大量内核漏洞的研究表明,特定的内核代码执行可能大多由用户模式的应用程序引起。因此,针对用户模式的应用程序,操作系统增加了大量的保护机制,用以保护和检测这类的攻击,比如:随机化、执行保护、内存防护等等。然而,针对操作系统溢出攻击,目前所做的工作还远远不够。 本文将讨论交流各种内核攻击技术和一些可能的内核攻击方法
0x01 环境配置
所有的演示都基于Windows 7 x86 SP1环境下编译的特意存在漏洞的驱动,本文将通过该驱动存在的漏洞来展示内核缺陷以及如果通过内核攻击技术来进行本地权限提升。
所需工具:
Windows 7操作系统
虚拟机
一个存在漏洞的驱动
Windows 内核调试器 – WinDBG
备注: 设置调试器管道名为 /./pipe/com1 ,同时被调试端也同样设置.
0x02 Windows 内核结构
在开始攻击之旅之前,我们先了解一下内核基本结构和windows进程空间的内存分配和执行方式。Windows操作系统有两个模式:内核模式和用户模式。任何一个程序都是在其中一个模式里执行。
图 1: Windows 结构 来自: logs.msdn.com
HAL: Hardware Abstraction Layer 硬件抽象层 –一组程序例程,使软件可跨平台移植; HalDispatchTable 保存着一些 HAL例程的地址。
0x03 堆栈溢出
当拷贝用户输入数据到事先分配的一个缓冲空间里时,如果没有做边界检查,就会发生栈溢出。 memcpy()函数在进行内存拷贝的时候不会做长度检查,如果拷贝过长的数据到预先定义的缓冲变量里,就会导致溢出的发生。
以下是一个用了 memcpy() 函数的程序。
图 2: StackOverflow.c
首先我们用大量的数据溢出它并且覆盖掉返回地址。 这样我们就会控制了程序后面要执行的指令。 我们用大量字符‘A’成功使栈发生了崩溃。 然而, 为了找到返回地址的精确位置,我们需要发送一组特定模式的数据来匹配到返回地址(译者注:可以是这样AAAABBBBCCCCDDDD…)。
通过溢出代码构造了一组输入数据,我们找到了返回地址的偏移如图:
图 3: 定位EIP
如上所示, EIP被 72433372 覆盖(内存地址左高位,右低位,对于字符是 72334372 ).然后定位到覆盖EIP的位置是字符串长度为2080的地方。
在我们的溢出代码里, 我们通过变量ring0_shellcode’定义了 shellcode 如下:
图 4: Shellcode
我们把shellcode地址放入我们溢出代码的缓冲区里,通过使shellcode地址覆盖返回地址,这样SHELLCODE得以执行以后,我们以用户模式启动的程序最终将以内核模式执行。
备注: 首先, 我们用Python脚本找到shellcode在内存的地址,例如:
1
ring0_shellcode_address = id(ring0_shellcode) + 20 //id(var) + 20
接着,把SHELLCODE地址放到可以覆盖返回地址的地方(上述找到的EIP偏移)。溢出代码执行后会调用SHELLCODE,这时会开启一个以系统权限执行的命令行SHELL,如图:
0x04 堆栈保护绕过
为了抵御堆栈溢出攻击,产生了一种保护机制:Stack Guard. 这种机制使得执行函数增加了两个元素:function_prologue和function_epilogue。Stack Guard 实际是通过编译时在这个两个元素处增加代码以设置和检验栈信息(保存在Canary里)。
Function prologue
图 6: _except_handler4
Function Epilogue
图8: Security Cookie效验
参照上面的程序,我们发现每次通过传统方式覆盖堆栈的时候, 我们也不得不同时覆盖掉Stack Cookie。 除非我们用正确的栈信息来覆盖Canary, 否则在函数尾的检查将失败并且程序会终止。
解决方法
我们将通过溢出覆盖异常处理函数地址的方法来绕过 Stack Cookie保护。 异常处理函数地址存放在堆栈里,我们可以任意的覆盖堆栈,当从用户空间传递大量数据到内核缓冲区的时候,我们把SHELLCODE地址覆盖掉异常处理函数的地址,这时触发异常并跳转去执行我们的SHELLCODE代码。
[1] [2] [3] 下一页