Windows RDP协议 Fuzzing 漏洞挖掘研究
基本介紹
本文描述了我在Fuzzing Windows RDP 客戶端和服務(wù)端方面所做的一些嘗試和挑戰(zhàn),以及Crsahs分析。
Microsoft 的遠(yuǎn)程桌面協(xié)議 (RDP) 持續(xù)受到安全社區(qū)的關(guān)注。從 2019 年發(fā)現(xiàn)的幾個(gè)可能危及數(shù)百萬面向互聯(lián)網(wǎng)的服務(wù)端的關(guān)鍵漏洞,到RDP 被攻擊者用作主要的初始訪問向量之一。
我對(duì)這個(gè)目標(biāo)最初的興趣是與 VM 相關(guān)的。因?yàn)檫B接到 Azure Windows 機(jī)器或 Hyper-V 虛擬機(jī)的默認(rèn)方式是 RDP,所以我認(rèn)為 RDP 是比較重要的目標(biāo)。
我很快就發(fā)現(xiàn)了 Park、Jang、Kim 和 Lee在 BlackHat Europe 2019 上發(fā)表的關(guān)于 RDP Fuzzing的精彩分享。演講者在短短幾個(gè)小時(shí)內(nèi)使用一個(gè)并不高效的 fuzzer 發(fā)現(xiàn)了幾個(gè)漏洞,因此我決定在他們的工作基礎(chǔ)上進(jìn)行構(gòu)建新的挖掘工具,將 fuzzing 能力擴(kuò)展到其他方面,改進(jìn)其性能并挖掘到我自己的 RDP 遠(yuǎn)程代碼執(zhí)行 (RCE)漏洞。
不幸的是,并不是所有的Fuzzing工具都能挖到嚴(yán)重的漏洞。我目前還沒有挖到 RDP 的RCE漏洞,但我確實(shí)設(shè)法找到了一些bugs,并更好地了解了協(xié)議、其組件以及Fuzzing過程和工具。我開發(fā)的Fuzzing框架足夠通用,有助于對(duì)其他目標(biāo)進(jìn)行Fuzzing。
在這篇文章中,我將分享我執(zhí)行上述所有操作的過程。首先,我將概述 RDP 和開發(fā)的Fuzzing設(shè)置,然后將分享我臨的挑戰(zhàn)以及如何處理的方法,最后,我將回顧在此過程中發(fā)現(xiàn)的幾個(gè)漏洞。
1.RDP 協(xié)議
遠(yuǎn)程桌面協(xié)議是用于遠(yuǎn)程訪問 Windows 計(jì)算機(jī)的通用協(xié)議,Malwaretech 最近將其述為“協(xié)議的協(xié)議”。RDP 允許在每個(gè)連接中運(yùn)行多個(gè)通道,并且每個(gè)通道都有不同的用途。這意味著每個(gè)通道都有自己的代碼來處理其數(shù)據(jù)、自己的結(jié)構(gòu)定義和數(shù)據(jù)流。這實(shí)質(zhì)上意味著 RDP 中確實(shí)存在多種協(xié)議。
RDP 連接中的通道
RDP 通道可以是靜態(tài)的,也可以是動(dòng)態(tài)的,但對(duì)于我們的目的而言,兩者之間的區(qū)別并不重要。如果你想了解更多關(guān)于RDP連接的內(nèi)部運(yùn)作原理,我建議你閱讀下文。
2.RDP Fuzz的挑戰(zhàn)
在“標(biāo)準(zhǔn)”Fuzzing場景中,有一個(gè)程序讀取由Fuzzer控制的輸入,輸入可以是文件或任何類型的數(shù)據(jù)流。然后程序處理數(shù)據(jù),同時(shí)Fuzzer監(jiān)視生成的數(shù)據(jù)的代碼覆蓋率。基于該覆蓋范圍,Fuzzer對(duì)輸入進(jìn)行變異,再次將變異的輸入發(fā)送到程序,然后重復(fù)該過程。
RDP Fuzzing是不同的,因?yàn)槲冶仨毷冀K有一個(gè) RDP 連接處于活動(dòng)狀態(tài)。Fuzzer可以輸入到程序中的數(shù)據(jù)需要作為協(xié)議數(shù)據(jù)單元 (PDU) 發(fā)送到特定通道(在Fuzzing期間也應(yīng)處于活動(dòng)狀態(tài))的頂部,并在開放連接中進(jìn)行。如前所述,每個(gè)通道都有自己的協(xié)議,因此需要逐個(gè)通道進(jìn)行Fuzzing。這給Fuzzing過程帶來了以下挑戰(zhàn)(這也可能適用于其他協(xié)議/網(wǎng)絡(luò)相關(guān)的Fuzzing):
客戶端-服務(wù)端架構(gòu)——在傳統(tǒng)的Fuzzing中,Fuzzer可以簡單地運(yùn)行目標(biāo)應(yīng)用程序并提供其輸入。在客戶端-服務(wù)端場景中,目標(biāo)應(yīng)用程序在連接的一側(cè)運(yùn)行,而輸入從另一側(cè)發(fā)送。在RDP的情況下,雙方通常在不同的機(jī)器上。
狀態(tài)性- RDP 是一種有狀態(tài)協(xié)議,這意味著你必須在對(duì)測試用例進(jìn)行Fuzzing時(shí)考慮連接的狀態(tài)。這會(huì)嚴(yán)重影響Fuzzing的穩(wěn)定性。
多輸入Fuzzing——當(dāng)對(duì)接受文件作為輸入的目標(biāo)進(jìn)行Fuzzing(文件格式Fuzzing)時(shí),Fuzzer對(duì)目標(biāo)的所有輸入都包含在單個(gè)文件中。相反,當(dāng)你對(duì)協(xié)議進(jìn)行Fuzzing時(shí),你可能需要發(fā)送一些連續(xù)的消息才能到達(dá)有效的代碼路徑。
尋找目標(biāo)代碼——當(dāng)你使用覆蓋率引導(dǎo)的Fuzzing時(shí),你通常需要向Fuzzer表明它需要在什么時(shí)候開始監(jiān)控代碼覆蓋率(即,處理你輸入的目標(biāo)函數(shù)是什么?)。RDP 有許多負(fù)責(zé)其操作的組件,在某些情況下,找到正確的位置可能是一項(xiàng)艱巨的任務(wù)。
這四個(gè)挑戰(zhàn)是我預(yù)計(jì)Fuzzing RDP的主要挑戰(zhàn)。在文中,我將討論如何克服這些挑戰(zhàn),以及在工作后期出現(xiàn)的其他挑戰(zhàn)。
Fuzzing技術(shù)細(xì)節(jié)
在本節(jié)中,我將介紹技術(shù)細(xì)節(jié),以及在工作過程中遇到的挑戰(zhàn)。還將解釋如何解決它們以實(shí)現(xiàn)有效的Fuzzing設(shè)置。我在 GitHub 上創(chuàng)建了一個(gè)工作存儲(chǔ)庫,其中包含我在此工作中編寫的所有代碼。
1.客戶端-服務(wù)端架構(gòu)
在這個(gè)工作中,我想要Fuzzing Windows 的 RDP 服務(wù)端及其 RDP 客戶端。對(duì) RDP 服務(wù)端進(jìn)行Fuzzing的動(dòng)機(jī)很明顯:攻擊者可以使用它來遠(yuǎn)程入侵 Windows 服務(wù)端并獲得對(duì)其的訪問權(quán)限。對(duì) RDP 客戶端進(jìn)行Fuzzing的動(dòng)機(jī)是不同的,如果攻擊者已經(jīng)獲取了 RDP 服務(wù)端權(quán)限的場景,然后等待受害 RDP 客戶端連接過來。一旦受害者連接上,攻擊者也可以通過 RDP 客戶端獲取受害者的機(jī)器權(quán)限。當(dāng)管理員連接到他們管理的服務(wù)端時(shí)可能會(huì)發(fā)生這種情況,由于 Hyper-V 利用 RDP 訪問其虛擬機(jī),甚至可以用作 VM 逃逸。
RDP 的 fuzzer 需要具有以下基本組件:
?檢測引擎跟蹤代碼覆蓋并檢測崩潰
?變異引擎產(chǎn)生新輸入
?輸入饋送器通過適當(dāng)?shù)哪繕?biāo)通道發(fā)送fuzzer的測試用例
?目標(biāo)二進(jìn)制文件狀態(tài)由檢測引擎跟蹤
客戶端和服務(wù)端的Fuzzing配置不同,但也有一些相似之處。
基本Fuzzing配置
在目標(biāo)方面,有以下內(nèi)容:
?Fuzzer – 定制的 afl-fuzz.exe
?檢測引擎——定制的 winafl.dll,使用定制的 DynamoRIO 和in_app檢測模式
?輸入 ——被寫入目標(biāo)和輸入發(fā)送者共享目錄中的中間文件的變異 PDU
在輸入發(fā)送方,有一個(gè)組件:
?代理——讀取中間文件并在目標(biāo)通道上發(fā)送輸入
將輸入的生成與向目標(biāo)的傳輸分離,允許輸入在被目標(biāo)處理之前從 RDP 連接的一側(cè)移動(dòng)到另一側(cè)。此外,我使用 WinAFL 的應(yīng)用內(nèi)檢測模式來不中斷正常的執(zhí)行流程。
為了使覆蓋引導(dǎo)的Fuzzing工作,輸入和它們觸發(fā)的代碼路徑之間必須存在一一對(duì)應(yīng)的關(guān)系。為了實(shí)現(xiàn)這一點(diǎn),我開發(fā)了“background fuzzing”,它將Fuzzer PDU 與常規(guī) PDU 區(qū)分開來,并且只跟蹤前者的代碼路徑。這是必不可少的,因?yàn)槲抑幌M鸉uzzer跟蹤我自己測試用例的覆蓋范圍,而不是通過連接發(fā)送的隨機(jī) PDU。
為了說明這一點(diǎn),讓我看看在Fuzzing將音頻從 RDP 服務(wù)端重定向到客戶端的RDPSND虛擬通道時(shí),類似的事情會(huì)是什么樣子。根據(jù)官方文檔,每個(gè) PDU 的第一個(gè)字節(jié)表示發(fā)送的消息類型。
資料來源:[ MS-RDPEA ]
msgType字段支持的值為0x01到0x0D。在這種情況下,可以通過以下方式使用第一個(gè)字節(jié)的最高有效位作為fuzz標(biāo)記:
?在發(fā)送 PDU 之前,代理會(huì)轉(zhuǎn)換第一個(gè)字節(jié)的最高有效位。
?WinAFL 在處理消息之前檢查第一個(gè)字節(jié)的最高有效位。如果該位打開,WinAFL 將關(guān)閉該位并跟蹤此消息的代碼覆蓋率。如果該位關(guān)閉,WinAFL 將忽略該消息并且不跟蹤任何覆蓋范圍。
在了解了客戶端設(shè)置和服務(wù)端設(shè)置之間的相似之處之后,讓我看看它們之間的區(qū)別,先從客戶端開始。
2.客戶端Fuzz配置
Windows RDP 客戶端是mstsc.exe,但處理虛擬通道數(shù)據(jù)的大部分邏輯都在客戶端加載的mstscax.dll中。
Hyper-V 虛擬機(jī)的遠(yuǎn)程訪問客戶端vmconnect.exe也使用mstscax.dll作為其核心功能。
Windows RDP 客戶端
為了簡單和高效,我在同一臺(tái)機(jī)器上同時(shí)執(zhí)行客戶端和服務(wù)端(目標(biāo)和代理)(使用客戶端連接到 localhost/127.0.0)。為了允許并行Fuzzing,我還使用了mimikatz來修補(bǔ)服務(wù)端,使其允許并發(fā) RDP 連接。
這些是對(duì)客戶端進(jìn)行Fuzzing時(shí)的設(shè)置組件:
?目標(biāo) - mstsc.exe和mstscax.dll的目標(biāo)模塊
?檢測引擎 – 創(chuàng)建客戶端進(jìn)程的 DynamoRIO 和 winafl.dll,報(bào)告代碼覆蓋率的 DynamoRIO 客戶端
?變異引擎——在同一臺(tái)機(jī)器上運(yùn)行的 AFL-Fuzz 并將新的測試用例寫入文件
?輸入饋送器——我的 RDPFuzzAgent,它打開服務(wù)端的句柄并在選定的虛擬通道上發(fā)送 PDU。代理從 AFL-Fuzz 創(chuàng)建的文件中獲取每個(gè)測試用例,并將它們發(fā)送到目標(biāo)通道上。
使用這些組件,我能夠?qū)崿F(xiàn)大約每秒 50-100 次執(zhí)行的執(zhí)行速度。
3.服務(wù)端Fuzz配置
為了找到包含 RDP 服務(wù)端主要邏輯的目標(biāo)二進(jìn)制文件,可以簡單地查看遠(yuǎn)程桌面服務(wù)服務(wù)。
PS C:\> gci HKLM:\SYSTEM\CurrentControlSet\Services\$((Get-Service -Name "Remote Desktop Services").Name)Hive: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TermService Name Property ---- -------- Parameters ServiceDll : C:\Windows\System32\termsrv.dll . . .RDP 服務(wù)端的主要邏輯確實(shí)在termrv.dll 中,它通過以下命令加載到svchost.exe進(jìn)程中:C:\Windows\System32\svchost.exe -k NetworkService -s TermService。
最初對(duì)服務(wù)端進(jìn)行Fuzzing的計(jì)劃與客戶端類似,即在需要運(yùn)行多個(gè)TermService實(shí)例的同一臺(tái)機(jī)器上并行地對(duì)目標(biāo)的幾個(gè)實(shí)例進(jìn)行Fuzzing。結(jié)果證明這是一項(xiàng)非常具有挑戰(zhàn)性的任務(wù),因?yàn)?Windows 默認(rèn)不支持此操作。當(dāng)我嘗試手動(dòng)執(zhí)行此操作時(shí),我在termrv.dll中看到了一些指向TermService及其注冊(cè)表項(xiàng)的硬編碼字符串,因此我決定將精力集中在其他地方,只使用多個(gè) VM 并行fuzz服務(wù)端。
在客戶端Fuzzing設(shè)置中,我使用服務(wù)端API 調(diào)用WTSVirtualChannelWrite()將Fuzzing輸入發(fā)送到目標(biāo)模塊。不幸的是,我找不到類似的 API 來讓通過 RDP 連接向服務(wù)端發(fā)送輸入。
因此,我選擇使用 Ubuntu 機(jī)器的定制FreeRDP,是一個(gè)開源 RDP 客戶端工具,將輸入發(fā)送到fuzz服務(wù)端。請(qǐng)注意,這不是Fuzzing的理想配置,這些限制導(dǎo)致服務(wù)端Fuzzing的速度大約是客戶端Fuzzing的 1/10。
服務(wù)端fuzz統(tǒng)計(jì)
這些是對(duì)服務(wù)端進(jìn)行Fuzzing時(shí)的設(shè)置組件:
?目標(biāo)文件——取決于被Fuzzing的通道。可以是termrv.dll、audiodg.exe、rdpinput.exe等。
?檢測引擎 - 在此設(shè)置中,Fuzzer無法控制目標(biāo)進(jìn)程的初始化。因此,為了跟蹤代碼覆蓋率,必須檢測一個(gè)正在運(yùn)行的目標(biāo)。WinAFL Fuzzer 有幾個(gè)可以使用的檢測引擎,我選擇 DynamoRIO 是因?yàn)樗目蓴U(kuò)展性和穩(wěn)健性。
?DynamoRIO(包括WinAFL)不支持直接連接到活動(dòng)進(jìn)程。
?值得注意的是,此附加功能為之前使用 WinAFL 無法fuzz的進(jìn)程打開了大門——用戶無法控制其創(chuàng)建的進(jìn)程。特別是,它允許對(duì) Windows 服務(wù)進(jìn)行Fuzzing。
?變異引擎——AFL-Fuzz 在 Windows 機(jī)器上運(yùn)行,并將新的測試用例寫入共享文件夾中的文件。
?輸入饋送器——我們構(gòu)建的FreeRDP連接到Windows計(jì)算機(jī)上的服務(wù)器,監(jiān)視共享文件夾中的新測試用例,并在目標(biāo)RDP通道上發(fā)送每個(gè)測試用例。
4.狀態(tài)性
在運(yùn)行第一個(gè)通道(對(duì)客戶端進(jìn)行Fuzzing)時(shí),我遇到了一個(gè)問題:只要在客戶端檢測到兩條無效消息,它就會(huì)立即終止連接。
為了避免這個(gè)問題,我在代理的邏輯中引入了一些強(qiáng)制協(xié)議語法,即限制允許輸入的空間。
?消息大小限制
-最小值、最大值
-分片(例如,如果消息包含一個(gè)由 4 字節(jié)元素組成的可變長度數(shù)組,則它應(yīng)該可以被 4 整除)
?數(shù)據(jù)值限制
-只允許特定值
-最小值和最大值
-值必須是 PDU 大小
-每個(gè) PDU 中的值必須不同
我從 Microsoft 的RDP 及其擴(kuò)展文檔中提取了一些 RDP 語法,一些從相關(guān)二進(jìn)制文件的逆向中提取,其余從跟蹤失敗的目標(biāo)執(zhí)行中提取。
例如,此邏輯可用于僅允許以受支持的msgTypes之一開頭的消息,后跟 PDU 的大小和唯一標(biāo)識(shí)符,并且其總大小介于 22 和 122 之間,其余為 2 模 4 .
值得一提的是,通過執(zhí)行這些強(qiáng)制措施,實(shí)際上限制了變異引擎隨心所欲更改測試用例的能力,因此可能會(huì)遺漏某些變異。出于這個(gè)原因,我嘗試盡可能少地強(qiáng)制執(zhí)行,同時(shí)仍然確保連接不會(huì)經(jīng)常關(guān)閉。
這里的另一個(gè)重點(diǎn)是,在處理此類問題時(shí),這些語法強(qiáng)制并不是唯一的選擇。在一個(gè)特定通道 (GFX) 的情況下,我轉(zhuǎn)而patch正在Fuzzing的實(shí)際目標(biāo)函數(shù),以便在一組無效消息的情況下它不會(huì)關(guān)閉連接。這使我能夠繼續(xù)fuzz無效消息并始終保持連接打開。在這里,你也有可能發(fā)現(xiàn)不會(huì)在原始代碼中重現(xiàn)的crashs。這是一個(gè)很好的例子,說明Fuzzing需要在確保獲得足夠的執(zhí)行速度的同時(shí)仍然保持目標(biāo)程序的原始功能以及變異引擎的自由之間的微妙平衡。
5.多輸入Fuzzing
大多數(shù)bug都依賴于一系列消息而不是單個(gè)消息。我發(fā)現(xiàn)的大多數(shù)bugs都涉及至少兩條消息,這并非巧合。
為了發(fā)現(xiàn)這些bugs,我引入了多輸入Fuzzing。我使用了一個(gè)Fuzzer字典來識(shí)別新消息的開始及其類型。然后,代理將根據(jù)這些字典單詞將輸入拆分為多個(gè) PDU,并一個(gè)接一個(gè)地發(fā)送。
因此,多輸入輸入可能如下所示:
___cmd07 < 1st PDU data> ___cmd02 < 2nd PDU data> ___cmd03 < 3rd PDU data>代理將其轉(zhuǎn)換為msgType 7、2和 3 的三個(gè)消息,以及它們各自的內(nèi)容。
為了保持Fuzzer創(chuàng)建的輸入與其觸發(fā)的代碼覆蓋率之間的一一對(duì)應(yīng)關(guān)系,我引入了第二個(gè)標(biāo)記來標(biāo)識(shí)序列中的最后一條消息。只有當(dāng) WinAFL 識(shí)別出帶有“最后一個(gè)序列”標(biāo)記的調(diào)用結(jié)束時(shí),它才會(huì)完成循環(huán)并創(chuàng)建下一個(gè)輸入。
雖然多輸入Fuzzing對(duì)當(dāng)前工作至關(guān)重要并且富有成效,但我還發(fā)現(xiàn)有必要限制每個(gè)測試用例的 PDU 數(shù)量。因?yàn)镕uzzer重復(fù)相同的消息 100 次會(huì)導(dǎo)致與發(fā)送一次不同的代碼序列。
6.Crashs重現(xiàn)問題
經(jīng)過大約一周的Fuzzing,第一次崩潰出現(xiàn)了。但是,當(dāng)我再次嘗試運(yùn)行相同的輸入時(shí),崩潰并沒有重現(xiàn)。這種情況經(jīng)常發(fā)生,很可能是由于協(xié)議的狀態(tài)性質(zhì)。換句話說,一個(gè)測試用例讓客戶端進(jìn)入一個(gè)特定的狀態(tài),然后被后續(xù)的測試用例“利用”來使目標(biāo)崩潰。
為了理解不可重現(xiàn)的崩潰,我修改了 WinAFL 以在檢測到崩潰時(shí)創(chuàng)建目標(biāo)進(jìn)程的內(nèi)存轉(zhuǎn)儲(chǔ)。
7.Crashs自動(dòng)化分析
在崩潰時(shí)創(chuàng)建轉(zhuǎn)儲(chǔ)解決了一個(gè)問題,但造成了另一個(gè)問題:一旦發(fā)現(xiàn)崩潰,很可能會(huì)重復(fù)遇到。通常,WinAFL 會(huì)嘗試檢測相同的崩潰并僅通知“特殊的崩潰”,但是我的多消息Fuzzing使這種檢測變得非常困難。考慮單個(gè)消息導(dǎo)致目標(biāo)崩潰的情況。fuzzer 可以在最后創(chuàng)建任何一組消息。這些消息集中的每一個(gè)都會(huì)使目標(biāo)崩潰,并且還會(huì)導(dǎo)致不同的覆蓋位圖,因?yàn)橄⒓捌涮幚矸绞讲煌?#xff0c;這將導(dǎo)致 WinAFL 每次都報(bào)告一個(gè)特殊的崩潰。
出于兩個(gè)原因,我必須自動(dòng)分析崩潰。首先,手動(dòng)分析每個(gè)崩潰的工作很繁瑣,其次,磁盤很快被內(nèi)存轉(zhuǎn)儲(chǔ)填滿。
為了克服這個(gè)問題,我編寫了一個(gè) WinDBG 腳本來分析崩潰并從中提取崩潰堆棧。然后,我運(yùn)行了一個(gè) PowerShell 腳本,該腳本定期分析崩潰并僅保留那些包含新堆棧的崩潰,并通過電子郵件向我發(fā)送消息。
在客戶端Fuzzing設(shè)置中,從Fuzzer創(chuàng)建目標(biāo)(mstsc.exe)的那一刻,到建立連接并可以發(fā)送第一條消息的那一刻,花費(fèi)了 10 多秒鐘。因此,在不重新啟動(dòng)目標(biāo)的情況下執(zhí)行盡可能多的迭代至關(guān)重要。我通過使用AFL-Fuzz的-fuzz_iterations參數(shù)并提供盡可能多的迭代來實(shí)現(xiàn)這一點(diǎn)。
8.多通道Fuzzing
與多輸入Fuzzing一樣,某些邏輯需要不同通道上的一系列消息。例如,如下面文檔中所述,使用多個(gè)通道支持從客戶端向服務(wù)端發(fā)送相機(jī)數(shù)據(jù)。
資料來源:[ MS-RDPECAM ]
因此,如果希望通過發(fā)送輸入來fuzz服務(wù)端,必須至少在兩個(gè)不同的通道上這樣做。
我的解決方案也很相似:fuzzer 字典確定了要發(fā)送消息的通道。
9.定位相關(guān)代碼
由于 RDP 在 Windows 中有許多不同的組件,甚至定位需要Fuzzing的目標(biāo)函數(shù)都具有挑戰(zhàn)性。
PS C:\> gci -Include *.exe, *.dll, *.sys -Recurse C:\Windows\ -ErrorAction SilentlyContinue | ?{[System.Diagnostics.FileVersionInfo]::GetVersionInfo($_).FileDescription -match "RDP|Remote Desktop"} | Measure-Object | select count Count -----191為了快速做到這一點(diǎn),我創(chuàng)建了一個(gè)小型數(shù)據(jù)庫,其中包含可能與我的工作相關(guān)的所有符號(hào)文件。
我的想法是下載與我的 Windows 版本相關(guān)的所有 PDB,從中提取所有函數(shù)名稱并將它們轉(zhuǎn)儲(chǔ)到一個(gè)文件中(鏈接回 exe/sys/dll),以便可以快速搜索函數(shù)名稱和定位與我當(dāng)前目標(biāo)通道相關(guān)的功能。
由于幾乎所有動(dòng)態(tài)通道接收函數(shù)都匹配以下模式C< class-name >::OnDataReceived,我們可以快速查看這些函數(shù)的列表并找出可能與我們所針對(duì)的通道相關(guān)的內(nèi)容。
Crashs漏洞分析
在本節(jié)中,我將分享我在該工作中發(fā)現(xiàn)的兩個(gè)bugs的技術(shù)細(xì)節(jié)。
1.AUDIO_PLAYBACK 通道(服務(wù)端→客戶端)
該AUDIO_PLAYBACK_DVC虛擬通道用于輸出從客戶端上輸入的服務(wù)端數(shù)據(jù)。它的正常流程包括兩個(gè)序列:初始化和數(shù)據(jù)傳輸。在協(xié)議的正常使用中,初始化序列在開始時(shí)發(fā)生一次,然后是許多數(shù)據(jù)傳輸序列。
?初始化序列——用于建立以下數(shù)據(jù)序列中使用的版本和格式
資料來源:[ MS-RDPEA ]
?數(shù)據(jù)傳輸序列——來自服務(wù)端的數(shù)據(jù)將在客戶端輸出
資料來源:[ MS-RDPEA ]
Wave和WaveInfo PDU包含初始化序列中交換的格式數(shù)組的索引,用于確定傳輸音頻數(shù)據(jù)的格式。
資料來源:[ MS-RDPEA ]
當(dāng)格式發(fā)生變化時(shí)ーー例如,Wave 或 WaveInfo PDU 的索引與上次使用的索引不同,客戶端會(huì)驗(yàn)證新索引是否有效。
// in mstscax!CRdpAudioController::OnNewFormat if ( (unsigned int)new_format_index >= this->formatArray_size )但是,只要格式索引保持不變,就會(huì)跳過此驗(yàn)證,下面是相關(guān)部分的偽代碼。
// in mstscax!CRdpAudioController::OnWaveData last_format_index = this->last_format_index; format_index_from_pdu = *((_WORD *)pdu + 3); //pdu is controlled by the server if ( last_format_index != format_index_from_pdu ) {CRdpAudioController::OnNewFormat(this, (__int64 *)format_index_from_pdu); // this is where the bound check is being made// but only if the format index is different than the last indexlast_format_index = *((unsigned __int16 *)pdu + 3);this->last_format_index = last_format_index; } formats_array = (AUDIO_FORMAT **)this->formatArray; current_format = formats_array[last_format_index]->wFormatTag; // crashes here在客戶端觸發(fā)此漏洞的流程如下:
漏洞流程
?服務(wù)端向客戶端發(fā)送具有0x1A格式的服務(wù)端格式 PDU,客戶端分配具有此大小的格式數(shù)組。
?服務(wù)端向客戶端發(fā)送一個(gè)Wave2 PDU,該P(yáng)DU使用數(shù)組中的格式0x5和數(shù)據(jù)。
-客戶端檢查此格式是否與上次發(fā)送的格式相同。
-如果是,它使用最后一個(gè)解碼器;如果不是,它將從格式數(shù)組加載新的解碼器函數(shù)指針。
?服務(wù)端再次向客戶端發(fā)送服務(wù)端音頻格式 PDU——這次只有0x2,導(dǎo)致客戶端釋放之前的格式數(shù)組并分配一個(gè)具有新大小的新格式。
?服務(wù)端最終使用最后使用的格式0x5發(fā)送另一個(gè)Wave2 PDU。
-由于格式?jīng)]有改變,客戶端不執(zhí)行任何有效性檢查。
-然后,客戶端執(zhí)行越界讀取,嘗試從 2 格式數(shù)組中讀取第6種格式造成崩潰。
這允許攻擊者使用額外的服務(wù)端音頻格式 PDU 重新分配格式數(shù)組,然后指定以前有效和使用的無效索引,導(dǎo)致客戶端讀取格式數(shù)組的邊界并崩潰。
請(qǐng)注意,此bugs在很大程度上依賴于多輸入Fuzzing,如果沒有此功能,我們將無法找到它。
2.AUDIO_INPUT 通道(客戶端→服務(wù)端)
所述AUDIO_INPUT虛擬信道用于從所述客戶端發(fā)送的聲音輸入到服務(wù)端。在服務(wù)端端,音頻輸入數(shù)據(jù)由提升的audiodg.exe進(jìn)程處理。
在AUDIO_PLAYBACK_DVC通道中,客戶端和服務(wù)端首先交換它們支持的一系列聲音格式。
資料來源:[ MS-RDPEAI ]
所述聲音格式的PDU開始的9個(gè)字節(jié)的報(bào)頭,其包括指令,格式編號(hào),和該分組的大小,接著格式的陣列,每個(gè)可變長度,加上附加數(shù)據(jù)的一個(gè)可選的字段。
資料來源:[ MS-RDPEAI ]
處理聲音格式 PDU的代碼在rdpendp.dll 中。它首先驗(yàn)證數(shù)據(jù)包大小至少為九個(gè)字節(jié),然后讀取頭部并驗(yàn)證頭部的大小不大于數(shù)據(jù)包的大小。
// in rdpendp!CAudioInputHandler::OnFormatsReceivedif ( size < 9 ){// ...}// ...size_from_msg = *(_DWORD *)(data + 5);if ( size_from_msg > size ){// ...}然后相同的函數(shù)從header讀取的大小中減去 9,并讀取header中指定的格式數(shù),只要剩余長度足夠大。header的大小不受整數(shù)下溢保護(hù),這可能會(huì)導(dǎo)致減法溢出,并導(dǎo)致程序從數(shù)據(jù)包末尾讀取“formats”。
// in rdpendp!CAudioInputHandler::OnFormatsReceived underflowed_size = size_from_pdu - 9; format_definition_offset = (unsigned __int16 *)(pdu + 9); if ( num_formats ) {while ( underflowed_size >= 0x12 ){format_definition_size = format_definition_offset[8];total_format_size = format_definition_size + 18;if ( underflowed_size < (unsigned __int64)(format_definition_size + 18) )break;(*class_fomats_array)[format_index] = (struct SNDFORMATITEM *)operator new[](total_format_size);local_format = (*class_fomats_array)[format_index];if ( !local_format ){status = E_OUTOFMEMORY;goto CLEAN_AND_RETURN;}memcpy_0(local_format, format_definition_offset, total_format_size);format_definition_offset = (unsigned __int16 *)((char *)format_definition_offset + total_format_size);underflowed_size -= total_format_size;if ( ++format_index >= num_formats )goto LABEL_50;}goto INVALID_ARG_EXIT; }研究總結(jié)
在這篇文章中,我記錄了我嘗試解決具有挑戰(zhàn)性的Fuzzing目標(biāo)的過程:Windows 的 RDP 客戶端和服務(wù)端。我想分享我的過程有幾個(gè)原因。
首先,我認(rèn)為即使你無法實(shí)現(xiàn)最初的目標(biāo)(例如 RCE),分享流程也很重要。這可以幫助你反思自己的流程——哪些看起來運(yùn)作良好,哪些可以改進(jìn)。
其次,盡管設(shè)置Fuzzing環(huán)境可能是一個(gè)復(fù)雜的過程,但我認(rèn)為這是一個(gè)值得追求的目標(biāo)——即使是像我在這里提出的更具挑戰(zhàn)性的目標(biāo)。RDP 是一個(gè)非常復(fù)雜的協(xié)議,具有許多組件和不同的代碼庫。Shodan.io搜索超過 400 萬臺(tái)使其成為攻擊者非常有利可圖的目標(biāo)。
最后
為了展示我的Fuzzing的通用性,我已經(jīng)將其應(yīng)用于幾個(gè) RPC 服務(wù)端,并取得了一些初步成果。
【網(wǎng)絡(luò)安全學(xué)習(xí)攻略】
總結(jié)
以上是生活随笔為你收集整理的Windows RDP协议 Fuzzing 漏洞挖掘研究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常见的安全应用识别技术有哪些?
- 下一篇: 用ASM编写一个简单的Windows S