天天看點

ASLR/DEP繞過技術概覽

<a target="_blank" href="http://bbs.pediy.com/showthread.php?t=101217">http://bbs.pediy.com/showthread.php?t=101217</a>

by WinsOn@Cybersword

     在經典的棧溢出模型中,通過覆寫函數的傳回位址來達到控制程式執行流程(EIP寄存器),通常将傳回位址覆寫為0x7FFA4512,這個位址是一條JMP ESP指令,在函數傳回時就會跳轉到這個位址去執行,也就是執行JMP ESP,而此時ESP剛好指向我們在棧上布置的Shellcode,于是就執行了Shellcode。

<a target="_blank" href="http://cybersword.net/exploit/558.html/attachment/422l1"></a>

         之是以棧上的資料能被執行,是因為早期作業系統沒有區分資料和代碼,EIP指向哪裡就去哪裡執行。

         當引入DEP(Data Execution Prevention 資料執行保護)之後,堆、棧上的記憶體頁屬性預設不再具有可執行屬性,此時如果想直接在棧上執行資料,就會發生錯誤:

<a target="_blank" href="http://cybersword.net/exploit/558.html/attachment/422l2"></a>

         常用的繞過DEP的技術室ROP(Return Oriented Programming,早期也叫Ret2Libc),ROP由一系列的 Gadget組成。所謂ROP Gadget,就是一系列以retn結尾的指令,所有的這些Gadget組合起來就能完成特定的任務,比如調用VirtualProtect給指定的記憶體塊添加可執行屬性。

<a target="_blank" href="http://cybersword.net/exploit/558.html/attachment/422l4"></a>

         為了達到穩定利用的目的,要求選取的Gadget的位址是固定的,無論是什麼時候,其指向的都是我們想要的指令。這裡引入了ASLR的概念。

         ASLR全稱Address Space Layout Randomization,即位址空間格局随機化。ASLR使得加載程式時不再使用固定的加載基位址加載。該技術需要作業系統以及應用程式的雙重支援,ASLR才能發揮正常的作用。支援ASLR的程式在PE頭中會設定IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE辨別表明其支援ASLR。

<a target="_blank" href="http://cybersword.net/exploit/558.html/attachment/422l5"></a>

         通過Visual Studio對項目屬性的配置,可以使二進制檔案支援ASLR:

<a target="_blank" href="http://cybersword.net/exploit/558.html/attachment/422l6"></a>

ASLR主要影響一下幾個部分,分别為:

1. 子產品随機化

         系統将PE檔案映射到記憶體時,對其加載基位址進行随機化處理,這個位址在系統啟動時确定,系統重新開機後會變化。如圖所示,用Ollydbg加載一個應用程式,檢視子產品清單:

<a target="_blank" href="http://cybersword.net/exploit/558.html/attachment/422l7"></a>

         重新開機作業系統後再次檢視,發現基位址全改變了:

<a target="_blank" href="http://cybersword.net/exploit/558.html/attachment/422l8"></a>

2. 堆棧随機化

         每次程式加載後,其記憶體空間中堆、棧的基址都會發生變化。于是記憶體中的變量所在的位址也會發生變化。

3. PEB/TEB随機化

         從Windows XP SP2開始,PEB、TEB的位址不再固定不變。

不過,幾乎很少簡單有人用固定的位址去擷取PEB、TEB指針,而是通過fs寄存器進行定位。

TEB可以用FS : [18h]擷取

PEB可以從TEB偏移30h處擷取

常用的繞過ASLR的方法有:

1. 攻擊未啟用ASLR的子產品

         雖然有映像随機化,但有可能程序中存在未啟用ASLR的子產品。 前面提到的ROP技術要求從一個固定的位址擷取Gadget,如果程序中存在未啟用ASLR的子產品,那麼就可以從那個子產品擷取Gadget了。 使用OD的OllyFindAddr插件可以快速找到程序空間中未啟用ASLR的子產品。

2. 堆噴射(HeapSpray)技術

         雖然有堆棧随機化,不過HeapSpray技術将ShellCode布局到0x0C0C0C0C(或者其他指定的位址上,通常這個位址要比較大),并不會受堆棧随機化的影響。 其實,HeapSpray中使用ROP繞過DEP的時候,就使用了前面提到的“攻擊未啟用ASLR的子產品”。 隻是,HeapSpray把ShellCode布局在堆上。

3. 覆寫部分傳回位址

         映像随機化中,雖然子產品的加載基位址發生變化,但是各子產品的入口點位址的低位字不變,隻有高位字進行了随機化處理。

對于位址0×12345678,其中5678部分是固定的,如果存在緩沖區溢出,可以通過memcpy對後兩個位元組進行覆寫,可以将其設定為0×12340000 ~ 0x1234FFFF中的任意一個值。

         如果通過strcpy進行覆寫,因為strcpy會複制末尾的結束符0×00,那麼可以将0×12345678覆寫為0×12345600,或者0×12340001 ~ 0x123400FF。

         部分傳回位址覆寫,可以使得覆寫後的位址相對于基位址的距離是固定的,可以從基位址附近找可以利用的跳轉指令。

         這種方法的通用性不是很強,因為覆寫傳回位址時棧上的Cookie會被破壞。不過具體問題具體分析,為了繞過作業系統的安全保護機制需要考慮各種各樣的情況。

4. Java Applet Spray

         Java Applet中動态申請的記憶體空間具有可執行屬性(PAGE_EXECUTE_READWRITE),類似HeapSpray技術,可以在固定的位址上配置設定滑闆指令(如NOP)和ShellCode,然後跳轉到那個位址上面去執行。 和正常的HeapSpray不同,Applet申請空間的上限為100MB,而正常的HeapSpray可以達到1GB。

5. JIT Spray

         JIT (Just In Time Compilation) 即時編譯,也就是解釋器(比如Python解釋器)。

主要思想是将 ActionScript代碼中進行大量的XOR操作。然後編譯成位元組碼,并且多次更新到Flash VM中,這樣它會建立很多帶有惡意Xor操作的記憶體塊。例如,一個序列為:

var y=(0×11223344^0×44332211^0×4433221);

正常情況下被解釋器解釋為:

<a target="_blank" href="http://cybersword.net/exploit/558.html/attachment/422l7-2"></a>

如果非正常的跳轉到中間某一個位元組開始執行代碼,結果就是另一番景象了:

<a target="_blank" href="http://cybersword.net/exploit/558.html/attachment/422l8-2"></a>

         關于JIT的詳細介紹,可以參考Pointer Inference and JIT Spraying以及Writing JIT-Spray shellcode for fun and profit,文章末尾會給對外連結接。

6. Tombkeeper在CanSecWest 2013上提出的基于SharedUserData的方法

         從Windows NT 4到Windows 8,SharedUserData的位置一直固定在位址0x7ffe0000上。 從WRK源代碼中nti386.h以及ntamd64.h可以看出:

#define MM_SHARED_USER_DATA_VA  0x7FFE0000

在x86 Windows上,通過Windbg,可以看到:

0:001&gt; dt _KUSER_SHARED_DATA SystemCall 0x7ffe0000

ntdll!_KUSER_SHARED_DATA

+0×300 SystemCall : 0x774364f0

0x7ffe0300總是指向KiFastSystemCall

0:001&gt; uf poi(0x7ffe0300)

ntdll!KiFastSystemCall:

774364f0 8bd4            mov     edx,esp

774364f2 0f34            sysenter

774364f4 c3              ret

         反彙編NtUserLockWorkStation函數,發現其就是通過7ffe0300進入核心的:

0:001&gt; uf USER32!NtUserLockWorkStation

USER32!NtUserLockWorkStation:

75f70fad b8e6110000      mov     eax,11E6h

75f70fb2 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)

75f70fb7 ff12            call    dword ptr [edx]

75f70fb9 c3              ret

         其中11E6是NtUserLockWorkStation的服務号(ShadowSSDT中0x01E6的服務),通過Xuetr可以看到:

<a target="_blank" href="http://cybersword.net/exploit/558.html/attachment/422l9"></a>

         這樣,在觸發漏洞前合理布局寄存器内容,用函數在系統服務(SSDT / Shadow SSDT)中服務号填充EAX寄存器,然後讓EIP跳轉到對應的地方去執行,就可以調用指定的函數了。但是也存在很大的局限性:僅僅工作于x86 Windows上;幾乎無法調用有參數的函數。

         64位Windows系統上0x7ffe0350總是指向函數ntdll!LdrHotPatchRoutine。

HotPatchBuffer結構體的定義如下:

struct HotPatchBuffer {

ULONG   NotSoSure01; // &amp; 0×20000000  != 0

ULONG   NotSoSure02;

USHORT  PatcherNameOffset;   // 結構體相對偏移位址

USHORT  PatcherNameLen;

USHORT  PatcheeNameOffset;

USHORT  PatcheeNameLen;

USHORT  UnknownNameOffset;

USHORT  UnknownNameLen

};

LdrHotPatchRoutine調用方式:

void LdrHotPatchRoutine (struct  *HotPatchBuffer);

         在觸發漏洞前合理布局寄存器内容,合理填充HotPatchBuffer 結構體的内容,然後調用LdrHotPatchRoutine。

         如果是網頁挂馬,可以指定從遠端位址加載一個DLL檔案;

         如果已經經過其他方法把DLL打包發送給受害者,執行本地加載DLL即可。

         此方法通常需要HeapSpray協助布局記憶體資料;且需要檔案共享伺服器存放惡意DLL;隻工作于64位系統上的32位應用程式;不适用于Windows 8(已經被修補)。

參考資料與擴充閱讀:

《0Day安全*軟體漏洞分析技術》第二版,王清

<a target="_blank" href="http://www.semantiscope.com/research/BHDC2010/BHDC-2010-Paper.pdf">Pointer Inference and JIT Spraying</a>

<a target="_blank" href="http://cansecwest.com/slides/2013/DEP-ASLR%20bypass%20without%20ROP-JIT.pdf">DEP-ASLR bypass without ROP-JIT</a>

繼續閱讀