转载请注明出处:http://blog.youkuaiyun.com/wangxiaolong_china
1.1 Linux栈溢出保护机制
基本的栈溢出攻击,是最早产生的一种缓冲区溢出攻击方法,它是所有其他缓冲区溢出攻击的基础。但是,由于这种攻击方法产生的时间比较长,故而GCC编译器、Linux操作系统提供了一些机制来阻止这种攻击方法对系统产生危害。下面首先了解一下现有的用于保护堆栈的机制以及关闭相应保护机制的方法,为进一步分析基本栈溢出提供了良好的实验环境。
1. 内存地址随机化机制
在Ubuntu和其他基于Linux内核的系统中,目前都采用内存地址随机化的机制来初始化堆栈,这将会使得猜测具体的内存地址变得十分困难。
关闭内存地址随机化机制的方法是:
sysctl –w kernel.randomize_va_space=0
2. 可执行程序的屏蔽保护机制
对于Federal系统,默认会执行可执行程序的屏蔽保护机制,该机制不允许执行存储在栈中的代码,这会使得缓冲区溢出攻击变得无效。而Ubuntu系统中默认没有采用这种机制。
关闭可执行程序的屏蔽保护机制的方法是:
sysctl –w kernel.exec-shield=0
3. gcc编译器gs验证码机制
gcc编译器专门为防止缓冲区溢出而采取的保护措施,具体方法是gcc首先在缓冲区被写入之前在buf的结束地址之后返回地址之前放入随机的gs验证码,并在缓冲区写入操作结束时检验该值。通常缓冲区溢出会从低地址到高地址覆写内存,所以如果要覆写返回地址,则需要覆写该gs验证码。这样就可以通过比较写入前和写入后gs验证码的数据,判断是否产生溢出。
关闭gcc编译器gs验证码机制的方法是:
在gcc编译时采用-fno-stack-protector选项。
4. ld链接器堆栈段不可执行机制
ld链接器在链接程序的时候,如果所有的.o文件的堆栈段都标记为不可执行,那么整个库的堆栈段才会被标记为不可执行;相反,即使只有一个.0文件的堆栈段被标记为可执行,那么整个库的堆栈段将被标记为可执行。检查堆栈段可执行性的方法是:
如果是检查ELF库:readelf -lW $BIN | grep GNU_STACK查看是否有E标记
如果是检查生成的.o文件:scanelf -e $BIN查看是否有X标记
ld链接器如果将堆栈段标记为不可执行,即使控制了eip产生了跳转,依然会产生段错误。
关闭ld链接器不可执行机制的方法是:
在gcc编译时采用-z execstack选项。
1.1 基本栈溢出攻击原理及实验
下面,将用一个栈溢出攻击的例子的方式,来详细的讲解基本的栈溢出攻击的详细方法步骤。
在进行试验之前,先利用上面讲解的方法,将相应的栈保护机制关闭掉。
root@linux:~/pentest# sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space = 0
root@linux:~/pentest# sysctl -w kernel.exec-shield=0
error: "kernel.exec-shield" is an unknown key
代码如下:
root@linux:~/pentest# cat vulnerable.c
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char buffer[500];
strcpy(buffer, argv[1]);
return 0;
}
编译源码:
root@linux:~/pentest# gcc -fno-stack-protector -z execstack -g -o vulnerable vulnerable.c
用gdb调试该程序:
root@linux:~/pentest# gdb vulnerable
GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL ve