用戶模式下基於異常和內核控制的線程級鉤子技術分析
簡介
在這篇文章中,我們將會跟大家介紹我們在研究過程中所發現的一種新型的鉤子(Hook)技術。
Hook技術可以幫助我們了解並控制操作系統中每一部分軟體組件的操作行為,使用了鉤子技術的部分軟體有:應用程序安全解決方案、系統應用工具、編程軟體(例如用於攔截、調試和功能擴展的軟體)、以及惡意軟體(例如rootkit)等等。
鉤子(Hook)是Windows消息處理機制的一個平台,應用程序可以在上面設置子進程/線程以監視指定窗口的某種消息,而且所監視的窗口可以是其他進程所創建的。當消息到達後,鉤子可以在目標窗口處理函數之前對數據進行處理它,因為鉤子機制允許應用程序截獲處理window消息或特定事件。鉤子實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。這時鉤子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。
需要注意的是,本文所要介紹的內容並不涉及提權以及漏洞利用技術。本文所介紹的技術主要用於後滲透場景,即攻擊者已經成功獲取到目標設備控制權的情況。由於惡意內核代碼(rootkit)一般都會嘗試在目標系統中實現持久化感染,因此隱藏技術也逐漸開始扮演一種十分重要的角色了。
技術描述
我們給這項技術取名為BoundHook,BoundHook技術可以在用戶模式場景中的特殊位置引發異常,然後通過捕捉異常來接管線程的執行。我們可以使用BOUND指令(Intel MPX內存保護擴展的一部分)來實現上述操作,這種指令原本的作用是通過檢測指針引用來提升軟體的安全性。簡而言之,BOUND指令可以檢測數組索引的越界情況(可能觸發內存崩潰漏洞),如果測試失敗則會引起軟體中斷。(32-bit: nt!KiTrap05, 64-bit: nt!KiBoundFault)
你可能會問,為什麼不對這兩種操作指令進行一下對比呢?因為英特爾的技術人員在設計這種新型指令時故意生成了一個異常,並讓操作系統來檢查邊界測試失敗的情況。
這種指令的語句如下所示:
BOUND r16, m16&16 – 檢測r16(數組索引)是否越界(m16&16指定)
BOUND r32, m32&32 – 檢測r32(數組索引)是否越界(m32&32指定)
當邊界檢查出現錯誤時,陷阱處理程序(trap handler)將會調用nt!KiHandleBound並執行已註冊的邊界異常回調程序。
內核模式驅動程序或者運行在內核模式下的shellcode payload可以使用nt!KeRegisterBoundCallback來為邊界檢測異常註冊一個回調程序。需要注意的是,這個函數並不是WDK header「提供」的,而且這裡還需要動態載入一個指向該函數的指針。
這種回調程序沒有任何的參數,並且會返回一個BOUND_CALLBACK_STATUS(枚舉類型),具體如下所示:
完成了邊界異常的註冊之後,內核模式代碼會得到一個指向用戶模式DLL基地址的指針,並計算出需要設置鉤子的函數地址。
獲取函數地址其實是一件非常簡單的事情,而且可以通過多種方式去實現,例如通過解析PE頭就可以。需要注意的是,解析一個某個特定進程所載入的圖片則需要在進程環境中進行,或者使用特定的API。
當我們的代碼計算出了函數地址之後,我們如果可以直接開始向這個地址寫入數據就非常好了。但是,由於這部分代碼存在於只讀/可執行內存之中,我們就沒辦法做到這一點了。
Windows內存保護的實現主要依賴一下幾個因素:
現在我們就有幾種選擇了。我們可以想辦法向這個地址寫入數據來觸發COW(copy-on-write)保護,或者使用__readcr0()和__writecr0()來修改CR0寄存器。除此之外,我們還可以分配我們的內存描述符列表(MDL)來描述內存頁面,並使用按位或(bitwise OR)來調整MDL和MDL_MAPPED_TO_SYSTEM_VA的許可權。最後這種方法相對來說更加「隱蔽」一些,因為根據當前PatchGuard實現的設計來看,這種方法是完全不可見的。
首先,我們給大家介紹如何修改CR0寄存器。CR-寄存器的描述如下所示(來源於Intel 64和IA-32軟體架構開發人員手冊):
」WP寫入保護(16位CR0)-當設置時,將阻止高等級進程向只讀頁面中寫入數據;當清空時,將允許高等級進程向只讀頁面中寫入數據。「
下面是CR0寄存器的一個簡單的修改樣例:
如果可以直接向DLL的COW頁面寫入數據的話,我們就能夠對操作系統中每一個使用了這個DLL的進程設置鉤子,因為我們已經可以影響cow-origin頁面了。
觸發邊界異常也是比較簡單的,比如說,下面的代碼將觸發一次錯誤異常:
因此,我們負責執行鉤子的內核模式代碼將能夠向目標位置寫入一個類似的彙編代碼,並成功接管目標線程的執行過程。
比如說,如果我們想要掛鉤KERNELBASE!CreateFileW,我們就可以將下面給出的這行操作碼注入到該函數的起始位置:
UCHAR opcodes[5]= {0x36, 0x66, 0x62, 0x0C, 0x24};n
你可以直接理解為:BOUND CX, DWORD PTR SS : [ESP]。在這種特殊場景下,我們假設CX為0(在真實的使用場景中,我們需要對每一個函數進行測試來決定這個值),而棧頂的值肯定大於0(這只是一個PoC,而並非最終的Exploit)。
現在,當我們將操作碼寫入進了KERNELBASE!CreateFileW之後,如果用戶模式下的線程調用這個函數時,我們內核模式下的回調函數就能夠完全接管這個用戶模式下的線程了。
如果可以實現的話,那我們的優勢就非常大了,比如說:
1. 掛鉤的頁面仍然是COW,因此反惡意軟體解決方案以及研究人員所進行的手動分析將無法發現頁面遭到了篡改。
2. 絕大多數反病毒產品不會檢測到我們的這種技術,而且這個問題似乎無法解決,因為頁面仍然是COW。
3. 用戶模式調試器將無法捕捉到這種鉤子。普通的內聯鉤子方法會讓已掛鉤的程序跳轉到其他的用戶模式代碼,但BoundHook技術可以通過內核邊界異常處理器來修改這種執行流程。
4. 絕大多數PatchGuard(PG)保護機制都無法察覺到我們的這種鉤子技術。根據目前PG的設計原理,用本文所介紹的MDL方法繞過COW機制是不會被檢測到的。對於修改CR0寄存器的方法來說,雖然CR0寄存器是受PG保護的,但PG發現這種修改操作的可能性也非常小,因為修改操作可以在非常短的時間內完成。
PoC-已掛鉤的線程調用棧:
總結
我們知道對於微軟而言,BoundHook技術所利用的技術因素並不會被他們認為是一種安全漏洞,因為設備管理員許可權已經被攻擊者拿到了。在此之前,微軟曾從CyberArk(GhostHook技術)那裡收到了類似問題的報告,而微軟對此的回應如下:
」我們已經對上報的安全問題進行了詳細的分析和調查,並且發現這並不是一個真正意義上的安全漏洞,因為這只是一種用於躲避安全檢測的技術,但設備此時已經被攻擊者入侵了。你們所提交的是一種後滲透技術,而且並不符合我們的漏洞規定,因此我們無法針對該問題發布更新補丁,但我們會在將來的Windows版本中考慮解決這個問題。「
但不管怎麼樣,我認為我們所設計的這種技術可以給軟體安全廠商以及惡意軟體開發者提供一種新的思路,也希望微軟能夠儘快解決這個問題(雖然他們不認為這是一個安全漏洞)。
推薦閱讀:
TAG:Hook |