第 6 章. 避免缓冲区溢出

 

敌人将侵占这地;他必拆毁你的堡垒,抢掠你的城池。

 阿摩司书 3:11 (NIV)
目录
6.1. C/C++ 中的危险
6.2. C/C++ 中的库解决方案
6.2.1. 标准 C 库解决方案
6.2.2. 静态和动态分配的缓冲区
6.2.3. strlcpy 和 strlcat
6.2.4. libmib
6.2.5. C++ std::string 类
6.2.6. Libsafe
6.2.7. 其他库
6.3. C/C++ 中的编译解决方案
6.4. 其他语言

一个极其常见的安全漏洞是“缓冲区溢出”漏洞。缓冲区溢出也称为“缓冲区过载”,并且有许多种缓冲区溢出攻击(包括“堆栈粉碎”和“堆粉碎”攻击)。从技术上讲,缓冲区溢出是程序内部实现的问题,但这是一个如此常见且严重的问题,以至于我将此信息放在单独的章节中。为了让您了解这个主题的重要性,在 CERT,1998 年的 13 个安全公告中有 9 个,以及 1999 年至少一半的安全公告都涉及缓冲区溢出。1999 年在 Bugtraq 上进行的一项非正式调查发现,大约 2/3 的受访者认为缓冲区溢出是系统安全漏洞的主要原因(其余受访者认为是“配置错误”)[Cowan 1999]。这是一个古老且众所周知的问题,但它仍在不断出现 [McGraw 2000]。

当您将一组值(通常是字符串)写入固定长度缓冲区,并且至少将一个值写入该缓冲区边界之外(通常超出其末尾)时,会发生缓冲区溢出。当从用户读取输入到缓冲区时可能会发生缓冲区溢出,但它也可能在程序中的其他类型的处理期间发生。

如果一个安全程序允许缓冲区溢出,那么溢出通常会被攻击者利用。如果缓冲区是本地 C 变量,则溢出可用于强制函数运行攻击者选择的代码。这种特定的变体通常被称为“堆栈粉碎”攻击。堆中的缓冲区也好不到哪里去;攻击者可能能够使用此类溢出来控制程序中的其他变量。更多详细信息可以在 Aleph1 [1996]、Mudge [1995]、LSD [2001] 或 Nathan P. Smith 的“堆栈粉碎安全漏洞”网站 http://destroy.net/machines/security/ 上找到。Crispin Cowan 等人在 2000 年在 http://immunix.org/StackGuard/discex00.pdf 上讨论了这个问题以及一些应对方法。Pierre-Alain Fayolle 和 Vincent Glaume 在 http://www.enseirb.fr/~glaume/indexen.html 上讨论了这个问题以及在 Linux 中应对它们的一些方法。

大多数高级编程语言本质上对此问题免疫,要么是因为它们自动调整数组大小(例如,Perl),要么是因为它们通常检测并防止缓冲区溢出(例如,Ada95)。但是,C 语言不提供针对此类问题的保护,并且 C++ 可以很容易地以导致此问题的方式使用。汇编语言也不提供保护,并且一些通常包含此类保护的语言(例如,Ada 和 Pascal)可以禁用此保护(出于性能原因)。即使您的大部分程序是用另一种语言编写的,许多库例程也是用 C 或 C++ 编写的,以及调用它们的“胶水”代码,因此其他语言通常不能像您希望的那样提供完整的缓冲区溢出保护。