我们把"disable-randomization"设置成 “off” 。我们两次运行了程序然后查看进程的模块在内存中映射的地址。我们发现他们中的大部分的地址都是不同的。但是并不是每一个模块都这样,这就是在ASLR被开启的情况下,漏洞仍然可以利用成功的关键原因。
5. 现代的栈溢出攻击虽然有这么多的保护措施,但是还是有溢出漏洞,而且有时我们可以成功的利用这些漏洞。我已经向你们演示栈中的金丝雀可以保护程序在溢出的情况下不跳到恶意的SIP去执行。但是这只金丝雀仅仅被放到了SIP的前面而不是在栈中的局部变量里面。所以我们可以使用第一个例子里面覆盖SIP(也就是函数返回地址 函数返回的时候SIP就会被赋予这个值)的那种方法来覆盖函数的局部变量。而这个会导致许多不同的问题,在一些情况下,我们覆盖了一个函数指针,这个指针会在未来某一个时刻被执行。也有可能我们覆盖了一个指针,这个指针指向的内存会在未来被写入用户数据,于是攻击者就可以在任意的位置写入数据了。类似的情形经常会被成功的利用而得到进程的控制权。下面的代码就演示了这样的一个漏洞:
-----------------------------------
$ cat stackvuln.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define MAX_SIZE 48
#define BUF_SIZE 64
char data1[BUF_SIZE], data2[BUF_SIZE];
struct item {
char data[MAX_SIZE];
void *next;
};
int go(void) {
struct item item1, item2;
item1.next = &item2;
item2.next = &item1;
memcpy(item1.data, data1, BUF_SIZE); // Whoops, did we mean MAX_SIZE?
memcpy(item1.next, data2, MAX_SIZE); // Yes, yes we did.
exit(-1); // Exit in shame.
}
void hax(void) {
execl("/bin/bash", "/bin/bash", "-p", NULL);
}
void readfile(char *filename, char *buffer, int len) {
FILE *fp;
fp = fopen(filename, "r");
if (fp != NULL) {
fread(buffer, 1, len, fp);
fclose(fp);
}
}
int main(int argc, char **argv) {
readfile("data1.dat", data1, BUF_SIZE);
readfile("data2.dat", data2, BUF_SIZE);
go();
}
$ gcc stackvuln.c -o stackvuln
$ sudo chown root:root stackvuln
$ sudo chmod +s ./stackvuln
-----------------------------------