Windows堆棧溢出利用的七種方式

Windows下的堆棧溢出攻擊和*nix下的,原理基本相同。但是,由於windows用戶進程地址空間分配和堆棧處理有其獨立的特點,導致了windows 環境下堆棧溢出攻擊時,使用的堆棧溢出字元串,與unix下的,區別很大。另外,windows的版本也導致了windows下的exploit不具有通用性。windows版本不同,而exploit使用了很多動態鏈接庫裡面的庫函數,其地址都是與dll的版本有關係的。不同的dll版本,裡面的庫函數的偏移地址就可能(注意:是可能)不同。因為windows的patch天天有,他的一些dll就更新很快。甚至可能不同語言版本的windows,其核心dll的版本都不同。用戶的dll一變更,那麼,我們的exploit裡面的shellcode就要重新寫。

為了解決這個問題,我想我們可以盡量減少固定地址的使用。即,使用GetProcAddress來獲得我們將使用的每一個系統函數,當然這就大大加長了我們的shellcode。但是,這也無法消除對kernel32.dll的中LoadLibrary和GetProcAddress的地址的直接引用,因為這兩個是shellcode中最基本的函數,自然就導致了對kernel32.dll版本的依賴。

一、利用VEH

向量化異常處理(VEH,Vectored Exception Handling)最初是在XP中公布,它的優先順序高於SEH,並且VEH是存在堆中的,它是你在代碼中明確添加的,並不伴隨try/catch之類的語句而產生,它也需要通過API(AddVectoredExceptionHandler)來註冊回調函數,並可註冊多個VEH,各個VEH結構體之間串成雙向鏈表,因此比SEH多了一個前指針。每一個VEH結構均存儲在堆上,其結構如下:

struct _VECTORED_EXCEPTION_NODE{ DWORD m_pNextNode; //指向下一個_VECTORED_EXCEPION_NODE結構,因此可用偽造的指針來覆蓋它 DWORD m_pPreviousNode; //指向上一個_VECTORED_EXCEPION_NODE結構 PVOID m_pfnVectoredHandler; //異常處理函數}

負責分發_VECTORED_EXCEPION_NODE的代碼如下:

77F7F49E 8B35 1032FC77 MOV ESI,DWORD PTR DS:[77FC3210] ;賦值後ESI指向_VECTORED_EXCEPION_NODE結構,即m_pNextNode77F7F4A4 EB 0E JMP SHORT ntdll.77F7F4B477F7F4A6 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8]77F7F4A9 50 PUSH EAX77F7F4AA FF56 08 CALL DWORD PTR DS:[ESI+8] ;可用shellcode-0x8去覆蓋m_pNextNode指針

接著我們在堆中確定shellcode地址,可先用垃圾字元去填充,比如0x41,然後在堆中搜索它。當發生堆溢出時,堆塊的前向指針和後向指針就會被篡改,比如異常出現在:

MOV DWORD PTR DS:[ECX],EAX ;EAX = Flink = 寫入的內容MOV DWORD PTR DS:[EAX+4],ECX ;ECX = Blink = 寫入的地址

那麼我們就可以用m_pNextNode-4來覆蓋ECX,然後用shellcode-8去覆蓋EAX。關於m_pNextNode指針的獲取,我們只需在觸發異常後,按shift+F7步過異常即可找到此指針,比如以下代碼:

77F60C2C BF 1032FC77 MOV EDI,ntdll.77FC3210 ;m_pNextNode指針77F60C31 393D 1032FC77 CMP DWORD PTR DS:[77FC3210],EDI77F60C37 0F85 48E80100 JNZ ntdll.77F7F485

關於EAX和ECX的偏移地址可通過pattern_create和pattern_offset來獲取。這樣當觸發異常時就會調用VEH,而此時下一個VEH結構即是我們特意構造的shellcode,這樣我們的惡意代碼就有可以被執行了。

二、利用UEF

系統默認異常處理函數(UEF,Unhandler Exception Filter)是系統處理異常時最後調用的一個異常處理常式,在堆溢出中,只需將這一地址覆蓋為我們的shellcode地址即可。獲取UEF地址的方法可以通過查看SetUnhandledExceptionFilter()的代碼來定位,接著再找到操作UnhandledExceptionFilter指針的MOV指令,比如以下代碼:

77E93114 A1 B473ED77 MOV EAX,DWORD PTR DS:[77ED73B4] ;UnhandledExceptionFilter指針77E93119 3BC6 CMP EAX,ESI77E9311B 74 15 JE SHORT kernel32.77E9313277E9311D 57 PUSH EDI77E9311E FFD0 CALL EAX

現在我們只需找到shellcode地址,或者看是否有某一寄存器reg剛好指向shellcode或其附近,然後用shellcode地址或者類似call [reg + offset]的指令地址來覆蓋UnhandledExceptionFilter指針,比較常用的指令如:

call dword ptr ds:[edi+74]call dword ptr ds:[esi+4c]

其它eax,ebx也有可能指向堆,亦可作為跳板來用。

三、利用PEB

由於當UEF被調用後,它最終會調用ExitProcess()來結束程序,而它在清理現場時需要進入臨界區以同步線程,因此會調用RtlEnterCriticalSection()t和RtlLeaveCriticalSection()。ExitProcess是通過存放在PEB中的一對指針來調用這兩個函數的,如果能夠利用DWORD SHOOT把這對指針篡改成shellcode入口地址,那麼在程序結束調用ExitProcess()就會執行shellcode。下面是在Windows XP SP3下PEB的情況:

0:000> dt _PEBntdll!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x003 SpareBool : UChar +0x004 Mutant : Ptr32 Void +0x008 ImageBaseAddress : Ptr32 Void +0x00c Ldr : Ptr32 _PEB_LDR_DATA +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS +0x014 SubSystemData : Ptr32 Void +0x018 ProcessHeap : Ptr32 Void +0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION //根據此指針來間接進入臨界區 +0x020 FastPebLockRoutine : Ptr32 Void +0x024 FastPebUnlockRoutine : Ptr32 Void +0x028 EnvironmentUpdateCount : Uint4B ……

但在WinXP SP2之後微軟就加入了PEB random保護,不再使用固定的PEB基址,而使用具有一定隨機性的PEB基址,以提高利用的難度。

四、Heap Spary

Heap Spary技術最早是由SkyLined於2004年為IE的iframe漏洞寫的exploit而使用到新技術,目前主要作為瀏覽器攻擊的經典方法,被大量網馬所使用。Heap Spary技術是使用js分配內存,所分配的內存均放入堆中,然後用各帶有shellcode的堆塊去覆蓋一大片內存地址,Javascript分配內存從低址向高址分配,申請的內存空間超出了200M,即大於了0x0C0C0C0C時,0x0C0C0C0C就會被覆蓋掉,因此只要讓IE執行到0x0C0C0C0C(有時也會用0x0D0D0D0D這一地址)就可以執行shellcode,這些堆塊可以用NOP + shellcode 來填充,每塊堆構造1M大小即可,當然這也不是固定。這樣當nop區域命中0x0c0c0c0c時,就可執行在其後面的shellcode。下面是一個簡單模板:

<html><body><object classid="clsid:6BE52E1D-E586-474F-A6E2-1A85A9B4D9FB" id="target"></object><script>Var shellcode="u68fcu7473u6668u6961……u53c4u5050uff53ufc57uff53uf857";var nop="u9090u9090";while (nop.length <= 0x100000/2){ nop+=nop;}nop = nop.substring(0,0x100000/2-32/2-4/2-shellcode.length-2/2);var slide = new Array();for ( var i=0; i<200; i++){ slide[i] = nop + shellcode;}var s= ;while (s.length < 748){ s+="x0c";}target.Overflow(s);</script></body></html>

五、Bitmap Flipping Attack

在 Heap Management 結構中包含有Freelist Bitmap標誌位,它是一個4位元組的DWORD值,當對應的FreeList[n]被填充時,bitmap就將被設置。當請求分配堆塊時,它會先搜索與之大小合適的FreeList[n],然後檢測對應的bitmap,若上面為0就表示上面是塊未使用的空閑塊,則對應的FreeList[n]將用於分配配塊,接著返回到對應的請求塊FreeList[n]指向的地址。因此如果我們可以控制Bitmap,並能夠覆蓋freelist[n]中的值,那麼我們就可以通過它來執行任意代碼。

六、Heap Cache Attack

Heap Cache主要用於降低頻繁遍歷FreeList[0]的性能消耗,以提高性能。它主要是為FreeList[0]中的堆塊創建擴展索引,更重要的是,Heap Manager並沒有將任何空閑塊移動緩存中,這些空閑塊一直保存在FreeList[0]中,但緩存中保存著一些指針,它們指向FreeList[0]中的某些節點,以此來提升訪問FreeList[0]的速度。堆緩存是一個bucket數組,每一個bucket包含有intptr_t位元組用於存儲大小,還有一個NULL指針或者FreeList[0]上的堆塊指針。默認情況下,數組包括有896個bucket,其大小在1024和8192之間,但大小是可配置的,我們可指定最大的緩存索引號。在堆緩存攻擊技術中又存在各種利用方式,比如De-synchronization Attack(通過覆寫堆塊頭信息中的大小域,使每次請求同等大小堆塊時都指向同一塊已經使用的內存塊,如果攻擊者可能控制這一內存塊中的內容,就有可能導致任意代碼執行),Insert Attack、Existing Attacks、Malicious Cache Entry Attack……這些方法有很大的局限性,在實際運用上很難派上用場,若想獲取更多關於這方面的信息可以參見BlackHat USA 2009上面的文章《Practical Windows XP/2003 Heap Exploitation》。

七、Bitmap XOR Attack

Bitmap XOR Attack 是通過異或操作來更改 freelist bitmap,如果系統嘗試清除這一錯誤的標誌位,那麼就可能從一個空閑位(free bit)切換到設置位(set bit),進而實現類似上文提到的bitmap attack。這個可以通過篡改堆塊頭信息中的大小域(CurSize),使其小於0x80,接著再使對應堆塊中的前向指向與後向指針相等(flink == blink),並保證其指向的地址是可讀的。更多信息可以參見BlackHat USA 2009上面的文章《Practical Windows XP/2003 Heap Exploitation》。後面這幾種方法實際利用價值不大,權當了解,學習思路更為重要。

原文出自公眾號elknot_cyber

推薦閱讀:

俄羅斯互聯網巨頭2500萬用戶數據泄露
花無涯帶你走進黑客之 小白入門 第一彈
Powershell攻擊指南——黑客後滲透之道系列之基礎篇
2017 NSA網路武器庫泄露工具總結分析
國內外有哪些漏洞信息發布平台?

TAG:网络安全 | 黑客Hacker | 堆栈溢出 |