网络安全知识:缓冲区溢出攻击
来源:硅谷动力 更新时间:2012-04-13
  攻击原理

  几十年来,缓冲区溢出一直引起许多严重的安全性问题。其中最著名的例子是:1988 年,因特网蠕虫程序在 finger 中利用缓冲区溢出感染了因特网中的数万台机器。但是,缓冲区溢出问题并非已成古老的历史,缓冲区溢出(又称堆栈溢出)攻击已成为最常用的黑客技术之一。据统计,仅去年缓冲区溢出就占使CERT/CC 提出建议的所有重大安全性错误的百分之五十以上。

  引起缓冲区溢出问题的根本原因是 C(与其后代 C++)本质就是不安全的,没有边界来检查数组和指针的引用,也就是开发人员必须检查边界(而这一行为往往会被忽视),否则会冒遇到问题的风险。标准C 库中还存在许多非安全字符串操作,包括:strcpy() 、sprintf() 、gets() 等。

  缓冲区溢出源于每个程序运行的需要:放置数据的空间。多数计算机程序都在内存中创建多个地址用于信息存储。C 编程语言允许程序员在运行时在内存的两个不同部分(堆栈和堆)中创建存储器。通常,分配到堆的数据是那些malloc() 或新建时获得的数据。而分配到堆栈的数据一般包括非静态的局部变量和所有按值传递的参数。大部分其它信息存储在全局静态存储器中。在分配同一数据类型的相邻块时,这块内存区域称为缓冲区。  

  在写入缓冲区时,C 程序员必须注意存储在缓冲区中的数据不能超过它所能容纳的量。缓冲区只能容纳一定数量的位,就象一个杯子只能盛一定量的水。如果放到杯子中的水太多,多余的水就会溢出到别的地方。相似地,如果试图放入缓冲区的数据比它能装入的要多,额外的数据就会溢出到别处。

  当程序写入超过缓冲区的边界时,这就是所谓的"缓冲区溢出"。发生缓冲区溢出时,会覆盖下一个相邻的内存块。由于C 语言本质上的不安全性,所以它允许程序随意(或者更准确地说是完全出于偶然)溢出缓冲区。没有运行时检查来这一防止写入超过缓冲区末尾,所以程序员必须在其自己的代码中执行这一检查,否则继续下去会出现问题。

  读取或写入超过缓冲区的末尾时,会导致许多不同(并且通常是不可预料的)行为:1) 程序的执行很奇怪,2) 程序完全失败,或者 3) 程序可以继续,而且在执行中没有任何明显不同。缓冲区溢出的副作用取决于:

  写入的数据中有多少超过缓冲区边界

  当缓冲区已满并且溢出时,覆盖了哪些数据(如果有的话)

  程序是否试图读取溢出期间被覆盖的数据

  哪些数据最终替换被覆盖的内存

  存在缓冲区溢出的程序的不确定行为使得对它们的调试异常棘手。最坏的情况是:程序可能正发生缓冲区溢出,但根本没有任何副作用的迹象。因此,缓冲区溢出问题常常在标准测试期间是发现不了的。认识缓冲区溢出的重要一点是:在发生溢出时,会潜在地修改碰巧分配在缓冲区附近的任何数据。

  一般情况下,覆盖其他数据区的数据是没有意义的,最多造成应用程序错误,但是,如果输入的数据是经过"黑客"精心设计的,覆盖缓冲区的数据恰恰是黑客的入侵程序代码,黑客就获取了程序的控制权。

  最简单的情况就是考虑直接在缓冲区后面的内存中分配一个布尔标志。这个标志决定运行程序的用户是否可以访问专用文件。如果有不怀好意的用户覆盖缓冲区,则会更改标志的值,从而指出攻击者是非法访问专用文件。

  缓冲区溢出导致安全性问题的另一个方法是通过摧毁堆栈。摧毁堆栈的目的是导致一个特定的编程故障:不仔细使用分配在程序运行时堆栈上的数据缓冲区,即局部变量和函数自变量。有效的摧毁堆栈所造成的后果比上一示例中提到的改变布尔访问控制标志的后果更为严重。有创造力的攻击者会通过摧毁堆栈利用缓冲区溢出的弱点,然后运行任何代码。这种想法是相当直接的:在某处插入一些攻击代码(例如,调用shell 的代码)并以将控制传递给攻击代码的方式来覆盖堆栈。

  此外,攻击者利用缓冲区溢出得到机器上的交互式会话 (shell)。如果被利用的程序以较高的优先权在运行(如 root用户或管理员),则攻击者就会在交互式会话中得到该优先权。最惊人的缓冲区溢出是堆栈的摧毁,它会在超级用户或 root、shell中造成后果。许多可以利用脚本都能在网络上找到,它们对特定体系结构上的堆栈进行摧毁。