作者:Ashfaq Ansari
本文總結了目前windows內核攻擊的各種攻擊技術.描述并演示了一些常見的繞過windows內核防護的方法,并舉一反三地介紹了如何通過內核缺陷找到類似的繞過方法。 通過對內核攻擊和內存結構的理解將會進一步豐富基于用戶模式應用程序的緩沖溢出知識。
通過大量內核漏洞的研究表明,特定的內核代碼執行可能大多由用戶模式的應用程序引起。因此,針對用戶模式的應用程序,操作系統增加了大量的保護機制,用以保護和檢測這類的攻擊,比如:隨機化、執行保護、內存防護等等。然而,針對操作系統溢出攻擊,目前所做的工作還遠遠不夠。 本文將討論交流各種內核攻擊技術和一些可能的內核攻擊方法
所有的演示都基于Windows 7 x86 SP1環境下編譯的特意存在漏洞的驅動,本文將通過該驅動存在的漏洞來展示內核缺陷以及如果通過內核攻擊技術來進行本地權限提升。
所需工具:
Windows 7 操作系統
虛擬機
一個存在漏洞的驅動
Windows 內核調試器 – WinDBG
備注: 設置調試器管道名為 \.\pipe\com1
,同時被調試端也同樣設置.
在開始攻擊之旅之前,我們先了解一下內核基本結構和windows進程空間的內存分配和執行方式。Windows操作系統有兩個模式:內核模式和用戶模式。任何一個程序都是在其中一個模式里執行。
圖 1: Windows?結構?來自: logs.msdn.com
HAL: Hardware Abstraction Layer 硬件抽象層?–一組程序例程,使軟件可跨平臺移植; HalDispatchTable 保存著一些 HAL例程的地址。
當拷貝用戶輸入數據到事先分配的一個緩沖空間里時,如果沒有做邊界檢查,就會發生棧溢出。 memcpy()函數在進行內存拷貝的時候不會做長度檢查,如果拷貝過長的數據到預先定義的緩沖變量里,就會導致溢出的發生。
以下是一個用了 memcpy() 函數的程序。
圖 2: StackOverflow.c
首先我們用大量的數據溢出它并且覆蓋掉返回地址。 這樣我們就會控制了程序后面要執行的指令。 我們用大量字符‘A’成功使棧發生了崩潰。 然而, 為了找到返回地址的精確位置,我們需要發送一組特定模式的數據來匹配到返回地址(譯者注:可以是這樣AAAABBBBCCCCDDDD…)。
通過溢出代碼構造了一組輸入數據,我們找到了返回地址的偏移如圖:
圖 3:?定位EIP
如上所示, EIP被?72433372 覆蓋(內存地址左高位,右低位,對于字符是 72334372 ).然后定位到覆蓋EIP的位置是字符串長度為2080的地方。
在我們的溢出代碼里, 我們通過變量ring0_shellcode’定義了 shellcode 如下:
圖 4:? Shellcode
我們把shellcode地址放入我們溢出代碼的緩沖區里,通過使shellcode地址覆蓋返回地址,這樣SHELLCODE得以執行以后,我們以用戶模式啟動的程序最終將以內核模式執行。
備注: 首先, 我們用Python腳本找到shellcode在內存的地址,例如:
#!python
ring0_shellcode_address = id(ring0_shellcode) + 20 //id(var) + 20
接著,把SHELLCODE地址放到可以覆蓋返回地址的地方(上述找到的EIP偏移)。溢出代碼執行后會調用SHELLCODE,這時會開啟一個以系統權限執行的命令行SHELL,如圖:
為了抵御堆棧溢出攻擊,產生了一種保護機制:Stack Guard. 這種機制使得執行函數增加了兩個元素:function_prologue和function_epilogue。Stack Guard 實際是通過編譯時在這個兩個元素處增加代碼以設置和檢驗棧信息(保存在Canary里)。
Function prologue
圖 6: _except_handler4
Function Epilogue
圖8: Security Cookie效驗
參照上面的程序,我們發現每次通過傳統方式覆蓋堆棧的時候, 我們也不得不同時覆蓋掉Stack Cookie。 除非我們用正確的棧信息來覆蓋Canary, 否則在函數尾的檢查將失敗并且程序會終止。
解決方法
我們將通過溢出覆蓋異常處理函數地址的方法來繞過 Stack Cookie保護。 異常處理函數地址存放在堆棧里,我們可以任意的覆蓋堆棧,當從用戶空間傳遞大量數據到內核緩沖區的時候,我們把SHELLCODE地址覆蓋掉異常處理函數的地址,這時觸發異常并跳轉去執行我們的SHELLCODE代碼。
圖 9: 堆棧溢出防護繞過
根據攻擊代碼,繞過 Stack Guard 后執行INT 3指令,觀察調試信息如圖:
圖 10: 繞過 stack Guard
圖11: 執行shellcode并停止在斷點
這個漏洞也被稱作: WRITE_WHAT_WHERE,可以使攻擊者可以在任意內存寫任意內容。 如果操作不當會導致程序崩潰(用戶模式)或者藍屏(內核模式BSOD)。
通常這里會有一些現限制
Value – 可以寫什么樣的內容
Size – 可寫內存的大小
而且有時只能遞增或遞減內存
這類漏洞相比其他已知類型漏洞比較難以發現, 但是對于惡意代碼執行非常有用。 這里有許多可改寫導致代碼執行的地方,比如: HalDispatchTable+4, 系統中斷表Interrupt Dispatch Table, 系統服務表System Service Dispatch Table等等。
如下是 WRITE_WHAT_WHERE 漏洞的結構:
既然漏洞允許我們自定義上述結構中的What和Where屬性, 我們把我們的shellcode地址寫在‘What’處,把 HalDispatchTable0x4 的地址寫在‘Where’ 處,如下:
圖 13: 部署shellcode 地址和HAL Dispatch Table地址
我們在內核調試器中斷程序,檢查HalDispatch Table 函數地址如下:
圖 15: Write_What_Where執行演示
Exploit執行后,我們在調試器檢查內存發現, HalDispatchTable+4處地址被SHELLCODE地址覆蓋, 然后SHELLCODE被執行。 下面對話框顯示程序在斷點處中斷。
圖 17: 改寫后斷點處的EIP
最后PAYLOAD里的shellcode將利用任意改寫漏洞得以執行.
當一個程序使用已經被釋放后的內存時,會導致意外的系統行為,如異常或可以用來獲得任意代碼執行。此類漏洞需要如下條件:
某些時候一個對象被創建,同時與一個虛表關聯,然后程序會通過某個方法來調用這個對象。如果在程序調用對象之前,我們把這個對象釋放,當程序調用該對象時就會導致程序崩潰。
這種情況下, 攻擊者布置好內存。然后, 分配類似大小的對象。接著, 攻擊者嘗試釋放一些對象來制造一個內存“孔”。 然后, 然后分配和釋放弱點對象,最后攻擊者填充這些“孔”,來接管弱點對象的內存空間。這類漏洞難以被發現和利用,并且需要一定條件:
Shellcode指針必須放在被釋放的對象的內存空間。
創建的“孔”的大小必須和釋放的對象的大小相等。
不應有相鄰的內存塊被釋放以防止Coalescing。
Coalescing: 當兩個獨立且相鄰的內存塊被釋放,操作系統會連接這些小內存塊,以創建一個大的內存塊,用以防止內存碎片。 這個過程叫Coalescing,它使Use After free漏洞攻擊變得更加困難。因此,內存管理器不會分配特定的內存,攻擊者獲得相同內存空間的機會很少。
如下給出了一個存在該漏洞的案例(內核下C函數):
首先我們讓被調試端/目標以guest權限運行。 我們首先必須在內核池分配一個弱點對象,以觸發Use After free漏洞, 然后釋放它并強制使程序使用這個被釋放的對象。
圖 18:Use After Free 對象被分配.等待釋放.
接下來, 我們釋放對象以創建內存“孔”.最后,我們填充所有被釋放的內存塊以控制被釋放對象的內存,需要花費一定的時間來實現內存控制,大概需要嘗試100次左右。 我們通常通過一個FakeObject來重新分配 UaF對象。
圖 19: 釋放并重新分配 UAF 對象
圖 20: 釋放并重新分配 UAF 對象
同時, 這些內存塊會被攻擊者控制的對象填充。這個時候我們看一下內存池, 我們會發現我們已經成功地重新分配了我們創建的內存“孔”。
圖 21: 所有連續的內存塊都被 IoCo填充以確保內存被均勻噴射
最后觸發使用被釋放的UaF對象,導致漏洞產生。 攻擊代碼執行后,會生成一個系統權限的SHELL, 如圖:
圖 22: 攻擊者代碼以系統權限執行
另一個有趣的漏洞是,可以利用內核缺陷通過進程令牌來提升權限。
下面的部分,我們說明了攻擊者如何從一個更高的或者不同特權的級別偷取令牌 ,然后進行權限提升或者使自身擁有和其他進程一樣的權限。盡管有很多已知的內核防護機制,比如ASLR、 DEP、 Safe SHE、SEHOP等等,但在內核中使用這些漏洞, 任何一個進程都可以被賦予系統權限。
下面將一步一步針對權限較低的用戶 ‘Guest‘,來說明利用令牌漏洞進行權限提升的過程。 我們將用內核調試器會話來提升cmd.exe 進程的權限,使它從Administrator權限到SYSTEM 權限。
用內核調試器找到當前正在運行的進程和他們的屬性,如下-
For cmd.exe
For?SYSTEM
現在我們知道了系統進程的令牌, 我們可以切換到 cmd.exe 進程 并找到這個進程令牌的位置。
從上面找到的地址中獲得KPCR 結構
在偏移 +0x120 獲得KTHREAD成員 CurrentThread的地址
獲得KAPC_STATE成員 ApcState的地址。 它包含一個指向 KPROCESS的指針
獲得KPROCESS成員 Process的地址。 它包含令牌值,在KTHREAD 基址偏移+0x40處
圖 23: KAPC List Entry
從 EPROCESS 結構獲得Token 成員的偏移。 KPROCESS是EPROCESS的第一個結構
獲得Token值
實際的令牌值需要把最后3位和0進行與操作,然后令牌值0x953b6037變為 0x953b6030?現在我們用系統令牌替換掉進程令牌。
圖 24: 令牌值被替換
令牌被替換后,進程馬上就被賦予了系統權限。 在被攻擊者電腦里驗證如下:
圖 25: 通過令牌漏洞提升Guest到系統權限
圖 26: 一個例子: 利用令牌漏洞對Guest用戶進行權限提升