1. 程式人生 > >棧溢位漏洞原理及基本利用(ret2addr,ret2arg)

棧溢位漏洞原理及基本利用(ret2addr,ret2arg)

菜雞總結下,方便複習。

ret2addr和ret2arg這兩種利用手法在《黑手緩衝區溢位教程》裡有所提及。這兩種只是基本的利用手法,如果開啟了NX(堆疊程式碼不可執行)或者ASLR就無用武之地了,需要更高階的利用手法,例如ret2libc,ret2plt,和ROP等高階利用手法,這篇筆記就只說下基本的利用手法及漏洞原理。

瞭解棧溢位漏洞,需要對彙編裡的call指令(相當於push eip和jmp 函式首地址 ),ret指令(相當於pop eip),函式的呼叫過程有所瞭解。《加密與解密》的逆向分析技術篇中函式部分說的很清楚。下面也會有所介紹。

簡介

棧溢位是向棧中寫入超過原本長度限制的資料,使棧中的其他資料被覆蓋,常見的是覆蓋棧中返回地址,改變程式的執行流程。
棧溢位漏洞成立需要兩個條件,其一是:有向棧中寫入資料的行為,另一個是:使用了gets,strcpy,strcat等 不限制資料輸入長度或者不檢查陣列長度的函式。

預備知識

函式的呼叫和返回等過程都是在棧中完成的,棧中也儲存著區域性變數和函式的引數。
說之前先複習下函式呼叫的知識。
呼叫函式前,如果函式有引數,需要先將引數傳入棧中(值傳遞本質是將變數複製一份壓入堆疊,而地址傳遞,是將變數的地址直接壓入棧中,通過加上中括號[],直接訪問)
一般情況下引數的入棧是從右往左依次入棧的(cdecl呼叫約定,stdcall,fastcall等)。
值傳遞模板(非fastcall呼叫約定,fastcall呼叫約定前兩個引數會直接用暫存器,不用堆疊,後面的引數仍然用堆疊傳參)如下:

mov eax,dword ptr [EBP-xxx]  //push 後無法直接接記憶體單元,需要先傳給暫存器
push eax //壓入堆疊傳參
mov eax,dword ptr [xxx]
push eax
.....

傳完引數後會call 函式(call 會將呼叫函式即母函式的call指令的下一條要執行的指令的地址壓入堆疊,然後再跳轉到函式程式碼段的首地址,這和cpu執行指令的過程有關,cpu執行指令的過程如下:1。讀取EIP指向的指令,將其放入指令緩衝器,2。EIP指向下一條指令,3。執行指令緩衝器裡的指令,然後返回 1。)

call 函式程式碼首地址

函式內容有個模板(下面為debug版,release版會有所不同)如下:

push ebp
mov ebp,esp
//這裡提升堆疊
sub esp,0x40  
//這裡是開闢緩衝區,不同編譯器開闢的緩衝區大小不同(會根據你所用的變數的多少和大小來開闢)。
push edi
push esi
push ebx
//保留現場
lea edi, dword ptr ss:[ebp-0x40]
mov ecx,0x10
mov eax,0xcccccccc
rep stos dowrd ptr es:[edi]
//填充緩衝區(清除垃圾資料),用於存放區域性變數
----------------
這裡是寫函式的功能
----------------
    pop ebx
    pop esi
    pop edi
    //恢復現場
    mov esp,ebp
    pop ebp
    ret

執行完call函式後的堆疊圖如下(下面是高址,上面是低址):
在這裡插入圖片描述
此時,EBP的位置就很關鍵了,使用EBP+xxx,可以訪問到傳入棧中的引數,向上EBP-xxx,可以訪問區域性變數。
當函式的程式碼執行到

mov esp,ebp
pop ebp
ret

此時ESP指向棧中的返回地址,執行ret執行(相當於pop eip)後就會將返回地址賦值給EIP,函式就執行完畢了,此時EIP重新指向母函式。

漏洞原理

漏洞的關鍵就是利用gets,strcpy,strcat 等函式,輸入或者拼接超過字元陣列原先規定的長度的字串
例如原先定義的字元陣列 a[8],你使用gets函式,輸入了"AAAAAAAAAAAABBBB",原先編譯器編譯成彙編時並沒有預留足夠的空間,例如前面函式模板中的 sub esp,0x40,他只開闢了0x40的空間,那麼多輸入的字串就會將堆疊中的其他內容覆蓋掉。
呼叫gets前的堆疊圖如下:
在這裡插入圖片描述
呼叫gets,輸入"AAAAAAAAAAAABBBB"後的堆疊圖如下:
在這裡插入圖片描述
起始EBP值,和返回地址以及後面的堆疊空間都可以被輸入的字串所覆蓋,這就是緩衝區溢位漏洞。

基本利用之ret2addr
ret2addr就是 return to address ,就是將堆疊裡的返回地址覆蓋為你所編寫的shellcode的首地址上,ret2addr特指的是緩衝區裡的shellcode。
利用緩衝區溢位後的堆疊圖如下:
在這裡插入圖片描述
當ret後 ,EIP就會指向shellcode的首地址,這樣就能執行你的shellcode了。如何找到shellcode的首地址,在下篇筆記再提及。

基本利用之ret2arg
與ret2addr不同的之處是 shellcode在返回地址的下面,而不是在棧幀裡。同時返回地址被覆蓋為JMP ESP這個指令的首地址。利用後的堆疊圖如下:
在這裡插入圖片描述
原來的返回地址被覆蓋為JMP ESP指令的首地址。
因為ret後 ,ESP加4,則此時ESP指向shellcode的首地址,而EIP指向了JMP ESP指令的首地址,執行JMP ESP後,EIP就指向了shellcode的首地址,這樣就會執行你的shellcode了。