SEHOP原理浅析
前言
SEHOP的全称是Structured Exception Handler Overwrite Protection(结构化异常处理覆盖保护),SEH攻击是指通过栈溢出或者其他漏洞,使用精心构造的数据覆盖结构化异常处理链表上面的某个节点或者多个节点,从而控制EIP(控制程序执行流程)。而SEHOP则是是微软针对这种攻击提出的一种安全防护方案。
微软最开始提供这个功能是在09年,支持的系统包括Windows Vista Service Pack 1、 Windows 7、Windows Server 2008 和 Windows Server 2008 R2,以及它们的后续版本。它是以一种SEH扩展的方式提供的,通过对程序中使用的SEH结构进行一些安全检测,来判断应用程序是否受到了SEH攻击。SEHOP的核心是检测程序栈中的所有SEH结构链表,特别是最后一个SEH结构,它拥有一个特殊的异常处理函数指针,指向的是一个位于NTDLL中的函数。
异常处理时,由系统接管分发异常处理,因此上面描述的检测方案完全可以由系统独立来完成,正因为SEH的这种与应用程序的无关性,因此应用程序不用做任何改变,你只需要确认你的系统开启了SEHOP即可。
对安全软件的意义
对于更高的操作系统,微软提出了或者实现了很多的安全解决方案,但一般这些方案在主流操作系统中都是默认关闭的(基于一些兼容性的考虑),安全软件是否可以针对特定的操作系统,提醒用户开启一些必要的安全选项(甚至在出现了兼容性问题时,帮助用户来关闭这些安全选项)?我想这将把漏洞溢出攻击的难度最大化,而把漏洞利用的可能性降至最低。而其成本是微乎其微的。
开启SEHOP功能
在Windows Server 2008 和 Windows Server 2008 R2下SEHOP默认是开启的,而在Windows Vista Service Pack 1、 Windows 7下默认则是关闭的。可以参考微软的一篇KB文章:http://support.microsoft.com/kb/956607/zh-cn,如果想自己开启可以简单进行如下操作:
(a) 打开注册表编辑器找到以下注册表子项:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\kernel,查看右边的属性DisableExceptionChainValidation的值。
注意如果您找不到
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\kernel\
子项下的 DisableExceptionChainValidation 注册表项,请自行创建一个DWORD类型的属性DisableExceptionChainValidation。
(b) 将DisableExceptionChainValidation 注册表项的值更改为 0,则表示启用了SEHOP
注意:值为 1 将禁用该注册表项。值为 0 则启用该功能。
基本原理
SEHOP的基本原理就是验证异常处理链表的完整性,下面这段伪码是在SEHOP功能开启后才会执行的逻辑(来源于Vistasp1,实现在RtlDispatchException函数里面):
// Skip the chain validation if the DisableExceptionChainValidation bit is set
if (process_flags & 0x40 == 0)
{
// Skip the validation if there are no SEH records on the linked list
if (record != 0xFFFFFFFF)
{
// Walk the SEH linked list
do
{
// 1、The record must be on the stack
if (record < stack_bottom || record > stack_top)
goto corruption;
// 2、The end of the record must be on the stack
if ((char*)record + sizeof(EXCEPTION_REGISTRATION) > stack_top)
goto corruption;
// 3、The record must be 4 byte aligned
if ((record & 3) != 0)
goto corruption;
handler = record->handler;
// 4、The handler must not be on the stack
if (handler >= stack_bottom && handler < stack_top)
goto corruption;
record = record->next;
} while (record != 0xFFFFFFFF);
// End of chain reached
// Is bit 9 set in the TEB->SameTebFlags field? This bit is set in
// ntdll!RtlInitializeExceptionChain, which registers
// FinalExceptionHandler as an SEH handler when a new thread starts.
if ((TEB->word_at_offset_0xFCA & 0x200) != 0) {
// 5、The final handler must be ntdll!FinalExceptionHandler
if (handler != &FinalExceptionHandler)
goto corruption;
}
} // end if (record != 0xFFFFFFFF)
}
从这段伪码可以看出SEHOP的一些限制检测条件:
•SEH结构都必须在栈上;
•最后一个SEH结构也必须在栈上;
•所有的SEH结构都必须是4字节对齐的;
•SEH结构中的handle(处理函数地址)必须不在栈上;
•最后一个SEH结构的handle必须是ntdll!FinalExceptionHandler函数;
•最后一个SEH结构的next seh指针必须为特定值0xFFFFFFFF;
下面这张图勾勒了SEHOP的基本原理
后记
SEHOP的保护是系统级别的,将更难绕过去。而且不需要修改原有的应用程序,对性能的影响基本可以忽略(因为只有在触发异常时才会触发SEHOP保护逻辑)。而覆盖SEH进行攻击是在/GS的保护出来以后,无法覆盖函数返回地址时,攻击者常用的技巧。
SEHOP方案终结了SEH覆盖攻击吗?未必!绕过SEHOP的方法可以参考文章:http://www.shell-storm.org/papers/files/760.pdf(中文翻译版:http://bbs.pediy.com/showthread.php?t=104707),它的基本思路是构造一个伪造的SEH链表,从而欺骗SEH检测,达到绕过的目的,这不在本文的讨论范畴,有兴趣的可以去参看上面的提到的文章。不过,作者也提到了在ASLR和DEP的都开启的情况下,利用是非常困难的。
参考资料
[1] 【翻译】绕过SEHOP安全机制
http://bbs.pediy.com/showthread.php?t=104707
[2] How to enable Structured Exception Handling Overwrite Protection (SEHOP) in Windows operating systems
http://support.microsoft.com/kb/956607
[3] Preventing the Exploitation of SEH Overwrites
http://www.uninformed.org/?v=5&a=2&t=txt
[4] Preventing the Exploitation of SEH Overwrites
[5] Preventing the Exploitation of Structured Exception Handler (SEH) Overwrites with SEHOP
http://blogs.technet.com/swi/archive/2009/02/02/preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx
[6] bypassing-browser-memory-protections
[7] Bypassing SEHOP