GS Cookie保护机制的有效性与局限性
Microsoft C/C++编译器支持GS开关,旨在运行时检测栈缓冲区溢出并终止进程,从而在大多数情况下防止攻击者控制易受攻击的机器。本文不会详细讨论GS的工作原理,因此可以参考这些MSDN文章以获取概述和大量关于GS如何工作以及什么是GS Cookie的详细信息。需要注意的是,根据具体的漏洞,即使函数受到GS Cookie的保护,基于栈的缓冲区溢出仍然可能被利用——例如,如果攻击者可以在Cookie检查之前获得控制权。然而,即使在这些情况下,GS通常也是利用和/或利用可靠性的重大障碍。最近有一些基于栈的攻击没有被GS缓解——本文以几个例子为例,探讨了原因。
MS08-067 – netapi32解析路径名漏洞
在MS08-067(另见相关的SDL博客文章)中,栈上有一个固定大小的缓冲区,漏洞在于解析路径中“..”子字符串的代码,根据需要将其替换为显式目录名。例如,“\server\A\B\C..\D....\E”将被解析为“\server\A\E”。漏洞在于,当向后搜索路径字符串缓冲区中的‘\’字符时,此搜索返回的指针有时可能最终位于路径缓冲区的开始之前。
让我们看看这一关键事实如何影响GS的有效性,当数据随后被复制到从该地址开始的内存中时。调用栈如下:
netapi32!ConvertPathMacros+0x101 netapi32!CanonicalizePathName+0x102
路径字符串缓冲区在CanonicalizePathNames中定义,该函数受GS保护。ConvertPathMacros函数不受GS保护:它接受指向CanonicalizePathNames中定义的路径缓冲区的指针作为其参数之一,并且本身没有会导致其受GS保护的局部变量。
初始栈布局如下,ConvertPathMacros函数有一个指向CanonicalizePathName中定义的pathBuffer变量的指针:

步骤1:搜索‘\’字符导致指针引用路径缓冲区开始之前的地址。

步骤2:攻击者控制的数据随后从该地址开始写入,覆盖了ConvertPathMacro的栈帧数据:

步骤3:ConvertPathMacro函数返回,但其返回地址已被覆盖,因此攻击者获得控制权。在上图中,CanonicalizePathName中的GS Cookie未被覆盖。注意,溢出是否覆盖了CanonicalizePathName中的GS Cookie是无关紧要的:这是因为CanonicalizePathName栈帧中的Cookie仅在CanonicalizePathName返回时检查。而攻击者在ConvertPathMacros返回时早已获得控制权。
这是一个基于栈的漏洞和攻击的例子,GS根本不是为了缓解这种情况而设计的:GS仅在以下情况下防止溢出:
- Cookie作为溢出的一部分被覆盖。
- 函数返回时达到Cookie检查。
在此示例中,上述两个标准均不需要适用。
MS07-017 – ANI文件解析漏洞
相比之下,与MS07-017中的ANI漏洞(另见相关的SDL博客文章)进行比较。相应的调用栈是:
user32!ReadChunk user32!LoadAniIcon
ReadChunk实际上只是将数据从ANI文件复制到LoadAniIcon提供的指针。攻击者无法控制此指针,因此利用MS07-017无法像前一种情况那样通过覆盖ReadChunk的栈帧来实现。然而,定义易受溢出影响的缓冲区的LoadAniIcon函数根本没有受GS保护!因此,针对LoadAniIcon返回地址的传统溢出是可行的。

在ANI漏洞中溢出的ANIHEADER局部变量是一个纯数据结构:
|
|
编译器使用启发式方法决定哪些函数受GS保护,这主要针对防止字符串缓冲区溢出。由于LoadAniIcon不包含此类字符串缓冲区,因此它不受GS保护。
与前一个示例不同,ANI漏洞原则上可以通过GS缓解,如果GS应用更广泛;例如,如果LoadAniIcon受GS保护,那么情况将如下所示:

溢出将覆盖GS Cookie,当LoadAniIcon返回时,GS Cookie检查将检测到溢出并终止进程。如开头所述,根据LoadAniIcon中溢出和函数退出时Cookie检查之间的确切控制流,仍然可能利用此漏洞;然而,GS已经移除了通用的利用方法,使任何漏洞利用更难开发,并且(经验告诉我们)通常可靠性要低得多。
事实证明,有一种方法可以通过pragma指示编译器在哪些函数受GS保护方面更加积极:#pragma strict_gs_check(Mike Howard在此博客中介绍)。事实上,部分由于此ANI漏洞,MSEC与Windows中的产品团队合作,在Windows Vista Service Pack 1中将严格的GS pragma应用于许多解析器组件。
总结
GS旨在缓解特定类别的基于栈的攻击,使基于栈的漏洞利用更难开发、可靠性更低,并在某些情况下将攻击者代码执行减少为拒绝服务。例如,在撰写本文时,我们不知道Windows XP SP2+或Windows Server 2003 SP1平台上的MS06-040的漏洞利用。SDL要求所有Microsoft产品都启用GS构建。许多第三方产品也使用GS——包括最近版本的Quicktime、Adobe Acrobat和Flash等。
当攻击者对溢出的开始位置或溢出/下溢的方向有更精细的控制时,GS缓解措施将无济于事。然而,在某些情况下,如果存在GS,它将有所帮助,而其缺失纯粹与编译器使用的默认启发式方法有关。对于高风险代码——例如处理不受信任数据的代码——考虑使用strict_gs_check pragma。
展望未来,我也对“增强型GS”的前景感到兴奋——“增强型GS”是什么?嗯,这是另一篇文章:请在接下来的几天内回来阅读所有相关内容!
当然,没有什么可以替代代码本身的安全!
- Tim Burrell, MSEC Security Science
相关文章链接
- /GS (Buffer Security Check), MSDN Visual C++ compiler options entry
- Compiler Security Checks in Depth, MSDN Visual Studio Technical Articles, Brandon Bray, February 2002
- MS08-067 and the SDL, SDL blog entry, Michael Howard, October 2008
- Lessons learned from the Animated Cursor Security Bug, SDL blog entry, Michael Howard , April 2007
- Hardening stack-based buffer overrun detection in VC2005 SP1, Michael Howard’s blog, April 2007
- #pragma strict_gs_check , MSDN C/C++ pre-processor reference.