使用 GCC 避免堆栈粉碎攻击
堆栈粉碎是一个奇特的术语,用于堆栈缓冲区溢出。它指的是利用代码中导致缓冲区溢出的错误的攻击。早些时候,程序员/开发人员有责任确保他们的代码中没有缓冲区溢出的可能性,但是随着时间的推移,像 gcc 这样的编译器已经有了标志,以确保破解者不会利用缓冲区溢出问题来破坏系统或程序。
当我尝试在具有 gcc 4.6.3 版本的 Ubuntu 12.04 上重现缓冲区溢出时,我开始了解这些标志。这是我想做的事情:
#include <stdio.h>
#include <string.h>
int main(void)
{
int len = 0;
char str[10] = {0};
printf("\n Enter the name \n");
gets(str); // Used gets() to cause buffer overflow
printf("\n len = [%d] \n", len);
len = strlen(str);
printf("\n len of string entered is : [%d]\n", len);
return 0;
}
在上面的代码中,我使用了 gets() 来接受来自用户的字符串。然后计算该字符串的长度并打印回标准输出。这里的想法是输入一个长度超过 10 个字节的字符串。由于 gets() 不检查数组边界,所以它会尝试将输入复制到 str 缓冲区中,这样就会发生缓冲区溢出。
这是我执行程序时发生的事情:
$ ./stacksmash
Enter the name
TheGeekStuff
len = [0]
len of string entered is : [12]
*** stack smashing detected ***: ./stacksmash terminated
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x45)[0xb76e4045]
/lib/i386-linux-gnu/libc.so.6(+0x103ffa)[0xb76e3ffa]
./stacksmash[0x8048548]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb75f94d3]
./stacksmash[0x8048401]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:06 528260 /home/himanshu/practice/stacksmash
08049000-0804a000 r--p 00000000 08:06 528260 /home/himanshu/practice/stacksmash
0804a000-0804b000 rw-p 00001000 08:06 528260 /home/himanshu/practice/stacksmash
0973a000-0975b000 rw-p 00000000 00:00 0 [heap]
b75af000-b75cb000 r-xp 00000000 08:06 787381 /lib/i386-linux-gnu/libgcc_s.so.1
b75cb000-b75cc000 r--p 0001b000 08:06 787381 /lib/i386-linux-gnu/libgcc_s.so.1
b75cc000-b75cd000 rw-p 0001c000 08:06 787381 /lib/i386-linux-gnu/libgcc_s.so.1
b75df000-b75e0000 rw-p 00000000 00:00 0
b75e0000-b7783000 r-xp 00000000 08:06 787152 /lib/i386-linux-gnu/libc-2.15.so
b7783000-b7784000 ---p 001a3000 08:06 787152 /lib/i386-linux-gnu/libc-2.15.so
b7784000-b7786000 r--p 001a3000 08:06 787152 /lib/i386-linux-gnu/libc-2.15.so
b7786000-b7787000 rw-p 001a5000 08:06 787152 /lib/i386-linux-gnu/libc-2.15.so
b7787000-b778a000 rw-p 00000000 00:00 0
b7799000-b779e000 rw-p 00000000 00:00 0
b779e000-b779f000 r-xp 00000000 00:00 0 [vdso]
b779f000-b77bf000 r-xp 00000000 08:06 794147 /lib/i386-linux-gnu/ld-2.15.so
b77bf000-b77c0000 r--p 0001f000 08:06 794147 /lib/i386-linux-gnu/ld-2.15.so
b77c0000-b77c1000 rw-p 00020000 08:06 794147 /lib/i386-linux-gnu/ld-2.15.so
bfaec000-bfb0d000 rw-p 00000000 00:00 0 [stack]
Aborted (core dumped)
好吧,令人惊喜的是,执行环境能够以某种方式检测到在这种情况下可能发生缓冲区溢出。在输出中,您可以看到检测到堆栈粉碎。这促使我探索如何检测缓冲区溢出。
在寻找原因时,我遇到了一个 gcc 标志“-fstack-protector”。这是此标志的描述(来自手册页):
-fstack-protector
发出额外的代码来检查缓冲区溢出,例如堆栈粉碎攻击。这是通过将保护变量添加到具有易受攻击对象的函数来完成的。这包括调用 alloca 的函数,以及缓冲区大于 8 字节的函数。进入函数时初始化守卫,然后在函数退出时检查。如果保护检查失败,则会打印一条错误消息并退出程序。
注意:在 Ubuntu 6.10 和更高版本中,如果没有找到 -fno-stack-protector、-nostdlib 和 -ffreestanding,则默认情况下会为 C、C++、ObjC、ObjC++ 启用此选项。
所以你看到 gcc 有这个标志,它发出额外的代码来检查缓冲区溢出。现在我想到的下一个问题是我在编译时从未包含此标志,然后如何启用此功能。然后我阅读了最后两行,上面说 Ubuntu 6.10 默认启用此功能。
然后,作为下一步,我决定在编译时使用标志“-fno-stack-protector”来停用此功能,然后尝试执行我之前做的相同用例。
这是我的做法:
$ gcc -Wall -fno-stack-protector stacksmash.c -o stacksmash
$ ./stacksmash
Enter the name
TheGeekStuff
len = [26214]
len of string entered is : [12]
因此我们看到,一旦使用此标志编译代码,然后使用相同的输入,执行环境就无法检测到实际发生的缓冲区溢出并破坏了变量“len”的值。
此外,如果您是 gcc 新手,您应该了解我们之前讨论过的最常用的gcc 编译器选项。
- 点赞
- 收藏
- 关注作者
评论(0)