eEye工程介紹:漏洞大曝光 編寫引導層RootKit
原創【51CTO.com 獨家翻譯】在先前的文章中,我們深入探討了如何通過Windows進程處理“特殊”熱鍵,并認為主要是通過Win32k.sys中和WINLOGON.EXE進程。這一次,我們將努力使用由eEye公司改編的被稱為SysRq的BootRootKit(引導層RootKit)。
更多的信息在eEye公司的bootroot的工程中,演示的相關幻燈片請到http://www.eeye.com/html/resources/downloads/other/index.html.下載。
SysRq(就是系統需求,它就像鍵盤中的SysRq按鍵)包含一個Kiobyte的引導代碼,加固在Windows自身內核中,一旦有本地調用,它就會先注冊一個特殊熱鍵(control +Shift+SysRq),一旦激活就能打開獲得最高級系統權限的cmd窗口。盡管這是非常有用的,同時也存在有爭議的系統管理員的工具,它或許會被用在非法的用途中,所以我們目前尚未把它開源。然而,該項目是在一個可行性技術上進行的,如果我們來討論它的技術細節和執行時還是很有趣并且很有意義的,這里我們建議讀者要有熟悉Windows內核的水平和用戶模式編程的相關知識。
SysRq 概況
SysRq的要求很簡單:當熱鍵被激活后,該引導扇區代碼才會在系統命令提示符下運行。因為引導扇區代碼,會一直駐守在window內核中,但cmd.exe命令是用戶系統最容易的也是最經常的調用。由于很多熱鍵處理由Winlogon完成,因為它主要作為系統對開始其他交互式過程處理(例如任務管理),例如用戶模式代碼就是最合適的主機進程。
正如我們前面提到,在FFDF0000h / 7FFE0000h處的用戶共享數據是內核空間和用戶空間的橋接,但是實際上,我們通過使用主機的用戶代碼來寫入到本地winlogon進程空間的地址里面。
我們使用這個實用技術來調用這個代碼,然后我們一直在這個項目中進行不斷的修改。在執行syerq1.0版本的時候,winxp及更高的版本容易造成藍屏,我們需要一個更好的解決方法。然而,這是要進行多次失敗的嘗試,這些失敗的嘗試看起來還是能給我們帶來一定的受益,它囊括了全部的技術細節。
#p#SysRq1.0版本只能工作在Windows2000系統上
SysRq的第一個階段與BootRootKit的相同,基本三個步驟:
1、安裝一個在Int13h(Bios 磁盤服務)的鉤子;
2、在OSLOADER補丁處并加載其程序;
3、Windows內核內存使用時要使用一次補丁。
這個問題的第三個步驟是我們只能加載可靠的內存尋址模塊。BootRootKit覆蓋在DOS中的的MZ和PE頭NDIS.SYS的代碼之間。因為它要用少于40h的字節的代碼植入到內核中,但是sysrq持續更新的代碼要比這遠遠大的多,所以我們必須做一些調整。
SysRq v1.0 也采用了相同的技術,在NTOSKRNL.EXE的DOS 代碼里安裝一個小的PspInitializeSystemDll 鉤函數,但是這只是第一步。臨時的鉤函數代碼通常映射在常規存儲器的頁里,并包含在SysRq的虛擬地址0處,通過修改固定的第一個頁表的的C00000000h虛擬地址而進入。將SysRq復制到用戶共享數據未用的FFDF0800h處,然后我們接著用鉤函數替換原先的函數,以便我們更好的執行我們的代碼。
我們的目標是要取代NTDLL.DLL的地址 !
LdrInitializeThunk,是我們已知的內核函數,用我們已知的初始化代碼,它在用戶態的每個進程里的用戶共享數據里面,在7FFE0000h處NTOSKRNL初始化代碼的通過我們叫做PspLookupsystemdllEntrypoint函數并且使用"LdrInitializeThunk"的字符串獲得這個地址。我們發現這個call指令通過先搜索這個字符串,然后這個鉤子函數再調用它自身而不是直接調用PspLookupsystemDllEntryPoint函數,我們只是想取代LdrInitializeThunk函數。(其他的NTDLL出口也使用這個函數,包括KiUserApcDispatcher 和KiUserExceptionDispatcher)。
鉤子函數通過原函數調用,然后用一個指向我們在用戶代碼共享處的指針替換掉LdrInitializeThunk函數指針。當然,我們必須保持原來的指針能正常使用,以便完成我們自定義的初始化代碼。
這個代碼就是是SysRq的核心。每一個新線程都要先通過異步過程調用,這是非常完美的設計。因為我們想要我們的代碼執行在每一個進程中,但是只能有一個winlogon生效,一個最簡單的方法就是不讓winlogon專門試圖訪問Windows內核。然而我們只是希望我們的代碼運行在進程初始化,而不是在每一個線程的初始化,但是我們不能在用戶模式下對這個掛鉤。一個簡單的方法就是檢查加載數據是否在[PEB+0Ch]處,
如果為非null,那說明這個進程已經初始化,我們的代碼不再做任何的操作。
當到達進程初始化之前,我們的代碼繼續搜索這個應用映像的unicode字符串的“SAS 窗口類,如果我們找到了它,那么接著在RegisterClassw調用中找到了這個字符串后并創建這個類。那時,我們找到SASWndProc,這個窗口存放類-通過檢索在這個vWNDCLASS.LpfnWndProc之前的存儲地址記錄,然后我們鉤住這個典型函數的入口點的修改。
我們的SASWndProc函數構造的盡可能的簡單-我們假定能替換PUSH EBP/MOV EBP/SUB ESP (值小于等于800h)這些指令,通過檢查MOV EDI,EDI"來檢查最近新增加的Service Packs例如Windows XP的SP2.為了安裝這個鉤子,我們用這個代碼寫入正在使用的ZwProtecVirtualMemory函數中,然后在植入到相對應的prolog函數,并給SAS window發一條信息。
該鉤子函數的行為就像一個非常基本的SASWndProc函數,因為我們用USER32.DLL來注冊我們的熱鍵。RegistrHotKey接收到WM_CREATE 信息,并處理這個熱鍵,當WM_HOTKEY 信息傳達到‘idHotKey'我們這個指定的值。顯而易見,當我們檢查RegisterHotKey,我們需要USER32.DLL的基址,但是它可以通過在PEB+2Ch的USER32DispatchRoutine指針來獲得標記,直到讓我們發現MZ和PE頭。
當按Ctrl + SHIFT +SysRq熱鍵被按下時,我們的WM_HOTKEY創建了一個“cmd”的進程并返回到SASWndProc的調用,并完全中斷原來函數調用。實際上,只有少數的api調用涉及到這樣的工作,更多的獲得了窗口駐留和當前桌面,因此我們可以得到一個命令shell甚至能鎖定或搶先登陸系統。
工作順序如下:
GetProcessWindowstation() GetUserObjectInformationA(hWinSta, UOI_NAME) OpenInputDesktop(0, FALSE, 0) GetUserObjectInformationA(hDesktop, UOI_NAME) CloseDesktop() |
一旦我們把構造好的“WindowsStation\Desktop”字符串填充到'lpDesktop'區域里一個啟動信息結構中,我們把這個過程叫做
CreateProcessA,并用CloseHand來對它們進行清理,那么我們就已經大功告成。用戶現在就會擁有一個系統權限下的command shell。
正如前面文章提到的,這個方法不能應用在Windowsxp或更高版本的WIN操作系統上。當激活Windows產品以后,它就會驗證WINLOGON.EXE在內存中的映像,一旦發現被篡改,它就會自動中斷該進程,造成藍屏而最終導致系統崩潰。雖然我們試圖用其他的代碼來攻擊或者更改,以及對其他的目標下鉤子,使得我們能夠再下鉤子以后,用代碼騙過驗證,這一操作之后,讓系統認為這與之前的系統并沒有任何變化,并沒有對系統中的目標下了鉤子。奔著這個目的我們一直使用其他的方法來完成,最終我們發布了SysRq2.0版本。
#p#SysRq2.0版本開始支持Windows2000,xp,以及2003操作系統
同BootRootKit(引導型rootkit)一樣,SysRq2.0版本也安裝在了INT13h這個硬指令中斷上,對啟動時的OSLOADER.EXE進行補丁操作,并對其下了鉤子,最終達到直接滲透到Windows內核。像SysRq1.0一樣,它也是對Winlogon 的SASWndProc進行替換操作,但現在的這個版本的技術實現與前一版本有著很大的區別。我們不能改變WINLOGON.EXE映像,但是我們可以通過對窗口類的注冊機制進行攻擊,從而來對它進行改變。
USER32.DLL的RegisterClassW 是RegisterClassExWOWW函數的一個封裝,經過一番探索后,我們在WIN32K.SYS中發現了一個具有同樣作用并且函數名也一樣的系統調用的user32函數,它就是NtUserRegisterClassExWOW函數。由于SysRq工作在核心態,可以直接對WIN32K.SYS下鉤子,但是我們在執行內核代碼時,不能對已經初始化的NTOSKRNL進行操作,我們卻可以從進程中SMSS.EXE加載的WIN32K.SYS進行操作。
這是比其他解決方法更有效的辦法。當WIN32K.SYS初始化時,它必須調用KeAddSystemServieceTable來為用戶以及GDI系統程序提供注冊。通過對這個函數進行下鉤子操作,并更快更好的把它加載到執行的WIN32K.SYS中,同時我們使用免費的系統服務表(_W32pServiceTable)和相應的參數表 (_W32pArgumentTable)。像SysRq1.0版本一樣,使用這個鉤子來對NTOSKRNL的頭的DOS代碼處來執行一個小stub,然后我們拷貝這個重置的SysRq代碼到用戶共享數據里面來進行恢復并執行該代碼。
我們對KeAddSystemServiceTable下鉤子主要是通過掛鉤NtUserRegisterClassExWOW這個系統調用函數來實現的,因為那個函數沒有輸出,因此需要一點更復雜的操作。針對這個問題,我們的答案是通過對在W32pServiceTable的代碼進行逐個函數掃描,直到找到NtUserRegisterHotkey,這是唯一的選擇,因為“FFFF7FF0H”那個最容易辨認的簽名是用來檢查禁止修改的標記。我們知道NtUserRegisterHotkey總是被應用到四個程序中,為了安全起見,我們還需要讓W32pServiceTable這個函數入口點能被寫入10h字節的內容,。
我們都知道這個函數位置在系統服務表里,我們對NtUserRegisterClassExWOW函數從前到后逐個字母進行比對搜索。這是找打它的最好的辦法,或者取6或者7(18h或者1Ch字節)來決定Windows版本。一旦發現這個函數,我們就替換掉WIN32K系統表的入口并用一個指針指到我們自己的鉤子函數處,最終替換掉原先這個函數的指針,并從已恢復運行的KeAddSystemServiceTable中脫掉鉤子。
該NtUserRegisterClassExWOW鉤子函數并不復雜,它只用來比較這個類的名稱(都2個參數與一個unicode字符串指針)和SAS Windows class來完成匹配,它用我們在用戶映射的用戶共享數據中的那個更換過的SASWndProc代碼來替換掉‘lpfnWndProc領域中的WNDCLASSEXW結構,替換完后再把在PEB特定位置的原始函數指針進行存儲操作。我們要進行輸入驗證,畢竟我們不希望發生內核漏洞。
最后,我們最終的目的是獲得系統的shell,獲得最高的系統權限。
SysRq 2.0有一個明顯的缺陷,它只能在支持終端服務系統上的命令行回話中工作——這個原因是由于每個回話都有自己Win32k全局數據內存的拷貝,包括存儲_W32pServiceTable的.data區域,可能這就是終端服務能夠使用Win32k.sys訪問全局變量的原因(感謝eEye工程師Robert Ross指出這一點)SysRq的未來版本中,可能會通過直接鉤掛NtUserRegisterClassExWOW代碼來克服這一缺陷,這個改進將可以影響到所有的終端服務回話。
#p#總結:
在這篇文章里,我們已經討論我們怎樣實現SysRq,eEye按照需求提供一個基于BootRoot的實用程序系統來實現獲得SYSTEM shell的技術細節。為了了解Windows hotkeys是如何工作的,以便使得在任何臺式機上都能支持WinLogon 系統進程來完成熱鍵注冊,我們進行了相關逆向工程操作。
詞匯注釋:
1、WinLogon登錄管理
Winlogon進程負責管理登錄相關的安全性工作,它負責處理用戶的登錄與注銷、啟動用戶shell、輸入口令更改口令、鎖定與解鎖工作站等。Winlogon進程必須保證其與安全相關操作對其他進程不可見,以免其他進程取得登錄密碼。
系統初始化時,啟動用戶程序之前,Winlogon進行特定工作已保障以上的需求。Winlogon進程將創建并打開一個Window Stations,然后設置一個訪問控制人口(ACE),該ACE中只包含Winlogon進程的SID,這樣就只有Winlogon進程才能訪問該Window Stations 。然后winlogon創建桌面,設置其中的winlogon桌面,只有winlogon可以訪問,其他進程不能訪問該桌面的任何數據和代碼;利用這一特性保護口令、鎖定桌面等操作的安全。winlogon還會注冊安全注意序列(SAS - secure attention sequence)的熱鍵,任何時候按下SAS熱鍵(缺省為ctrl+alt+del),將調用Winlogon,切換到安全桌面,從而使密碼捕捉程序不能接收登錄密碼和更改密碼等安全活動。
2、涉及函數解釋
NtQueryDirectoryFile
在WINNT里在某些目錄中尋找某個文件的方法是枚舉它里面所有的文件和它的子目錄下的所有文件。文件的枚舉是使用NtQueryDirectoryFile函數。
KeAddSystemServiceTable
函數KeAddSystemServiceTable允許Win32.sys和其他設備驅動程序添加系統服務表。除了Win32k.sys服務表外,使用KeAddSystemServiceTable添加的服務表會被同時復制到 KeServiceDescriptorTable和KeServiceDescriptorTableShadow中去。
【51CTO.COM 獨家翻譯,轉載請注明出處及譯者!】
【編輯推薦】
原文:Source: Derek Soeder,Software Engineer,http://www.eeye.com/html/resources/newsletters/vice/VI20051104.html
【編輯推薦】