深入分析Win32k系統調用過濾機制
前言
Windows內核漏洞的利用具有高風險,經常用于瀏覽器沙箱逃逸。許多年以來,發現的大多數漏洞都是來源于Win32k.sys驅動,它負責處理來自GDI32.DLL和user32.dll的調用。為了緩解這些漏洞,微軟在window10上實現了Win32系統調用過濾.總體思路是在進程入口處嘗試阻止大量的發往win32.sys的系統調用,以便阻止未知的漏洞利用。我沒有找到實現的相關細節,也不確定效果如何。我發現的唯一資料就是由Peter Hlavaty發表在 Rainbow Over the Windows的相關文章。
系統調用101
我理解過濾技術的第一個方法就是研究系統調用是如何執行。分析是基于Windows 10周年更新的64位版本。通常,系統調用在gdi32.dll或者user32.dll的函數中被初始化,最終會在win32u.dll中調用真實的系統調用。但是,我們可以直接使用匯編來顯示系統調用,在以下的POC中,我查詢到系統調用號為0x119E的WIN32K函數是NtGdiDdDDICreateAllocation。所以我簡單地創建下面的測試應用程序:
下圖是NtGdiDdDDICreateAllocation的匯編代碼:
當運行syscall指令后,將轉入內核模式執行.真實的系統調用位于NT!KiSystemStartService。但是,由于有大量的系統調用,所以我們需要在調試器中設置一個條件斷點:
運行POC并開啟斷點
首先顯示的系統調用號就是我們提供的0x119E,其實參數1,2,3,4保存在寄存器RCX,RDX,R8和R9中。在IDA中查看相關代碼:
查閱代碼,我們發現一個有趣的問題:RBX寄存器的內容是什么,這又是從何而來。嘗試引用KiSystemServiceStart,我們發現 RBX在以下函數中被設置:
MOV RBX GS:188將Win32SyscallFilter.exe的內核線程結構載入RBX中,驗證如下:
研究算法
接下來的問題是RBX + 0x78代表什么,事實證明,它代表一系列的標志位。下圖引用的兩個標志是GuiThread和RestrictedGuiThread,它們分別位于標志的第6位和和19位。
在我們的例子中,標志位的值如下:
由于線程不是一個GUI線程,所以會重定向的將它轉換成一個GUI線程,然后返回相同的指針。繼續執行會發生:
Win32kSyscallFilter并沒有做任何事。但是接下來的檢查很有趣。RestrictedGuiThread標志指明,如果啟用系統調用過濾,會在進程級別上進行檢測:
因此,對于當前的進程和線程,系統調用過濾沒有啟用。查看進一步執行,將體現出這個標志位的重要性:
如果開啟了系統調用過濾功能,KeServiceDescriptorTableFilter將取代KeServiceDescriptorTableShadow,如果沒有開啟過濾,則將使用KeServiceDescriptorTableShadow。接下來要觀察系統調用表的使用,如下圖所示:
在經過運算后,RDI包含系統調用數目。在WIN32K系統的情況下,它的值是0x20。所以,取決于系統調用過濾是否開啟,不同的表會被載入R10.這兩個選項是:
然后該表將轉入真實的函數調用:
跟隨以上的算法,我們在調試器中找到:
所以這很顯然,系統調用號通過負偏移指向W32pServiceTable結構,然后指向真實的NtGdiDdDDICreateAllocation函數。這是非常好的,但是如果開啟了系統調用過濾,會有什么區別呢,這可以使用W32pServiceTableFilter來進行驗證:
我們看到在此之前并沒有什么區別,這是因為NtGdiDdDDiCreateAllocation并不是過濾的API之一,如果我們選擇其他的系統調用,比如NtGdiDdDDiCreateAllocation,它的系統調用號是0x117E。我們基于是否啟用系統調用過濾來對比以下兩個輸出。
首先是未啟用系統調用過濾的:
然后是啟用系統調用過濾:
我們發現,如果開啟了系統調用過濾功能,系統調用是不允許的另一個函數被調用的。該過濾函數驗證是否啟用系統調用過濾并簡單的結束系統調用。
利用的結果
現在我們了解了系統調用過濾是如何工作的,我們需要研究它是如何防止內核漏洞利用的。首先要看看它保護的是什么進程,到目前為止,僅僅是微軟Edge可以啟用這個功能,目前第三方程序沒有可以啟用它的接口。這意味著系統調用過濾僅僅關心Microsoft Edge漏洞并且僅限于內核漏洞。下面我們可以看到MicrosoftEdgeCP.exe的進程結構,并啟用了系統調用過濾:
回顧我早期文章,關于重新啟用tagWND對象作為讀寫原語的用法的,我想知道是否使用這個方法的任意系統調用都會被過濾,在那個方法中使用的內核模式的函數是:
- NtUserCreateWindowEx
- NtUserDestroyWindow
- NtUserSetWindowLongPtr
- NtUserDefSetText
- NtUserInternalGetWindowText
- NtUserMessageCall
在win32k.sys中沒有stub_*方法的函數意味著不會被過濾。結論就是Win32K系統調用過濾不會阻止可能導致漏洞的系統調用,但是當系統調用觸發一個漏洞時,它一定會阻止,可能是write-that-wherer或者是一個緩沖區溢出。WIN32K系統調用過濾所提供的保護是巧妙的,但關鍵在于是否使用了系統調用來觸發漏洞。