不久之前,微軟修復了Microsoft Kernel Streaming Server(一個Windows內核組件,用于相機設備虛擬化和共享)中的一個關鍵漏洞,即CVE-2023-36802。該漏洞是一個內核漏洞,將允許本地攻擊者將權限升級為SYSTEM。

在這篇文章中,我們將對Windows內核安全進行剖析,然后會詳細介紹該漏洞,并討論跟該漏洞相關的類文件,以及該漏洞的利用探索過程。

攻擊面分析

Microsoft Kernel Streaming Server(mskssrv.sys)是Windows多媒體框架服務中的一個組件,該服務可以虛擬化相機設備,并允許在多個應用程序之間共享該設備。

在發現了漏洞CVE-2023-29360(一個TPM驅動程序漏洞)后,我們便開始探索這一個攻擊面。于是乎,我們便開始在反匯編工具中分析相關的二進制文件。通過分析后,我們不僅發現該漏洞是一個邏輯錯誤漏洞,而且這個漏洞很容易被觸發,利用起來也相對簡單。

初始分析

在MS KS Server中觸發執行

首先,我們需要能夠從一個用戶空間應用程序來訪問到驅動程序。而存在漏洞的函數功能可以通過驅動程序的DispatchDeviceControl程序來訪問,這也就意味著,我們可以通過向驅動程序發送一個IOCTL來訪問該功能。要實現這一點,我們需要通過使用設備路徑調用CreateFile來獲得驅動程序設備的句柄。一般來說,設備名稱或路徑的識別相對容易:在驅動程序中找到對IoCreateDevice的調用,并檢查包含設備名稱的第三個參數即可。

我們可以看到,設備名稱的參數為NULL,調用函數的名稱表明mskssrv是PnP驅動程序,對IoAttachDeviceToDeviceStack的調用表明創建的設備對象是設備堆棧的一部分。實際上,這意味著當I/O請求發送到設備時,會調用多個驅動程序。對于PnP設備,需要設備接口路徑才能訪問該設備。

使用WinDbg內核調試器,我們可以看到哪些設備屬于mskssrv驅動程序和設備堆棧:

如上所示,mskssrv的設備連接到了屬于swenum.sys驅動程序的下游設備對象上,并且擁有一個屬于ksthunk.sys的上游設備。

從設備管理器中,我們可以找到目標設備實例ID:

我們現在有足夠的信息來使用配置管理器或SetupApi函數獲取設備接口路徑了,使用檢索到的設備接口路徑之后,我們就可以打開設備的句柄了。這樣一來,我們就可以在mskssrv.sys中觸發代碼執行。創建設備時,會調用驅動程序的PnP調度創建函數。為了觸發額外的代碼執行,我們可以發送IOCTL與將在驅動的調度設備控制功能中執行的設備進行對話。

漏洞分析

我們當時在分析驅動程序通信機制時發現了這個漏洞,首先我們需要看一看用戶模式下的兩個組件,即fsclient.dll和frameserver.dll。下面給出的是PublishRx IOCTL函數:

從FsContext2檢索流對象后,將會調用FSRendezvousServer::FindObject以驗證指針是否與全局FSRendezvousServer存儲的兩個列表中的對象相匹配。一開始,我們假設這個函數有某種方法來驗證請求的對象類型。但是,如果指針位于上下文對象列表或流對象列表中,則函數返回TRUE。請注意,這里并沒有向FindObject傳遞關于對象類型的信息。這也就意味著,上下文對象可以作為流對象傳遞,這就是一個對象類型混淆漏洞,在對流對象進行操作的每個IOCTL函數中都存在該漏洞。為了修復該漏洞,微軟使用FSRendezvousServer::FindStreamObject替換了FSRendezvousServer::FindObject。

漏洞利用

原語

由于上下文注冊對象小于(0x78字節)流注冊對象(0x1D8字節),因此可以在越界內存上執行流對象操作:

池噴射(堆噴射)

為了利用漏洞原語,我們需要能夠控制訪問的越界內存,這里可以通過觸發在易受攻擊對象的同一內存區域中分配許多對象來實現,這種技術被稱為堆噴射或池噴射。易受攻擊的對象是在非分頁碎片堆池中分配的。我們可以使用Alex Ionescu的經典噴射技術來噴射緩沖區,這將允許我們對0x30字節DATA_QUEUE_ENTRY Header以下的內存內容進行完全控制。通過使用這種技術進行噴射,我們可以拿到如下圖所示的內存結構:

使用選定的池噴射方法,我們可以控制0xC0-0x10F和0x150-0x19F范圍內的對象偏移量。

常量寫入

我們在PublishRx IOCTL中找到了一個非常合適的常量寫入原語,這個原語可用于在任意內存地址寫入常量值。下面是FSStreamReg::PublishRx函數的部分代碼段:

流對象包含偏移量為0x188的列表Header,該列表Header描述了FSFrameMdl對象列表。在上面的反編譯代碼段中,會迭代此列表,如果FSFrameMdl對象中的標記值與應用程序傳入系統緩沖區中的標記相匹配,則會調用函數FSFrameMdl::UnmapPages。

使用上述漏洞利用原語,可以完全控制FSFrameMdlList,從而可以完全控制pFrameMdl指向的FsFrameMdl對象。下面給出的是UnmapPages部分代碼段:

在上面反編譯函數的最后一行,常數值2被寫入了這個FSFrameMdl對象的可控制偏移值。這種常量寫入可以與I/O環技術結合使用,以實現任意內核讀寫和權限提升。

雖然我選擇了使用常量寫入原語,但另一個有用的漏洞利用原語也出現在了這個函數中。MmUnmapLockedPages調用的參數BaseAddress和MemoryDescriptorList都是可控的,這樣就可以取消映射任意虛擬地址處的映射,并構造類似釋放后使用(UAF)的原語。

總結

從該漏洞中我們可以總結出一個經驗,即不要對所執行的檢查做出假設。這種情況非常常見,也是很多開發人員容易忽視的地方,特別是在處理進程間通信的驅動程序中,很可能還存在該漏洞的各種變種版本,因此更加需要注意,