我读过许多关于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不太安全吗?
答案 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 - 插入您喜欢的任何代码。