作者:天融信阿爾法實驗室
公眾號:https://mp.weixin.qq.com/s/2qdomRT1a6WvRfg2dtZr5A

一、 簡介

CVE-2019-0708經微軟披露已經有一個多月了,本文將主要圍繞以下幾個方面介紹該漏洞

  1. 經過分析驗證該漏洞是一個UAF漏洞,引發UAF漏洞的指針是由何時創建以及為何該指針在Free之后又被使用,是本文重點關注的地方。

  2. 該漏洞屬于RDP協議實現方面的漏洞,文中會列舉與該漏洞相關的RDP協議知識。

二、RDP協議介紹

2.1 協議簡介

遠程桌面協議(RDP, Remote Desktop Protocol)是一個多通道(multi-channel)的協議。

RDP協議也是C/S網絡結構,雙方通過TCP連接進行通信,基本也是基于請求/響應這樣的數據交換模式,這里貼一張來自微軟發布的RDP協議時序圖,該圖詳細描述了RDP連接中請求及響應的順序及過程。

fdbf0d84942c7fbec73f99f55c1dd077.png

該文檔([MS-RDPBCGR].pdf)地址如下:

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/5073f4ed-1e93-45e1-b039-6e30c385867c

文中不介紹通訊細節,有興趣的讀者可以自行閱讀該文檔。

2.2 靜態虛擬信道(Static Virtual Channels)

靜態虛擬通道允許RDP Client和RDP Server通過主RDP數據連接通信。虛擬通道數據是特定于應用程序的,對RDP不透明。連接時最多可以創建31個靜態虛擬通道。RDP Client在連接序列的Basic Settings Exchange階段請求并確認所需虛擬通道列表,并在Channel Connection階段進行信道的連接。

每個虛擬通道充當獨立的數據流。RDP Client和RDP Server檢查在每個虛擬通道上接收的數據,并將數據流路由到適當的處理函數以進行進一步處理。

三、UAF成因及調試過程

已確認的是,CVE-2019-0708為UAF漏洞,眾所周知UAF漏洞主要是由于對象指針在釋放后再次被使用而引發的安全問題。那么在這一部分內容中,我將描述引發UAF漏洞的指針是由何時創建以及為何該指針在Free之后又被使用。

3.1 觸發UAF的過程:

  1. RDP連接建立,RDP Server 默認調用IcaCreateChannel() 創建MS_T120靜態虛擬信道,并綁定到0x1F信道號,此時是該信道第一次綁定。

  2. RDP Client 在通訊的Channel Connecttion階段告知 RDP Server 綁定名稱為“MS_T120”的信道到指定信道,此時Server使用IcaFindChannelByName()函數搜索到默認創建的MS_T120信道,將該信道對象綁定到用戶指定的信道號上。此時是MS_T120信道第二次綁定。

  3. 至此,MS_T120信道已經完成2次綁定。隨后RDP Client 告知RDP Server斷開第二次綁定的信道,該操作會引發RDP Server調用IcaFreeChannel()釋放該信道并釋放該對象占用的空間。

  4. 隨后RDP Client 將通知RDP Server關閉RDP連接,此操作會引Server調用SingalBrokenConnection()函數釋放信道號0x1F的MS_T120信道對象。由于該對象空間已經釋放過,這里再次調用IcaFreeChannel()函數執行清理操作,其中ExDeleteResourceLite()會引用該對象的成員數據而觸發UAF漏洞。

3.2 調試過程

下圖是漏洞補丁修復前后對比圖:

b9ff2ddef7e8e3bd4151ee3f243306a7.png

關鍵的修改是針對_IcaBindChannel()函數的調用前增加了一個條件判斷,判斷的內容是stricmp()返回值,也就是字符串是否相等。

而以字面意思解讀icaFindChannelByName(),就是以名字查找信道。下圖為該函數的實現,通過遍歷列表,可以確定的是信道對象中偏移0x94的位置就是信道名稱。

0c6894e3077f7c8862f62e8f3d5dffa0.png

回過頭來看漏洞補丁的代碼,實際上打過補丁后,在調用icaBindChannel()函數的之前,也是進行信道名稱的判定,當信道名稱為”MS_T120” 的時候,后續調用icaBindChannel()的第三個參數,強制改為0x1F。

這里看一看icaBindChannel()函數的實現,關鍵在第12行的代碼中,會將傳入該函數參數1的信道指針,寫入一個內存地址中。顯而易見的是,寫入的地方是通過參數2及參數3計算得到。

4c35732d0b29e342962b131f6d08872d.png

實際上這個函數就是漏洞的關鍵,至于為什么關鍵,我們后面再談。首先先介紹一下,引起UAF的對象指針是何時創建的。

早在前文已經介紹過,RDP協議定義靜態虛擬信道,而名稱為MS_T120的信道就是其中一個。MS_T120在RDP協議建立之初,就會由RDP服務端主動創建,本次漏洞引起UAF的對象指針就是MS_T120信道。termdd!icaCreateChannel()函數用于創建信道,在該函數設置斷點,使用微軟遠程桌面連接工具連接并觀察一下該信道建立的過程。

c143b46be61ebadac03fec4cf85a401b.png

通過分析該函數代碼可知,參數2偏移0xC的位置為信道名稱。建立RDP連接,WinDbg停在IcaCreateChannel()處,其參數中的名稱正是MS_T120

7d945746ea609bca51e6b46be6af814e.png

進一步跟蹤該函數,進入了關鍵函數_IcaAllocateChannel(),如下圖,該函數首先調用ExAllocatePoolWithTag()申請空間,之后就是對象成員初始化工作。

a5e5ff96da215e59617e82226db07f89.png

037023801ea7cc98911053ea266c1c2a.png

值得注意的是,在初始化完部分成員變量之后,又調用了icaBindChannel(),在windbg中實時跟蹤該調用

a413ab07c687853bf25245d121b9bac3.png

可以發現,此時的參數3為0x1F,此時調用icaBindChannel()將新創建的MS_T120信道放入數組下標0x1F的位置。

也就是說,MS_T120信道對象指針在RDP 連接創建的時候就會建立,并立即綁定到0x1F信道號中。這是該指針創建的地方,在這里還將該信道綁定到了0x1F信道號中。

此時回過頭來看微軟補丁修復的地方,未修復之前,程序代碼在調用icaFindChannelByName()之后,緊接著調用icaBindChannel()將信道綁定到指定的信道號中。

8f55004fbc40139525b189a73068a534.png

在修復之后,會判斷信道是否為MS_T120,如果是,將綁定的信道號重定向為0x1F,而不是用戶指定的信道號。實際上UAF漏洞的關鍵就在這里,我們知道MS_T120信道在連接建立之初就已經和0x1F綁定,此時如果再次將MS_T120和另一個信道號綁定,在關鍵數組中就會存在2個指針值,也就是綁定了2次。

ccad2a6c9fc95bf793eae326bbc46977.png

目前在Github上(https://github.com/n1xbyte/CVE-2019-0708/ )有一份可以UAF導致藍屏的POC,下面跟蹤驗證一下。

306946f31828a8361fc628ca2d807c35.png

該POC使用Python編寫,在ubuntu上安裝python環境即可運行該POC

01568527d83cf3827f340fbb430d4776.png

該POC通過發送MCS Connect Initial請求,觸發RDP服務端中icaBindVirtualChannel()中引發UAF漏洞的代碼。

71a6f5a1db59a3704518f86b21ecf0f4.png

繼續單步走觀察調用_IcaBindChannel()時的信道號,下圖可見此時信道號為3.此時會將MS_T120信道與POC中指定的3號信道號綁定。

e900b8491bdabdb7727eb3be14394224.png

之后該POC會發送數據包通知RDP服務端斷開3號信道的連接,這將會引發服務端調用icaFreeChannel(),該函數會調用ExFreePoolWithTag() 釋放空間。

18eb38400782bae855e63fd7e7f82464.png

7e616bcc96069a12ac12061eb6540e90.png

之后POC通知RDP Sever關閉RDP連接,而在關閉連接的時候,會觸發默認的位于下標0x1F的信道釋放操作,如下圖所示(圖片為多次調試所截取, 其中關鍵指針值不同不要引起疑惑):

5efdca1f197ab8bc5e6c25ec452b77b5.png

這里繼續單步走, 可以發現觸發了內核異常。

15b81402a0af25434409ec00522a2c70.png

F5繼續運行系統,引發藍屏了。顯示如下

1d00d500bca296595d17492c06440f02.png

經過智能分析后如下,核心原因則是0x83e9b362處的代碼對ecx保存的內存地址進行了寫,可以看到的是,當時的ecx為0, 根據異常類型表示,當前IRQL無法對0地址進行讀寫。

3dfe85221a47bf1d878c98c5df983991.png

4d159f8a273cd130cefe2349fe316290.png

仔細看下2張圖可以發現,ecx 來源于edi,而edi 是icaFreeChannel()的傳入參數,也就是待釋放的信道對象指針。已釋放的指針被再次引用,所以導致了漏洞。

8aa64cf275588943179845a8e71adb2f.png

cfbcccb1954c04a7151b1542d55c6602.png

這里引用的來源即icaFreeChannel()中調用的ExDeleteResouceLite(),在釋放信道對象之前,會使用該對象的一些數據。

1fbd5eb1a7d08b5b5626a08c3cd8185d.png

四、 結語

通過以上的分析可知,MS_T120信道被綁定兩次(一次由RDPserver創建并綁定,第二次由我們發送數據包綁定)。由于信道綁定在兩個不同的ID下,我們得到兩個獨立的引用。

當使用其中的一個引用來關閉信道時,將刪除該引用,信道對象也將釋放。 但是,另一個引用仍然存在。如果我們可以在第一次釋放信道對象空間之后,通過內核POOL噴射,獲得在該信道對象填充自定義數據的能力,在第二次調用IcaFreeChannel()進行空間釋放時,由于該函數會引用已被控制的內核對象,就有機會造成讀寫任意內核地址進而達到任意代碼執行的目的。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/971/