聲明:主要內容來自《The Shellcoder's Handbook》,摘錄重點作為筆記並加上個人的一些理解,如有錯,請務必指出。
幾乎所有的malloc實現都會用中繼資料儲存塊的位置、大小或與小塊有關的特殊資料。dlmalloc用儲存桶儲存這些資料,還有些malloc實現用平衡樹結構儲存它們。這些中繼資料一般儲存在兩個地方:malloc實現自己使用的全域變數;分配給使用者的記憶體塊的前/後位置。
基本堆溢出
絕大多數堆溢出的基本原理如下:堆和棧很相似,既包含了資料資訊,也包含了用來控製程序理解這些資料的維護資訊。我們所要掌握的技巧,就是通過malloc和free來達到這個目的:把一或兩個字寫入我們能控制的記憶體位址。
我們先看一個會產生堆溢出的程式:
//file: basicheap.c#include <stdio.h>int main(int argc, char *argv[]){ char *buf; char *buf2; buf = (char *)malloc(1024); buf2 = (char *)malloc(1024); printf("buf=%p, buf2=%p/n", buf, buf2); strcpy(buf, argv[1]); free(buf2);}
該程式分配有兩個緩衝區,它們在記憶體中是相鄰的,當第一個緩衝區溢位時會改寫第二個緩衝區中的中繼資料。編譯用ltrace跟蹤運行:
sep@sep:~/project/shellcode$ ltrace ./basicheap `perl -e 'print "A" x 5000'`__libc_start_main(0x8048444, 2, 0xbfb19a24, 0x80484d0, 0x80484c0 <unfinished ...>malloc(1024) = 0x804a008malloc(1024) = 0x804a410printf("buf=%p, buf2=%p/n", 0x804a008, 0x804a410buf=0x804a008, buf2=0x804a410) = 30strcpy(0x804a008, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...) = 0x804a008free(0x804a410 <unfinished ...>--- SIGSEGV (Segmentation fault) ---+++ killed by SIGSEGV +++
發生了segmentation fault。兩個緩衝區的首地址分別是0x804a008和0x804a410,它們之間的距離是0x804a410-0x804a008=1032=1024+8,它等於緩衝區的長度1024位元組加上存貯塊資訊頭的8個位元組。進行strcpy了5000個位元組的資料到buf中時,發生了堆溢出,從而改寫了buf2的頭部塊資訊,這時進行free(buf2)操作就會導致段故障。
對於strace、ltrace等工具的使用,見:http://www.ibm.com/developerworks/cn/linux/l-tsl/
我們怎樣欺騙malloc,使它處理改寫後的記憶體塊是問題的關鍵。首先,我們要清除被改寫的塊頭部的previous-in-use位,然後把“前一塊”的長度設為負數,這樣將運行我們在緩衝區中定義我們自己的塊。
malloc實現,包括Linux中dlmalloc,都把額外資訊儲存到空閑塊裡。空閑塊的前4個位元組是前向指標,接下來的4個位元組是一個後向指標,這兩種指標把空閑塊掛在雙向鏈表上。在對雙向鏈表的插入和刪除操作中,我們可以利用這些指標改寫任意記憶體位址中的資料。
./basicheap `python -c 'print "A" * 1024 + "/xff/xff/xff/xff" + "/xf0/xff/xff/xff"'`
該命令運行後,堆緩衝區buf溢出,改寫了buf2頭部的8個位元組為0xfffffff0和0xffffffff。
找出緩衝區的長度,在以上的例子中我們通常可以在記憶體中看到緩衝區(以A開始的)的起始位置,這個word之前的資料就是緩衝區的長度。(gdb) x/xw buf-4顯示長度是1033,它等於buf的長度1024加上儲存塊資訊的8個位元組,而最後1位指示這個塊之前是否還有其他塊。如果它被置位(像這個例子),表明這塊頭部沒有儲存前一個塊的大小;如果它被置0,則表示這個塊之前還有一個塊,而且buf-8中的資料就是前一個塊的大小。倒數第二位是一個標記,表示這個塊是否由nmap分配的。
sep@sep:~/project/shellcode$ gdb ./basicheapGNU gdb 6.4.90-debianCopyright (C) 2006 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome 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 "i486-linux-gnu"...Using host libthread_db library "/lib/i686/cmov/libthread_db.so.1".(gdb) r `python -c 'print "A"*(1024)+"/xfc/xff/xff/xff"+"/xf0/xff/xff/xff"+"AAAAABCDEFGH" '`Starting program: /home/sep/project/shellcode/basicheap `python -c 'print "A"*(1024)+"/xfc/xff/xff/xff"+"/xf0/xff/xff/xff"+"AAAAABCDEFGH" '`Failed to read a valid object file image from memory.buf=0x804a008, buf2=0x804a410 ;輸出兩個緩衝區的首地址*** glibc detected *** /home/sep/project/shellcode/basicheap: free(): invalid pointer: 0x0804a410 ***======= Backtrace: =========/lib/i686/cmov/libc.so.6[0xb7ecc764]/lib/i686/cmov/libc.so.6(cfree+0x96)[0xb7ece966]/home/sep/project/shellcode/basicheap[0x80484b2]/lib/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7e74455]/home/sep/project/shellcode/basicheap[0x80483b1]======= Memory map: ========08048000-08049000 r-xp 00000000 08:01 30559 /home/sep/project/shellcode/basicheap08049000-0804a000 rw-p 00000000 08:01 30559 /home/sep/project/shellcode/basicheap0804a000-0806b000 rw-p 0804a000 00:00 0 [heap]b7d00000-b7d21000 rw-p b7d00000 00:00 0 b7d21000-b7e00000 ---p b7d21000 00:00 0 b7e48000-b7e54000 r-xp 00000000 08:01 1318 /lib/libgcc_s.so.1b7e54000-b7e55000 rw-p 0000b000 08:01 1318 /lib/libgcc_s.so.1b7e5d000-b7e5e000 rw-p b7e5d000 00:00 0 b7e5e000-b7fb3000 r-xp 00000000 08:01 11668 /lib/i686/cmov/libc-2.7.sob7fb3000-b7fb4000 r--p 00155000 08:01 11668 /lib/i686/cmov/libc-2.7.sob7fb4000-b7fb6000 rw-p 00156000 08:01 11668 /lib/i686/cmov/libc-2.7.sob7fb6000-b7fb9000 rw-p b7fb6000 00:00 0 b7fc0000-b7fc3000 rw-p b7fc0000 00:00 0 b7fc3000-b7fc4000 r-xp b7fc3000 00:00 0 [vdso]b7fc4000-b7fde000 r-xp 00000000 08:01 11658 /lib/ld-2.7.sob7fde000-b7fe0000 rw-p 0001a000 08:01 11658 /lib/ld-2.7.sobf879000-bf88e000 rw-p bf879000 00:00 0 [stack]Program received signal SIGABRT, Aborted.0xb7fc3410 in ?? ()(gdb) x/xw 0x804a008-4 ;列印buf-4處的內容0x804a004: 0x00000409 ;buf的大小(gdb) x/xw 0x804a410-8 ;列印buf2-8處的內容0x804a408: 0xfffffffc ;原來應該為前一個塊的大小,現被改寫為0xfffffffc(gdb) x/xw 0x804a410-4 ;列印buf2-4處的內容0x804a40c: 0xfffffff0 ;原來應該為buf2的大小,現被改寫為0xfffffff0(gdb) x/xw 0x804a410 ;緩衝區buf2被填充了字串“AAAAABCDEFGH”0x804a410: 0x41414141 (gdb) x/xw 0x804a410+40x804a414: 0x44434241(gdb)
到這裡,在我作業系統上實驗的結果和原書的完全不同。由於缺少實驗的直觀結果,加上原書寫得非常簡略晦澀,很多內容我是一知半解的,在這裡就不繼續這部分了。