作者:Eyal Itkin
原文鏈接:https://research.checkpoint.com/2020/apache-guacamole-rce/
翻譯:知道創宇404實驗室

概述

在許多公司,日常工作包括每天到辦公室在公司計算機上安全地使用公司內網。有時,員工可能需要特殊的異地訪問,并使用工具遠程連接到公司的網絡。

然而,自從 COVID-19 大流行爆發以來,這種日常工作已經發生了逆轉。在 Check Point,與世界各地許多其他的公司一樣,現在絕大多數工作都是遠程完成的。這種從現場工作到異地工作的轉變意味著現在比以往任何時候都更多地使用用于遠程連接到公司網絡的 IT 解決方案。這也意味著這些解決方案中的任何安全漏洞都會產生更大的影響,因為公司依靠這項技術來保持其業務正常運轉。

Apache Guacamole是一種流行的遠程工作基礎架構,在全球的docker 下載量超過1000 萬次。在我們的研究中,我們發現 Apache Guacamole 容易受到幾個關鍵的反向 RDP 漏洞的攻擊,并且還受到FreeRDP 中發現的一些新漏洞的影響。簡而言之,這些漏洞允許已經成功入侵組織內部計算機的攻擊者在毫無戒心的工作人員嘗試連接到受感染的計算機時對 Guacamole 網關發起攻擊。然后黑客可以完全控制服務器,并攔截和控制所有其他連接的會話。

在這個簡短的視頻演示中,我們展示了如何設法利用這些漏洞并成功控制 Guacamole 網關和所有連接的會話:

https://youtu.be/vm8i9jJ2Np0

介紹 Apache Guacamole

我們決定將遠程工作的技術解決方案作為一個有趣的研究課題。事實上,我們的 IT 部門立即懇請我們審查一個這樣的解決方案:Apache Guacamole。如前所述,它是市場上較為突出的工具之一。不僅許多組織使用該產品連接到他們的網絡,許多網絡和安全產品也在他們自己的產品中嵌入了 Apache Guacamole。包括:Jumpserver Fortress、Quali、Fortigate等等。

經過一番摸索,我們畫出了推薦的網絡架構的基本草圖,如圖1所示:

guacamole_figure_1

圖 1:部署 Apache Guacamole 網關的典型網絡架構。

從本質上講,員工使用瀏覽器連接到公司面向 Internet 的服務器,通過身份驗證過程,然后訪問他的公司計算機。當員工只使用他的瀏覽器時,guacamole服務器會選擇一種支持的協議(RDP、VNC、SSH 等)并使用開源客戶端連接到特定的公司計算機。連接后,guacamole服務器充當中間人,在將事件從所選協議轉換為特殊的“guacamole協議”時來回中繼事件,反之亦然。

現在我們了解了架構,還有一些有希望的攻擊向量需要檢查:

1、反向攻擊場景:企業網絡內的一臺受感染機器利用傳入的良性連接攻擊網關,旨在接管它。

2、惡意員工場景:一名惡意員工使用網絡內的計算機來利用他對連接兩端的控制并控制網關。

我們需要一個 0-Day 嗎?

在我們深入研究代碼之前,讓我們簡要地關注一下 FreeRDP。在我們之前對反向 RDP 攻擊的研究中,我們發現了這個 RDP 客戶端中的幾個關鍵漏洞,使其暴露在惡意 RDP“服務器”的攻擊之下。換句話說,惡意公司計算機可以控制連接到它的毫無防備的 FreeRDP 客戶端。我們甚至為我們的一個漏洞 ( CVE-2018-8786 ) 提供了一個基本的 PoC,并且我們已經演示了遠程代碼執行。

通過查看Apache Guacamole已發布的版本,我們可以看到只有 2020 年 1 月底發布的 1.1.0 版本增加了對最新 FreeRDP 版本(2.0.0)的支持。知道我們在 FreeRDP 中的漏洞僅在 2.0.0-rc4 版本上進行了修補,這意味著2020 年 1 月之前發布的所有版本都使用易受攻擊的 FreeRDP 版本。

我們本可以在這里停下來,估計大多數公司尚未升級到最新版本的可能性很高,并且可能已經使用這些已知的 1-Days 進行了攻擊。但是,我們決定再次搜索 RDP 協議中的漏洞,更具體地說:

1、guacamole-server的代碼,同時只關注對RDP協議的支持。

2、最新發布的 FreeRDP 版本代碼:2.0.0-rc4 版。

最重要的是,我們的利用條件是它能夠在默認安裝上工作,僅使用默認啟用的功能,并且希望不需要來自客戶端的任何交互。開始吧。

尋找新的漏洞

熟悉 FreeRDP 的代碼,以及整個 RDP,在這次安全審計中真的很有幫助。我們很快就開始尋找漏洞。

CPR-ID-2141 – 我們的第一個信息泄露漏洞

CVE: CVE-2020-9497

文件: protocols\rdp\channels\rdpsnd\rdpsnd-messages.c

功能: guac_rdpsnd_formats_handler()

旁注:由于 Apache 沒有使用我們報告的漏洞 (CPR-ID) 和他們發布的 CVE-ID 之間的 1:1 映射,我們將主要參考漏洞通過他們的(更準確的)CPR-ID

為了在 RDP 連接和客戶端之間中繼消息,開發人員為默認 RDP 通道實現了他們自己的擴展。一個這樣的通道負責來自服務器的音頻,因此毫不奇怪地稱為rdpsnd(RDP 聲音)。

然而,通常情況下,guacamole-serverFreeRDP 之間的集成點被證明是容易出錯的。傳入的消息由 FreeRDP 的wStream對象包裝,并且應使用此對象的 API 解析數據。但是,如圖 2 所示,開發人員忘記強制傳入流對象必須包含與數據包聲明的字節數匹配的字節數。

guacamole_figure_2

圖 2:缺少輸入過濾導致越界讀取。

通過發送惡意的rdpsnd通道消息,惡意的 RDP 服務器可能會導致客戶端認為該數據包包含大量字節,這些字節實際上是客戶端本身的內存字節。這反過來會導致客戶端用這些字節向服務器發回響應,并給 RDP 服務器一個大量的、心臟出血式的信息泄露原語。

CPR-ID-2142 – 再次信息泄露

CVE: CVE-2020-9497

文件: protocols\rdp\channels\rdpsnd\rdpsnd.c

功能: guac_rdpsnd_process_receive()

在同一個 RDP 通道中,不同的消息具有類似的漏洞。這次它將越界數據發送到連接的客戶端,而不是返回到 RDP 服務器。

guacamole_figure_3

圖 3:類似的越界讀取,這次是將數據泄露給客戶端。

雖然有用,但這種泄露會將信息發送給客戶端,我們希望在客戶端甚至不知道網關受到攻擊的情況下構建漏洞。

CPR-ID-2143 – 仍然是,信息泄露

CVE: CVE-2020-9497

文件: protocols\rdp\plugins\guacai\guacai-messages.c

功能: guac_rdp_ai_read_format()

我們很想找到一個額外的頻道guacai,負責聲音信息。該通道負責“音頻輸入”,因此得名guacai。盡管容易受到與前一個頻道大致相同的漏洞的影響,但默認情況下該頻道是禁用的。

圖 4:另一個越界讀取,就像第一個一樣。

在我們研究的這一點上,我們發現了 3 個主要的信息泄露漏洞,這對于繞過 ASLR(地址空間布局隨機化)應該綽綽有余。然而,我們仍然需要一個內存損壞漏洞來完成我們的漏洞利用鏈。感覺卡住了,我們又去看看 FreeRDP,希望能找到我們之前研究中可能遺漏的漏洞。

FreeRDP,我們的老朋友

自從我們上次查看 RDP 客戶端以來,沒有對其進行太多更改;補丁版本仍然是迄今為止發布的最新版本。在尋找漏洞時總是如此,讓我們首先了解wStream該客戶端使用的類型中的一個關鍵設計“功能” 。在圖 5 中,我們可以看到這個結構體的字段:

guacamole_figure_5

圖5:該wStream對象,用來包裹傳入/傳出分組。

這是一個簡單的流包裝器的經典示例:

buffer – 指向接收包開始的指針。

pointer – 指向接收數據包內的讀取頭的指針。

length – 傳入數據包的大小,以字節為單位。

在從輸入流解析給定字段之前,應進行檢查以確保流足夠大以容納它。這樣的檢查可以在圖 6 中看到:

guacamole_figure_6

圖 6:檢查可用輸入,使用Stream_GetRemainingLength().

我們再怎么強調這個輸入檢查的重要性都不為過。每次解析或跳過字段時,指針字段都會相應地前進。稍后,當執行下一次檢查時,它看起來像這樣:

guacamole_figure_7

圖 7:使用當前流的頭部計算剩余長度。

一旦指針字段通過傳入數據包的末尾,此計算將下溢,從而返回一個巨大的無符號值,該值應表示剩余的負字節數。總之,漏了一個檢查,剩下的就沒用了。正如我們很快發現的那樣,這種有趣的設計選擇使 FreeRDP極易受到越界讀取漏洞的影響。

在我們介紹這些越界讀取漏洞之前,重要的是要注意我們為什么關心它們。通常,讀取只有在以某種方式將讀取的字節返回給攻擊者時才有用。否則,讀取只能用作在嘗試訪問內存中未映射的頁面時使程序崩潰的一種方式。Apache Guacamole 攻擊場景很特別,因為我們擁有連接的兩端。例如,如果內存字節被解析為屏幕的圖形更新,這些更新仍將發送到連接的客戶端。

在這種攻擊場景中,每個越界讀取漏洞都可能變成一個弱但仍然有用的信息披露。

CPR-ID-2145 和 CPR-ID-2146 – FreeRDP 中的越界讀取

記住wStream對象中有趣的設計缺陷,我們所要做的就是尋找不受檢查支持的讀取操作。這很有效,我們發現了兩個這樣的漏洞:CPR-ID-2145CPR-ID-2146

但是,在向供應商報告它們時,我們發現它們都已經被兩個不同的小組報告了。由于這些是重復的,即使我們僅在幾個小時后提交了它們,也應該將它們歸于合法的研究人員。

因此,我們決定讓其他團隊展示他們的發現更為合適,并從我們的博客文章中刪除了有關他們的詳細信息。

我們需要一個內存損壞......

此時,我們發現了 5 個漏洞,這些漏洞可以作為我們攻擊中的信息披露利用原語。但是,我們甚至還沒有發現一個內存損壞漏洞。在 FreeRDP 中尋找此類漏洞非常煩人,因為每次我們有線索時,檢查都會阻止它。很多時候,這個檢查是針對我們報告的漏洞的補丁,所以我們真的不能抱怨太多。

Zensploitation Twitter(@zensploitation)中的這篇帖子幾乎總結了我們在研究中此時的感受:

guacamole_figure_8

圖 8:https://twitter.com/zensploitation/status/1244598246879547393

在這一點上,我們認為我們已經走得太遠了,不能簡單地放棄。我們決定再看一遍guacamole服務器,這一次我們收獲頗豐。

CPR-ID-2144 – 最后,內存損壞

CVE: CVE-2020-9498

文件: protocols\rdp\plugins\guac-common-svc\guac-common-svc.c

功能: guac_rdp_common_svc_handle_open_event()

RDP 協議將不同的“設備”公開為單獨的“通道”,每個設備一個。這些包括rdpsnd聲音通道、cliprdr剪貼板通道等等。作為一個抽象層,通道消息支持分片,允許它們的消息長達 4GB。為了正確支持rdpsnd和rdpdr(設備重定向)通道,guacamole-server 的開發人員添加了一個額外的抽象層,在文件中實現:guac_common_svc.c.圖 9 顯示了在此文件中實現的碎片處理:

guacamole_figure_9

圖 9:處理傳入的通道片段。

我們可以看到第一個片段必須包含該CHANNEL_FLAG_FIRST片段,并且在處理時根據總消息的總聲明長度分配一個流。

但是,如果攻擊者發送沒有此標志的片段會發生什么?它似乎只是簡單地附加到以前的剩余流中。在這一點上,這看起來是一個很有前途的 Dangling-Pointer 漏洞。現在我們只需要檢查開發人員是否記得將其設置NULL為前一個碎片消息完成處理時。

guacamole_figure_10

圖 10:在不清除懸空指針的情況下釋放使用的流。

圖 10 清楚地表明,在碎片消息完成重組并繼續解析后,它被釋放。就是這樣。沒有人將懸空指針設置為NULL!

惡意 RDP 服務器可能會發送使用先前釋放的wStream對象的亂序消息片段,從而有效地成為一個 Use-After-Free 漏洞。最重要的是,這wStream是我們希望為此類漏洞獲得的最強大的對象,因為如果將指針字段設置為所需的內存地址,它可以用于任意寫入。最重要的是,rdpsnd在我們損壞的wStream對象被使用后,我們在通道中有一個有用的信息泄露漏洞。通過一些努力,一個特制的wStream對象可以將我們的原始漏洞變成一個更強大的任意讀取漏洞利用原語。

最后,遠程代碼執行 (RCE)

如前所述,通過使用漏洞 CVE-2020-9497CVE-2020-9498,我們設法實現了我們的任意讀取和任意寫入漏洞利用原語。使用這兩個強大的原語,我們成功實現了遠程代碼執行漏洞利用,其中guacd當遠程用戶請求連接到他的(受感染)計算機時,惡意公司計算機(我們的 RDP“服務器”)可以控制進程。

guac_pe_fig_1

圖 11:漏洞利用截圖——從接管guacd過程中彈出一個計算。

但并沒有就此結束。該guacd進程僅處理單個連接并以低權限運行。傳統上,此時我們需要一個權限提升漏洞來接管整個網關。事實上,在與 Apache 協調披露期間,維護人員提出的問題之一是這種攻擊場景是否真的可能發生。我們能否以某種方式僅從一個guacd進程接管網關中的所有連接?

讓我們來了解一下。

Apache Guacamole – 深入探討

如果我們深入研究我們之前看到的 Guacamole 網關的網絡架構,我們會看到以下內容:

guac_pe_fig_2

圖 12: Apache Guacamole 架構的重點視圖。

對于權限提升,我們的重點是以下兩個組成部分:

  • guacamole-client – 標記為Web Server。

  • guacamole-server – 標記為Proxy。

guacamole客戶端

guacamole-client 組件負責執行用戶身份驗證的 Web 服務器。該 Web 服務器保存每個用戶會話所需的配置,存儲如下信息:

通緝協議——通常是 RDP。
網絡內工作人員 PC 的 IP 地址。

等等。

客戶端成功通過身份驗證后,guacamole-clientguacamole-server 發起 Guacamole 協議會話,為客戶端創建匹配會話。這是通過連接到guacd進程正在偵聽的TCP 端口 4822(默認情況下)上的 guacamole-server 來完成的。

創建會話后,guacamole-client 僅在 guacamole-server 和客戶端瀏覽器之間來回傳遞信息。

guacamole服務器

根據 Apache 的文檔:“guacdGuacamole 的核心。” 啟動時,guacd偵聽 TCP 端口 4822 并等待來自 guacamole-client 的傳入指令。請務必注意,此端口上的通信不使用身份驗證或加密(可以啟用 SSL,但它不是默認設置)。為此,我們在圖 12 中添加了兩個防火墻,它們應該負責限制對這個 TCP 端口的訪問,只允許 guacamole-client 連接。

建立連接后,guacd創建一個新線程并調用負責啟動 Guacamole 協議的函數。此時,有兩個用戶選項:

創建新連接。
加入現有連接

旁注:我們使用術語connection而不是session,因為這是 Guacamole 用來指代與給定計算機的連接的術語。每臺計算機都有一個連接,多個用戶可以共享同一個連接。沒有“用戶會話”,因為整個設計基于與給定計算機的 Guacamole 連接,用戶只需加入連接即可。

第一種選擇是迄今為止使用最廣泛的。在這種情況下,會為新創建的連接生成一個隨機唯一id (UUID),并為其生成一個fork()ed 進程。UUID 和新進程之間的映射存儲在一個名為的內存字典中proc-map,并且 UUID 被發送回 guacamole 客戶端。需要注意的是,生成的進程會在啟動與網絡內部計算機的連接之前立即放棄其權限。

第二個選項非常獨特,可能是為了讓多個用戶可以共享一個連接并一起工作而實現的。在這種情況下,用戶通過提供連接的 UUID 請求加入現有連接。為了區分用戶,創建連接的用戶是“所有者”,其他用戶將“所有者”設置為false。此選項還包括為未標記為“所有者”的用戶建立只讀連接的可能性。

guac_pe_fig_3

圖 13:添加新用戶并將過程存儲在 中proc-map以允許其他人加入。

為了支持加入用戶,給定連接的衍生進程繼承了用于與父guacd進程通信的套接字對。當主線程初始化所需的客戶端時,例如,用于 RDP 連接的 FreeRDP,另一個線程等待來自父進程的消息,向我們的進程發出新用戶要求加入連接的信號。

該guacd進程充當產生每個連接進程的連接管理器,同時也為這些產生的進程實現核心邏輯。因此,從現在開始,我們將父guacd進程稱為主進程。

權限提升 – 分步

步驟 0 – 接管單個 guacd 進程

我們已經有了這部分的有效利用。

步驟 1 – 偽裝成guacamole客戶

雖然guacd我們控制的進程只是在網關內運行的低權限進程,但它仍然具有一些有用的權限。首先,在網關上運行使我們能夠通過 TCP 端口 4822 連接到主進程。由于主進程不希望通過此端口進行身份驗證,因此沒有什么可以阻止我們像普通的guacamole客戶端一樣連接到它并控制進程.

步驟 2 – 從我們的記憶中獲取秘密

這是我們要利用的關鍵設計選擇。由于guacd可執行文件包含主進程和每個連接進程的邏輯,因此當產生新的連接進程時,只fork()使用它。這句話值得重復:只 fork()使用,不使用execve()!

這是什么意思?分叉進程包含其父進程的整個內存快照,并且在execve()調用時該快照將替換為新映像。如果沒有這個關鍵調用,子進程將繼承其父進程的整個內存地址空間。這包括:

全內存布局——當我們想要攻擊父進程時,對于繞過 ASLR 很有用。
完整的內存內容——存儲在主進程中的每個秘密也被提供給子進程。

這意味著我們的進程具有proc-map映射每個秘密連接 UUID 到其各自進程的映射。我們只需要在我們的內存中找到這個數據結構,我們就會擁有所有當前活動的 UUID

定位 proc-map 本身純粹是技術性的。在我們的漏洞利用中,我們通過從/proc/<pid>/maps. 數據結構如此之大,以至于被mmap()分配到獨立的內存分配中,因此它在文件中有自己的條目。

步驟 3 – 加入所有會話

我們已經讓主要流程確信我們可以發起 Guacamole 協議請求,現在我們甚至知道要請求哪些請求。我們的下一步是通過提供它們現在已知的 UUID 來請求加入每個現有連接。

guac_pe_fig_4

圖 14:標記我們成功加入現有連接的日志條目。

令人驚訝的是,“只讀”會話屬性是由 guacamole-client 設置的。這意味著雖然我們不是連接的所有者,但我們仍然可以為加入的用戶關閉“只讀”權限位并獲得連接的完全權限。此外,除了主進程中的日志消息(如圖 14 所示)之外,沒有其他用戶剛剛加入連接的可見跡象。

步驟 4 – 重復

如果您密切關注,您可能已經注意到我們的攻擊計劃中的一個缺點:我們的guacd過程有一個過時的proc-map映射圖像。在我們生成后開始的任何會話只會在真實中更新proc-map,因此在我們過時的內存映像中將不可用。

這個缺點有一個簡單的解決方案。每個選定的時間間隔,比如 5 分鐘,我們都可以向主進程發送命令以啟動新的 RDP 連接并連接到網絡內受感染的機器。這樣,guacd就會產生一個新的,并且現在擁有更新版本的proc-map. 使用我們的原始漏洞,我們也可以攻擊這個過程,并“刷新”我們的proc-map.

當鏈接在一起時,這里是完整的漏洞利用鏈,RCE + PE,正在運行: https://youtu.be/vm8i9jJ2Np0

披露時間表

2020331 日 – 向 Apache 披露了漏洞。
2020331 日 – Apache 做出回應并要求提供更多信息。
2020331 日 – 向 FreeRDP 披露了漏洞。
2020331 日 – FreeRDP 做出回應并要求提供更多信息。
2020331 日——FreeRDP 通知我們 CPR-ID-2145CPR-ID-2156 是重復的,因為它們已經在 2020330 日單獨報告過。(我們運氣不好。)
202058 日 – Apache 在推送到其 GitHub的靜默提交中修補了漏洞。
2020510 日 – 我們通知 Apache,他們的補丁修復了所有報告的漏洞。
2020512 日 – Apache 向報告的 4 個漏洞發布了 2CVE-ID
2020628 日 – Apache 發布了官方補丁版本 – 1.2.0

結論

我們展示了反向 RDP 攻擊場景的新角度,這是我們最初在 2019 年初提出的攻擊場景。雖然用戶通常認為 RDP 客戶端……嗯……作為客戶端,但 Apache Guacamole 場景告訴我們其他情況。在標準情況下,攻擊者可以利用客戶端中的漏洞來控制單個公司計算機。但是,當部署在網關內時,此類漏洞對組織的影響要嚴重得多。

雖然在 COVID-19 大流行的艱難時期,在家遠程工作的過渡是必要的,但我們不能忽視這種遠程連接的安全影響。使用 Apache Guacamole 作為我們的示例目標,我們能夠成功演示如何使用組織內部的受感染計算機來控制處理所有遠程會話到網絡的網關。一旦控制了網關,攻擊者就可以竊聽所有傳入會話,記錄所有使用的憑據,甚至啟動新會話以控制組織內的其余計算機。當大部分組織都在遠程工作時,這個立足點就相當于獲得了對整個組織網絡的完全控制權。

我們強烈建議每個人確保所有服務器都是最新的,并且任何用于在家工作的技術都經過全面修補以阻止此類攻擊企圖。在我們的案例中,在發現漏洞并證明它們確實可以利用后的 24 小時內,我們實施了安全修復程序,并成為第一個針對此安全漏洞進行保護的生產環境,從而確保我們的員工可以安全地遠程連接。


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