缓冲区溢出导致安全漏洞的示例

时间:2010-07-20 15:59:24

标签: c security overflow buffer

我读过许多关于strcpy,memcpy等不安全函数的文章,这些文章在处理外部数据时可能会导致安全问题,例如文件内容或来自套接字的数据。这可能听起来很愚蠢,但我写了一个易受攻击的程序,但我没有设法“破解”它。

我理解缓冲区溢出的问题。以此示例代码为例:

int main() {
   char buffer[1];
   int var = 0;

   scan("%s", &buffer);
   printf("var = 0x%x\n", var);
   return 0;
}

当我执行程序并输入“abcde”时,程序输出0x65646362,它是十六进制+ little-endian中的“edcb”。但是我读到你可以修改在堆栈上推送的eip值,以使程序执行一些不需要的代码(例如,在调用system()函数之前)。

然而,函数的程序集开始如下:

push %ebp
mov %ebp, %esp
and $0xfffffff0, %esp
sub $0x20, %esp

由于%esp的值在函数开始时是随机的,并且由于这个“和”,似乎没有可靠的方法将精确值写入推送的eip值。

此外,我读到可以执行你在缓冲区中编写的代码(这里缓冲区只有1个字节长,但实际上它足够大以存储一些代码)但是你会给它什么值eip是为了这样做(考虑到缓冲区的位置是随机的)?

那么为什么开发人员如此担心安全问题(除了程序可能会崩溃)?你有一个易受攻击的程序的例子,以及如何“破解”它来执行不需要的代码?我在linux上试过这个,Windows不太安全吗?

6 个答案:

答案 0 :(得分:15)

阅读Aleph One的优秀文章:Smashing the Stack for Fun and Profit

答案 1 :(得分:4)

一方面,不要低估能否在EIP中不可靠地放置一个值所带来的危险。如果一个漏洞利用16次,并且攻击的服务会自动重启,就像许多Web应用程序一样,那么在尝试访问时失败的攻击者总是可以尝试再试一次。

同样在很多情况下,ESP的价值不如你想象的那么随机。对于32位系统的初学者来说,它几乎总是四的倍数。这意味着and $0xfffffff0, %esp指令提供的额外填充将为0,4,8或12个字节。这意味着可以重复四次写入返回EIP的值,以覆盖返回EIP地址的所有可能偏移量。

实际上有更多激进的stack protection / buffer overflow detection机制。但是,即便如此,也有各种方法和手段。

此外,有关此类事情可能存在危险的示例,请考虑var的值对您的逻辑是否重要,如以下玩具示例所示。

int main() {
  char buffer[1];
  int var = 0;

  var = SecurityCheck();

  scan("%s", &buffer);
  if (var != 0)
    GrantAccess();
  else
    DenyAccess()
}

答案 2 :(得分:4)

此外,您不必使用指向字符串中某些内容的指针覆盖EIP。例如,您可以使用指向system()的指针覆盖它,并使用指向程序图像中固定位置的/bin/sh指针覆盖下一个单词。

编辑:请注意system使用PATH(实际上它通过shell运行命令),因此"sh"也一样好;因此,任何以字符串末尾的“sh”结尾的英文单词都会提供您需要的参数。

答案 3 :(得分:1)

基于缓冲区溢出的实际漏洞利用的典型示例是1988年的Morris Worm

答案 4 :(得分:1)

正如其他答案中所提到的,绝对可靠性并不总是对攻击成功至关重要。自动重启的应用程序就是一个例子。 suid程序上的本地可利用的缓冲区溢出将是另一个。还有NOP雪橇技术可以增加成功利用的机会,在你的shellcode之前加入大量的NOP,这样你就有更好的机会正确猜出shellcode的“开始”。

还有许多技术可以提高攻击的可靠性。在Windows上,当天早些时候,许多漏洞利用程序(trampoline)中某处的“jmp%esp”地址覆盖了返回地址。

“通过示例进行不安全的编程”对Linux有一个很好的技巧。清理环境并将shellcode放在环境变量中。回到当天,这将导致靠近堆栈顶部的可预测地址。

还有像return-into-libc和面向回归的编程等变体。

甚至还有一篇关于如何利用1字节堆栈溢出的文章(意味着缓冲区只被一个字节溢出)(btw,1字节堆溢出在绝大多数情况下也可以利用,除非保护)。

总而言之,开发人员并不是偏执狂,有很多方法可以利用即使是最奇怪的情况,并记住:

  • 当一个程序完成它应该做的事情时,它的质量很好。
  • 一个程序在完成它应该做的事情时是安全的,而不再是

答案 5 :(得分:0)

这是一个Windows版本和教程:

http://www.codeproject.com/KB/winsdk/CodeInject.aspx

我一直被警告的一般情况是:

printf( string );

因为用户可以在其中提供"%n",这允许您将所需的任何内容插入到内存中。您需要做的就是找到系统调用的内存偏移量,传递几个"%n"和垃圾字符,然后将内存地址插入到通常为返回向量的堆栈中。 Voila - 插入您喜欢的任何代码。