buflab實驗


一:准備工作

1,三個二進制文件

bufbomb:一個有緩沖區溢出漏洞的程序

makecookie:可以根據用戶不同的userid生成的唯一的cookieuserid不同,cookie不同,所要解決的方法就不同。

hex2raw:使編寫的緩沖區利用代碼的轉換為一個字符串的格式,只有經過轉換以后才可以輸入到getbuf

2,生成cookie的方法

3bufbomb函數原型及介紹

getbuf函數類似於gets函數,它是讀入一段字符串,字符串以\n或者eof表示結束,並把存儲起來,但是getbuf提供的緩沖區只有32個字符大小,但是getbuf本身又對輸入的字符是否超過緩沖區大小進行安全檢查,從而帶來了緩沖區溢出漏洞。

使用方法:

 

參數:

-u,確保不同的userid用不同的cookie

-n為了level4,棧基址隨機化模式的時候使用。

-s上傳到服務器進行打分

4,提交方式

 

5,提示

 

二:實驗

Level 0

實驗描述:

test函數調用getbuf函數,調用完以后還返回test函數,現在我們要做的是調用getbuf函數后,通過輸入我們的exploit,使得調用完以后不返回test函數了而是執行smoke函數。

test函數:

 

解決方法:

反匯編:

 

在反匯編結果中找到getbuf函數,smoke函數:

Getbuf函數:

 

畫出test函數調用getbuf函數的棧幀結構:

 

由反匯編結果可知,給輸入的字符串分配的空間是從%ebp-0x28開始的,換為10進制就是40個字節,而返回地址是在%ebp+0x4處,push %ebp本身又占了四個字節,所以結構為:

返回地址

4字節

Getbuf的,從%ebp到輸入字符串的空間為44個字節

 

Smoke函數:

 

由反匯編可得smoke函數的入口地址為0x08048e0a

綜上,我們需要做的就是把上面的44個字節隨意填滿(不要填換行),然后把原來的返回地址改為smoke函數的入口地址。0x0a是換行\nASCII值,所以不可以輸入,那么我們就輸入0x8048e0b來代替。

 

Level1

實驗描述:

test函數調用getbuf函數,調用完getbuf以后不返回getbuf的調用者test而是去執行fizz函數。fizz函數要求傳入參數,參數必須是cookie

fizz函數:

 

解決方法:

反匯編bufbomb找到fizz函數:

 

level0類似,通過上一題已經知道了棧幀結構,所以我們需要做的還是把那44個字節填滿,然后再填寫fizz函數的入口地址(0x08048daf)用來覆蓋原來的返回地址。

關鍵:找到fizz函數的參數從棧中的什么地方傳入的,然后我們把我們的cookie寫進這個fizz會獲取參數的地方。實參只在主調函數中有效,形參只在被調函數中有效,我們要做的就是修改實參,它的位置就是在返回地址的上面4個單位。而返回地址已經被我們破壞,會默認它上面四個字節為返回地址,然后再向上4個字節來取參數。

 

                                           執行leave

所以答案如下:

Level2

實驗描述:

執行完getbuf()后,不返回到test,而是去執行函數bang,但是區別是bang也要傳入參數,且參數是是一個全局變量。

bang函數:

 

解決方法:

反匯編:

 

bang函數的反匯編代碼

 

可以看到,bang、函數的入口地址為0x08048d52.

接下來要做的就是改變全局變量global_value的值,使他的值為cookie

由匯編代碼,第四行 mov 0x804d10c,%eax 可以知道,global_value存放的位置是0x804d10c

由此寫下匯編代碼:

首先把我們的cookie寫到全局變量的地址中,然后在把bang的入口地址入棧,通過ret指令來執行bang函數

 

然后把.s文件變成字節碼:

利用gdb調試找到我們的exploit的地址了,用我們的地址來覆蓋返回地址,從而執行我們的代碼。

 

所以答案如下:

 

Level3:

修改getbuf()函數的返回值(正常狀態為0x1)為你的cookie,然后讓函數正常返回到test.

解決方法:

函數調用棧,函數調用結束以后,棧被釋放,而返回結果會放在eax寄存器中。test不需知道調用的getbuf是怎么執行的,只需要到eax寄存器中去取返回值,所以可以在getbuf執行完以后,再把eax寄存器中的值動手腳修改為cookie

有關棧的恢復:需要兩個部分,一個是ebp一個是eip的地址,一個是恢復poptest的原ebp,所以在破壞之前,用gdb調試出來test的原ebp是多少記錄下來,恢復的時候在賦值給它。

gdb來調試得到ebp的值:

 

testebp0x556839f0

eip的地址,就是返回地址,也就是test中在callgetbuf函數的下一條指令的地址:

 

call的下一條指令的地址是0x8048e50

所以.s代碼:

 

 

%eax的值改為cookie

%ebp的恢復,改成0x556839f0

把下一條指令地址0x8048e50壓入

返回

.s文件變成字節碼:

 

所以答案是:

 

第一句 第三句 ret

填充0

%ebp恢復

自己的返回地址

 

Level4

調用getbufn函數,

其緩沖區大小為512個字節, 且每次棧的位置都會變化

nop只是執行eip自加1不進行其他的操作。在無法猜測的時候,只需要找到最大的地址。

解決方法:

反匯編,查看getbufn反匯編結果。

 

buf的首地址為-0x208%ebp)為十進制520個字節大小。

每次運行testnebp都不同,所以每次getbufn里面保存的testebp也是隨機的,但是棧頂的esp是不變的,我們就要找到每次隨機的ebpesp之間的關系來恢復ebp。我們先通過調試來看一下getbuf里面保存的ebp的值的隨機范圍為多少。

ebp的值              減去0x208buf的首地址

0x556839c0             0x556837b8

0x556839f0             0x556837e8

0x55683970             0x55683768

0x556839f0             0x556837e8

0x55683950             0x55683748

testn的反匯編代碼:

 

call getbufn的下一條指令的地址為0x8048ce2

此外,還可以看到,mov  %esp,%ebp 此時espebp相等

push  %ebx  此時ebp=esp+0x4

sub   $0x24,%esp 這個時候執行完后,ebp=esp+0x28,這就是espebp每次的變化關系,通過esp來恢復我們的每次的ebp

由此寫出以下匯編代碼:

 

.s文件變成字節碼:

509nop

15個字節碼,覆蓋篡改保存ebp

Buf首地址覆蓋返回地址,是可能的最大的首地址

三:總結

要想做明白這個實驗,就需要弄懂緩沖區溢出原理,以及堆棧的過程,函數調用的實現過程,函數傳參的底層實現等問題。並且,光理解原理也還遠遠不夠,還需要會應用。本次實驗的幾個level是逐步跟進的,一點一點的深入。Level0只需要理解原理,進行覆蓋地址,level1是修改參數,level2是修改全局變量,level3是恢復棧結構,level4是在level3第基礎上實現隨機化。在實驗的過程中還需要注意避免輸入換行對應的數字碼等等。

另外,在做本次實驗的過程中還遇到一個比較特別的問題,這個問題很隱蔽,難以發現,花費了很久才找到。在將自己寫的code4.s匯編的過程中,出現了機器碼生成錯誤的問題,mov指令對應的機器碼應該是b8,而在我的機器中,level4,卻生成了a1,至今還不知道為什么會出現這個問題。在與其他同學交流的過程中,發現其他同學也有過這種情況,是push指令對應的機器碼發生了錯誤。

截圖如下:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM