缓冲区溢出导致安全漏洞的示例
我读了很多关于不安全函数(如 strcpy、memcpy 等)的文章,这些函数在处理外部数据(如文件内容或来自套接字的数据)时可能会导致安全问题。这听起来可能很愚蠢,但我编写了一个易受攻击的程序,但我没有设法“破解”它。
我了解缓冲区溢出的问题。拿这个示例代码来说:
int main() {
char buffer[1];
int var = 0;
scan("%s", &buffer);
printf("var = 0x%x\n", var);
return 0;
}
当我执行程序并输入“abcde”时,程序输出0x65646362,即十六进制+小尾数的“edcb”。但是我读到,您可以修改压入堆栈的 eip 值,以使程序执行一些不需要的代码(例如,在调用 system() 函数之前)。
然而,函数的汇编是这样开始的:
push %ebp
mov %ebp, %esp
and $0xfffffff0, %esp
sub $0x20, %esp
由于 %esp 的值在函数开始时是随机的,并且由于这个“与”,似乎没有可靠的方法将精确的值写入推送的 eip 值。
此外,我读到可以执行您在缓冲区中编写的代码(这里缓冲区只有 1 个字节长,但实际上它足够大以存储一些代码),但是您将按顺序为 eip 赋予什么值这样做(考虑到缓冲区的位置是随机的)?
那么为什么开发人员如此担心安全问题(除了程序可能崩溃)?您是否有一个易受攻击的程序的示例以及如何“破解”它以执行不需要的代码?我在linux上试过了,Windows不太安全吗?
I read many articles about unsafe functions like strcpy, memcpy, etc. which may lead to security problems when processing external data, like the content of a file or data coming from sockets. This may sound stupid, but I wrote a vulnerable program but I did not manage to "hack" it.
I understand the problem of buffer overflow. Take this example code:
int main() {
char buffer[1];
int var = 0;
scan("%s", &buffer);
printf("var = 0x%x\n", var);
return 0;
}
When I execute the program and type "abcde", the program outputs 0x65646362 which is "edcb" in hexadecimal + little-endian. However I read that you could modify the eip value that was pushed on the stack in order to make the program execute some unwanted code (eg. right before a call to the system() function).
However the function's assembly starts like this:
push %ebp
mov %ebp, %esp
and $0xfffffff0, %esp
sub $0x20, %esp
Since the value of %esp is random at the start of the function and because of this "and", there seems to be no reliable way to write a precise value into the pushed eip value.
Moreover, I read that it was possible to execute the code you wrote in the buffer (here the buffer is only 1 byte long, but in reality it would be large enough to store some code) but what value would you give to eip in order to do so (considering the location of the buffer is random)?
So why are developpers so worried about security problems (except that the program could crash) ? Do you have an example of a vulnerable program and how to "hack" it to execute unwanted code? I tried this on linux, is Windows less safe?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
阅读 Aleph One 的精彩文章:粉碎堆栈以获得乐趣和利润。
Read the excellent article by Aleph One: Smashing the Stack for Fun and Profit.
一方面,不要低估与在 EIP 中不可靠地放置值相关的危险。如果漏洞利用的次数为 16 次,并且它所攻击的服务会像许多 Web 应用程序一样自动重新启动,那么在尝试获取访问权限时失败的攻击者可以随时尝试、再试一次。
此外,在很多情况下,ESP 的值并不像您想象的那样随机。对于 32 位系统的初学者来说,它几乎总是四的倍数。这意味着
和 $0xfffffff0, %esp
指令提供的额外填充将为 0、4、8 或 12 字节。这意味着可以将要写入返回 EIP 的值重复四次,以覆盖返回 EIP 地址的所有可能的偏移量。实际上还有更激进的堆栈保护/缓冲区溢出检测机制。然而,即使是这些,也有一些方法和手段。
另外,有关此类事情可能存在危险的示例,请考虑
var
的值对您的逻辑是否很重要,如以下玩具示例所示。Well for one thing, don't under estimate the hazards associated with being able to unreliably place a value inside EIP. If an exploit works one in 16 times, and the service it is attacking automatically restarts, like many web applications, then an attacker that fails when trying to get access can always try, try again.
Also in a lot of cases the value of ESP is less random than you think. For starters on a 32-bit system it is nearly always a multiple of four. That means that the extra padding offered by the
and $0xfffffff0, %esp
instruction will be either 0, 4, 8 or 12 bytes. That means that it is possible to just repeat the value that is to be written into the return EIP four times to cover all possible offsets to the address of return EIP.There are actually much more aggressive stack protection / buffer overflow detection mechanisms around. However, there are ways and means around even these.
Also, for an example of where this sort of thing can be dangerous, consider if the value of
var
was important to you logic as in the following toy example.此外,您不必使用指向字符串中某些内容的指针来覆盖 EIP。例如,您可以使用指向
system()
的指针覆盖它,并使用指向程序映像中固定位置的/bin/sh
的指针覆盖下一个单词。编辑:请注意,
system
使用PATH
(实际上它通过 shell 运行命令),因此"sh"
也同样好;因此,字符串末尾以“sh”结尾的任何英语单词都可以提供您需要的参数。Further you don't have to overwrite EIP with a pointer to something in your string. For example you could overwrite it with a pointer to
system()
and overwrite the next word with a pointer to/bin/sh
at a fixed location in the program image.Edit: Note that
system
uses thePATH
(actually it runs the command via a shell), so"sh"
would be just as good; thus, any English word ending in "sh" at the end of a string provides the argument you need.基于缓冲区溢出的实际利用的典型示例是 1988 年的 Morris 蠕虫。
A classic example of an actual exploit based on buffer overruns is the Morris Worm of 1988.
正如其他答案中提到的,绝对可靠性并不总是攻击成功所必需的。自动重新启动的应用程序就是一个例子。 suid 程序上本地可利用的缓冲区溢出是另一个问题。还有 NOP sled 技术可以增加成功利用的机会,在 shellcode 之前放置大量 NOP,这样您就有更好的机会正确猜测 shellcode 的“开始”。
还有更多技术可以提高攻击的可靠性。在 Windows 上,过去,许多漏洞利用程序(蹦床)中某处的“jmp %esp”地址覆盖了返回地址。
“不安全编程示例”对 Linux 有一个很好的技巧。清理您的环境并将 shellcode 放入环境变量中。在过去,这将导致在堆栈顶部附近出现一个可预测的地址。
还有一些变体,例如 return-into-libc 和面向返回的编程。
Phrack 上甚至有一篇关于如何利用 1 字节堆栈溢出(意味着缓冲区仅溢出了一个字节)的文章(顺便说一句,在绝大多数情况下,除了保护之外,1 字节堆溢出也是可利用的)。
总而言之,这并不是说开发人员偏执,即使是最奇怪的情况也有很多方法可以利用,并且记住:
As mentioned in other answers, absolute reliability is not always essential for the attack to succeed. Applications that restart automatically are an example. Locally exploitable buffer overflows on suid programs would be another. And there's the NOP sled technique to increase chances of successful exploitation, put a lot of NOPs before your shellcode so you have a much better chance to correctly guess the "start" of your shellcode.
There are many more techniques for increasing the reliability of attacks. On Windows, back in the day, many exploits overwrote the return address with the address of a "jmp %esp" located somewhere in the program (trampoline).
"Insecure programming by example" had a nice trick for Linux. Clean your environment and put your shellcode in an environment variable. Back in the day, this would lead to a predictable address near the top of the stack.
And there are also variants like return-into-libc and return-oriented programming.
There was even an article on Phrack on how to exploit 1-byte stack overflows (meaning the buffer was overrun by only one byte) (btw, 1-byte heap overflows are also exploitable in the vast majority of cases, barring protections).
To sum up, it's not that developers are paranoid, there are lots of ways to exploit even the strangest cases, and remember:
这是 Windows 版本和教程:
http://www.codeproject.com/KB/winsdk /CodeInject.aspx
我总是被警告的一般情况是:
因为用户可以在其中提供一个
"%n"
,它允许您将任何想要插入内存的内容。您需要做的就是找到系统调用的内存偏移量,传递一些"%n"
和垃圾字符,然后将内存地址插入到返回向量通常所在的堆栈中是。瞧——插入您喜欢的任何代码。Here's a windows version and tutorial:
http://www.codeproject.com/KB/winsdk/CodeInject.aspx
The general case I was always warned about was:
Because the user can provide a
"%n"
in there, which allows you to insert anything you want into memory. All you need to do is find the memory offset for a system call, pass a few"%n"
's and junk characters, and thus insert the memory address onto the stack where the return vector would normally be. Voila -- insert any code you like.