異常中的異常——藉助系統異常處理特例實現匪夷所思的漏洞利用
Author:[email protected]
0x00 簡介
記憶體的讀、寫、執行屬性是系統安全最重要的機制之一。通常,如果要改寫記憶體中的資料,必須先確保這塊記憶體具有可寫屬性,如果要執行一塊記憶體中的程式碼,必須先確保這塊記憶體具有可執行屬性,否則就會引發異常。然而,Windows系統的異常處理流程中存在一些小小的特例,藉助這些特例,就可以知其不可寫而寫,知其不可執行而執行。
0x01 直接改寫只讀記憶體
我在CanSecWest 2014的演講《ROPs are for the 99%》中介紹了一種有趣的IE瀏覽器漏洞利用技術:透過修改JavaScript物件中的某些標誌,從而關閉安全模式,讓IE可以載入類似WScript.Shell這樣的危險物件,從而執行任意程式碼而完全無需考慮DEP。
不過,修改SafeMode標誌並非是讓IE可以載入危險物件的唯一方法。
IE瀏覽器的某些介面實際上是用HTML實現的,這些HTML通常儲存在ieframe.dll的資源中,例如:列印預覽是res://ieframe.dll/preview.dlg
,整理收藏夾是res://ieframe.dll/orgfav.dlg
,頁面屬性則是res://ieframe.dll/docppg.ppg
。
IE瀏覽器會為這些HTML建立獨立的渲染例項,以及獨立的JavaScript引擎例項。而為這些HTML建立的JavaScript引擎例項中,SafeMode本身就是關閉的。
所以,只需將JavaScript程式碼插入到ieframe.dll的資源中,然後觸發IE的相應功能,被插入的程式碼就會被當作IE自身的功能程式碼在SafeMode關閉的JavaScript例項下執行。
不過,PE的資源節是隻讀的,如果試圖用某個能對任意地址進行寫入的漏洞直接改寫ieframe.dll的資源,會觸發寫訪問違例:
#!bash
eax=00000041 ebx=1e2e31b0 ecx=00000000 edx=00000083 esi=1e2e31b0 edi=68b77fe5
eip=69c6585f esp=0363ac00 ebp=0363ac84 iopl=0 nv up ei pl nz na pe cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010207
jscript9!Js::JavascriptOperators::OP_SetElementI+0x117:
69c6585f 88040f mov byte ptr [edi+ecx],al ds:002b:68b77fe5=76
0:008> !exchain
0363b0f0: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+1570 (69b421d1)
0363b648: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+1570 (69b421d1)
0363bab8: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+1570 (69b421d1)
0363bb78: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+28c0 (69c71564)
0363bbc0: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+2898 (69c7150f)
0363bc44: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+276a (69d0dedd)
0363c588: MSHTML!_except_handler4+0 (66495fa4)
CRT scope 0, filter: MSHTML! ... Omitted... (6652bbe8)
func: MSHTML!... Omitted... (6652bbf1)
0363c62c: user32!_except_handler4+0 (7569a61e)
CRT scope 0, func: user32!UserCallWinProcCheckWow+123 (75664456)
0363c68c: user32!_except_handler4+0 (7569a61e)
CRT scope 0, filter: user32!DispatchMessageWorker+15e (756659b7)
func: user32!DispatchMessageWorker+171 (756659ca)
0363f9a8: ntdll!_except_handler4+0 (776a71f5)
CRT scope 0, filter: ntdll!__RtlUserThreadStart+2e (776a74d0)
func: ntdll!__RtlUserThreadStart+63 (776a90eb)
0363f9c8: ntdll!FinalExceptionHandler+0 (776f7428)
在上面的異常處理鏈中,mshtml.dll中的異常處理函式最終會呼叫kernel32!RaiseFailFastException()。如果g_fFailFastHandlerDisabled標誌是false,就會終止當前程式:
#!cpp
int __thiscall RaiseFailFastExceptionFilter(int this) {
signed int **v1; // [email protected]
CONTEXT *v2; // [email protected]
signed int v3; // [email protected]
UINT v4; // [email protected]
HANDLE v5; // [email protected]
v1 = (signed int **)this;
if ( !g_fFailFastHandlerDisabled )
{
v2 = *(CONTEXT **)(this + 4);
g_fFailFastHandlerDisabled = 1;
RaiseFailFastException(*(PEXCEPTION_RECORD *)this, v2, 2u);
v3 = 1653;
if ( *v1 )
v3 = **v1;
v4 = v3;
v5 = GetCurrentProcess();
TerminateProcess(v5, v4);
}
return 0;
}
但是,如果g_fFailFastHandlerDisabled標誌為true,異常處理鏈就會執行到kernel32!UnhandledExceptionFilter(),並最終執行kernel32!CheckForReadOnlyResourceFilter():
#!cpp
int __stdcall CheckForReadOnlyResourceFilter(int a1) {
int result; // [email protected]
if ( BasepAllowResourceConversion )
result = CheckForReadOnlyResource(a1, 0);
else
result = 0;
return result;
}
如果BasepAllowResourceConversion 也為true,CheckForReadOnlyResource()函式就會將試圖寫入的那個記憶體分頁的屬性設為可寫,然後正常返回。
也就是說,如果先將g_fFailFastHandlerDisabled和BasepAllowResourceConversion這兩個標誌改寫為true,之後就可以直接修改ieframe.dll的資源,而不必擔心其只讀屬性的問題,作業系統會處理好一切。
另外還有個小問題。如果像上面所說的那樣觸發了一次CheckForReadOnlyResource()中的修改記憶體屬性的操作,記憶體屬性的RegionSize也會變成一個記憶體分頁的大小,通常是0x1000。而IE在以ieframe.dll中的HTML資源建立渲染例項前,mshtml!GetResource()函式會檢查資源所在記憶體的RegionSize屬性,如果該屬性小於資源的大小,就會返回失敗。然而,只需將要改寫的資源從頭到尾全部改寫一遍, RegionSize就會相應變大,從而繞過這個檢查。
這樣,利用Windows寫訪問異常對PE檔案資源節開的綠燈,就可以寫出非常奇妙的漏洞利用程式碼。
0x02 直接執行不可執行記憶體
我在VARA 2009的演講《漏洞挖掘中的時間維度》中介紹了一種較為少見的模組地址釋放後重用漏洞。比如一個程式中執行緒A呼叫了模組X的函式,模組X又呼叫了模組Y的函式。模組Y的函式由於某種原因,耗時比較長才能返回。在它返回前,如能讓執行緒B將模組X釋放,那麼模組Y的函式返回時,返回地址將是無效的。當時發現在Opera瀏覽器中可以利用Flash模組觸發這種漏洞,一款國產下載工具也有類似問題。
另外還有不少其它型別的漏洞,最終表現也和上述問題一樣,可以執行某個固定的指標,但無法控制該指標的值。在無DEP環境下,這些漏洞並不難利用,只要噴射程式碼到會被執行的地址即可。而在DEP環境下,這些漏洞通常都被認為是不可能利用的。
但如果在預期會被執行到的地址噴射下面這樣的資料:
#!cpp
typedef struct _THUNK3 {
UCHAR MovEdx; // 0xba mov edx, imm32
LONG EdxImmediate;
UCHAR MovEcx; // 0xb9 mov ecx, imm32
LONG EcxImmediate; // <- put your Stack Pivot here
USHORT JmpEcx; // 0xe1ff jmp ecx
} Thunk3;
即使在DEP環境下,儘管堆噴射的記憶體區域確定無疑不可執行,但你會驚奇地發現系統似乎還是執行了這些指令,跳到ecx所設定的地址去了。只要把ecx設為合適的值,就可以跳往任何地址,繼而執行ROP鏈。
這是因為Windows系統為了相容某些老版本程式,實現了一套叫ATL thunk emulation的機制。系統核心在處理執行訪問異常時,會檢查異常地址處的程式碼是否符合ATL thunk特徵。對符合ATL thunk特徵的程式碼,核心會用KiEmulateAtlThunk()函式去模擬執行它們。
ATL thunk emulation機制會檢查要跳往的地址是否位於PE檔案中,在支援CFG的系統上還會確認要跳往的地址能否透過CFG檢查。同時,在Vista之後的Windows預設 DEP policy 下,ATL thunk emulation機制僅對沒有設定 IMAGE_DLLCHARACTERISTICS_NX_COMPAT的程式生效。如果程式編譯時指定了/NXCOMPAT引數,就不再相容ATL thunk emulation了。不過還是有很多程式支援ATL thunk emulation,例如很多第三方應用程式,以及32 位的 iexplore.exe。所以,類似Hacking Team洩露郵件中的CVE-2015-2425,如能用某種堆噴成功搶佔記憶體,也可藉此技巧實現漏洞利用。
這樣,利用系統異常處理流程中的ATL thunk emulation能直接執行不可執行記憶體的特性,就可以讓一些通常認為無法利用的漏洞起死回生。
(本文大部分內容完成於2014年10月,涉及的模組地址、符號資訊等基於Windows Technical Preview 6.4.9841 x64 with Internet Explorer 11。)
0x03 參考
- ROPs are for the 99%, CanSecWest 2014
- Bypassing Browser Memory Protections
- (CVE-2015-2425) “Gifts” From Hacking Team Continue, IE Zero-Day Added to Mix
- 《漏洞挖掘中的時間維度》,VARA 2009
相關文章
- 異常篇——異常處理2022-02-27
- 異常-throws的方式處理異常2018-09-02
- 異常處理與異常函式2016-11-12函式
- 異常的處理2024-08-05
- C#自定義異常 統一異常處理2018-07-05C#
- 異常-try...catch的方式處理異常12018-09-02
- 異常-try...catch的方式處理異常22018-09-02
- 異常處理2024-10-18
- Java 異常表與異常處理原理2019-01-27Java
- restframework 異常處理及自定義異常2018-07-09RESTFramework
- 利用異常表處理Linux核心態缺頁異常(轉)2007-08-16Linux
- MVC使用異常過濾器處理異常2020-10-22MVC過濾器
- Python 中的異常處理2016-12-19Python
- React 16 中的異常處理2017-07-27React
- Ruby中的TypeError異常處理2024-08-20Error
- spring中的統一異常處理2018-09-21Spring
- windows核心程式設計---未處理異常,向量化異常處理與C++異常2017-12-23Windows程式設計C++
- .NET中異常處理的最佳實踐2015-01-29
- Java中的異常處理最佳實踐2024-07-14Java
- JavaScript 異常處理2020-08-13JavaScript
- ThinkPHP 異常處理2019-12-18PHP
- React 異常處理2019-03-01React
- 08、異常處理2021-02-20
- JAVA 異常處理2020-10-31Java
- JAVA異常處理2022-02-24Java
- Abp 異常處理2022-02-27
- oracle異常處理2023-05-09Oracle
- PowerShell 異常處理2018-01-30
- plsql異常處理2017-03-26SQL
- Swift 異常處理2017-09-28Swift
- JS異常處理2018-05-08JS
- app異常處理2017-12-26APP
- Oracle 處理異常2010-11-05Oracle
- MySQL異常處理2016-06-02MySql
- 異常處理 (轉)2007-12-11
- golang - 異常處理2024-07-02Golang
- 異常處理22024-07-02
- 異常處理12024-07-03