實驗環境:Win7 sp1 x64
實驗工具:vs2013 ImmunityDebug mona.py
本節內容需要了解兩個概念,一個是
Windows
異常處理機制(seh),還有一個就是 GS保護.
SEH:
(Structured Exception Hadnling)結構化異常處理 是(windows)作業系統提供給程式設計者的強有力的處理程式錯誤或異常的武器
C語言中通常透過_try catch 來實現
int main()
{
_try{
//可能出現異常崩潰的程式碼
}
_except(EXCEPTION_EXECUTE_HANDLER) {
//異常處理程式
}
return 0;
}
其實Windows在原始的程式棧前面新增了一個異常處理結構,該結構由一系列的異常處理連結串列組成,這條連結串列的起始點總是放在TIB(Thread Information Block)的第一個成員中,在x86計算機中儲存在FS:[0]暫存器中。連結串列的最後總是預設處理程式,這個預設處理程式的指標總是0xFFFFFFFF
GS保護機制:
Windows在VS7.0(Visual Studio 2003)及以後版本的VisualStudio中預設啟動了一個安全編譯選項——GS(針對緩衝區溢位時覆蓋函式返回地址這一特徵)
GS保護機制是在函式即將呼叫的時候向棧楨壓入一個DWORD的隨機值,同時也向.data段中存放一個Security Cookies,
1. 被壓入棧中的隨機值位於EBP之前.在.data段中的資料實現棧Cookies的校驗
2. 在函式返回之前,系統將會執行一個額外的安全驗證操作,被稱作SecurityCheck
3. Security當校驗發現棧Cookies和 .data的副本不吻合則表明發生溢位
4. 當檢測到棧中發生溢位時,系統接管異常,函式不會被正常返回,ret指令也不會被執行
5. 當棧中發生溢位時,Security Cookie將被首先淹沒,之後才是EBP和返回地址
GS保護機制的實現細節是
1 系統以.data段的第一個DWORD 作為Cookie的種子
2 每次程式執行時的Cookie的種子都不一樣,隨機性很強
3 棧楨初始化完畢後用EBP異或種子,作為當前函式的Cookie,以此區別不同函式,增強Cookie的隨機性
4 在函式返回前,用EBP異或還原出Cookie種子
繞過GS安全保護的方案
1) 透過覆蓋SEH連結串列來阻止系統接管異常處理.
2) 透過改寫C++虛表指標來控制程式流程 #msvcrt 採用程式堆後失效
3) 用一些未開啟GS安全保護的函式進行溢位(可能是關鍵字保護) ||小於四位元組的Buf
程式很簡單,讀取檔案來還原溢位場景,我將print函式放在了ReadFile下面,可以看到在ShowFileInfo中Printf函式下面有一個Security_Check_Cookie 這就是我們的GS緩衝區檢測機制的這個函式,工程專案是realse版本的,在專案屬性只開啟GS。關閉 最佳化選項,dep,aslr,safeseh(vs專案屬性選擇配置屬性->連結器->命令列填寫“/SAFESEH:NO ”)
我們可以試試如果和上次一樣覆蓋掉返回地址當執行到Security_Check_Cookie的時候,他會檢查棧Cookies和 .data的副本,這時候GS就分發系統異常處理請求然後就由系統接管處理你這個異常 我們可以先用mona外掛檢視程式當前seh連結串列
這個地址指向的就是Pointer to next SEHrecord 下面的SE hander是ntdll中的系統接管處理。
我們現在的思路就是覆蓋掉這個SEH異常處理連結串列SEH handler 還需要18*4個位元組才能覆蓋掉我們用mona生成一個1024+72位元組的字串.命令列引數重新載入不要忘了Imdebug加引數啟動,現在可以看到已經覆蓋掉了SEH handler了
我們繼續單步到Printf 看到了引數已經被我們覆蓋掉了
接下來就是尋找個跳板了,seh通常利用的是pop pop ret 一旦進入異常處理,就會把Pointer to next SEH的這個地址壓入棧中進行系統處理,透過pop pop然後這個地址ret到我們的eip中,因為Pointer to Next..是可控的所以我們控制這個地址來控制eip,然後就是可以透過mona來找pop pop ret 來覆蓋Se handler
找到這一行PAGE_EXECUTE_READ,後面的保護機制都是false
Address=00401804 Message= 0x00401804 : pop edi # pop esi # ret | startnull,ascii {PAGE_EXECUTE_READ} 找到一個ppr的地址我們開啟十六進位制編輯器以小端儲存將尾部四個位元組覆蓋掉
我們已經知道了當執行完我們的pop popret 會把上一個函式的地址彈到eip中我們這個地址也是我們可以控制的. 我們在Pointer to next SEH record地址做一個跳板跳轉至我們的shellcode中但是當前地址已經離棧底很近了,所以我們要把shellcode放置在我們的buf中,向上跳。但是這裡還有一個細節就是當你向上跳轉的時候是jmp一個負的地址 那麼這條jmp XXXX 這條指令就會撐爆當前的這四個位元組空間,覆蓋掉了後面的se handler資料,所以我們要先在下面找到一個比較近的一塊空區域然後在那塊區域的地址上寫上我們jmp shellcode的指令.我們選擇 0018FF8O這個地址
現在我們加長我們的文字,可以看到下圖中, 現在搜尋我們的pop pop ret指令,反彙編視窗ctrl+G 輸入00401804在然後再pop上下斷點,shitf+f9執行觀察eip,當執行完ret指令後的當前指令修改為jmp 0018FF80
上圖是覆蓋Pointer to NextRecord為jmp 0018FF80為向下跳轉,單步一步,然後這裡需要一個向上跳轉的jmp這裡就用我們文字的第一個位元組作為shellcode起始位置0018FF80 jmp 0018FF80的二進位制是 E9 B3 FB FF
此時0018FF80地址處的指令就是jmp 0018FF80 單步就到了我們的buf頭了
可以看到我們覆蓋的資料了,二進位制是這樣的
接下來我們就在buf中扣一段shellcode在0018FF80這裡寫指令跳到它的首地址直接用第一個位元組的地址 0018FB38
總結下過程:
1. 找到SEH處理函式, 尋找跳板pop pop ret來覆蓋掉ntdll中的 seHandler
2. 構造跳板跳向shellcode,位元組長度問題可以在seHandler下方找跳板間接找跳板跳向 shellcode