
關於 CVE-2024-0517
CVE-2024-0517 是 Google Chrome 120.0.6099.224 之前版本的 V8 JavaScript 引擎中的越界寫入漏洞,遠端攻擊者可透過製作的 HTML 頁面利用堆損毀。此漏洞最早由 Qrious保護 的 Toan (Suto) Pham 報告。

當應用程式使用一種類型分配或初始化指針、物件或變數等資源,但之後使用與原始類型不相容的類型存取該資源時,即會發生類型混淆(CWE-843)。在此 CVE 中,類型混淆是在稱為折疊式分配 (folded allocation) 的記憶體分配過程中觸發的,而 Maglev (V8 JavaScript 引擎的最佳化編譯器) 則採用此過程進行記憶體最佳化。
攻擊者可利用類型混淆,並透過 WebAssembly 寫入任意 shellcode,在受害者的機器上執行指令。
攻擊階段
攻擊者可以寄存一個包含精心製作的 HTML 頁面的網站,並誘騙使用者透過釣魚電子郵件或社交網路存取該網站。當使用者使用有漏洞版本的 Google Chrome 瀏覽器造訪網站時,內嵌的惡意程式碼就會執行任意指令。

V8 JavaScript 引擎
V8 JavaScript 引擎由 Google 以 C++ 寫成,於 2008 年 9 月 2 日發佈。V8 可編譯和執行 JavaScript 原始程式碼、處理物件的記憶體分配,以及垃圾回收不再需要的物件。V8 是 Google Chrome 的核心元件,可嵌入任何 C++ 應用程式,例如 Node.js,以執行 JavaScript 或 WebAssembly 程式碼。
磁浮與折疊分配
Maglev 是 V8 中的優化編譯器,可增強程式碼執行和記憶體分配。Maglev 只會在程式碼經常執行並標示為 「熱 」時執行,這表示需要透過編譯加快執行速度,而不是逐行解釋較慢。
通常,分配發生在非連續的記憶體區域,導致記憶體使用稀疏且效率不高。為了解決這個問題,V8 採用了一種稱為折疊式分配的技術,可連續且同時分配多個變數。Maglev 也在其進程中使用折疊式分配來優化分配。

世代垃圾收集
為了清理未使用的記憶體區域,V8 採用了世代垃圾回收 (GC) 技術,將記憶體分成兩個空間:年輕世代和年老世代。此外,還有兩個垃圾回收器:次要垃圾回收器負責清理年輕空間,而主要垃圾回收器則處理舊空間的清理工作。新世代是新建立物件初始分配的記憶體區域,舊世代則是儲存長效物件的記憶體區域。在年輕一代中經過多次次要 GC 循環的物件,最後會升級到舊一代。

漏洞分析
概述
當從沒有明確定義構造器 (base default constructor) 的基類繼承類別建立物件,而隨後又建立另一個物件時,便會產生此漏洞。由於折疊分配的關係,第一個物件的分配可能會在第二個物件的分配之後。如果在這兩個分配之間發生垃圾回收等事件,就可能產生類型混淆漏洞。
根本原因分析
OPSWAT 研究生研究員對分配過程中的 V8 工作流程進行了詳細分析,並確定在此過程中會調用以下功能:

在這個過程中,TryBuildFindNonDefaultConstructorOrConstruct 函式發現了一個問題:BuildAllocateFastObject 函式延展 current_raw_allocation_(一個指向同時分配給多個變數的記憶體區域指針)來建構子類實例,但卻無法透過將其設定為 null 來清除它。
因此,下一個建立的物件總是緊接著 current_raw_allocation_ 指向的記憶體進行分配,不論在第二次分配之前有任何事件。

如果啟動 GC,則與 current_raw_allocation_ 相鄰的記憶體區域可以被指派給其他物件。這可能會導致一種情況:在觸發 GC 並建立另一個物件之後,兩個指標會引用相同的記憶體區域,但卻具有不同的資料類型,因而產生類型混淆漏洞。

開發
為了利用此漏洞,OPSWAT 研究員建立了包含 shellcode 的 WebAssembly 實體,並嘗試透過 GC 觸發類型混淆,以控制記憶體並執行 shellcode:

觸發器類型混淆
在初始化過程中,我們首先定義一個包含空物件的陣列 (_arrayObject)。接下來,我們會建構一個子類的實體以及一個觸發垃圾收集器。最後,我們定義另一個浮點數的陣列,命名為 _arrayDouble。

這些構造必須重複執行,使程式碼多次執行,導致 V8 將其標示為 「熱 」並觸發 Maglev 編譯器。我們透過在一個循環中呼叫子類的建構器來達成這個目的,如下所示:

在循環中重複初始化這些物件後,會觸發類型混淆。
建立讀寫基元
成功觸發類型混淆後,執行 shellcode 需要讀取記憶體,並覆寫受控位址的記憶體。為此,我們建立了讀取和寫入基元。開發基元會利用物件中的元資料,為我們提供任意讀/寫記憶體區域,並利用該區域執行任意程式碼。

此步驟中的讀取和寫入原素將使我們能夠在下一步中控制 WebAssembly 實例的跳轉表指針。
建立 WebAssembly Instances
接下來,我們建立兩個 WebAssembly 實體:一個用來儲存 shellcode,另一個用來觸發 shellcode。為了避免透過讀寫原語直接將 shellcode 寫入 WebAssembly 實體的記憶體中,我們在 WebAssembly 實體中定義了一些浮點常數值。

WebAssembly 實例的控制跳轉表指標
使用讀寫原語;我們調整第二個 WebAssembly 實例的跳轉表指標,跳過第一個 WebAssembly 實例中常數的編譯碼的一些位元組,這樣浮點常數就會被解釋成我們想要的 shellcode:

執行 WebAssembly Instance 以執行 Shellcode
最後,在觸發了類型混淆並使用讀/寫原素來控制 WebAssembly 實例的跳轉表指針之後,我們調用了第二個 WebAssembly 實例的匯出函式,使得第一個 WebAssembly 實例中的 shellcode 被執行。
我們使用的 shellcode 是為了終止 Linux 機器上的所有進程而設計的,如下所示:

由浮點數轉換而來的執行此指令的組合碼如下:

模擬安全漏洞
為了在真實情境中模擬這種攻擊,OPSWAT 的研究員製作了一個惡意製作的 HTML 頁面。

受害者會收到一封釣魚電子郵件,郵件中會嵌入一個連結,連結會指向寄存這個精心製作的 HTML 頁面的網域名稱。

如果受害者使用有漏洞版本的 Google Chrome 瀏覽器存取連結,shellcode 即會執行,導致所有程序終止。結果,使用者會登出,如下圖所示:

修復
MetaDefender Endpoint™ 利用其「脆弱應用程式」功能,主動減緩此 CVE。此解決方案可有效地找出並顯示端點環境中 Google Chrome 應用程式的所有相關 CVE。若要解除威脅,使用者可立即卸載 Chrome 或套用最新的安全修補程式。只要執行其中一種對策,CVE 即會完全受到控制,大幅降低端點遭受成功網路攻擊的風險。

引用
https://nvd.nist.gov/vuln/detail/CVE-2024-0517
https://cwe.mitre.org/data/definitions/843.html
https://blog.exodusintel.com/2024/01/19/google-chrome-v8-cve-2024-0517-out-of-bounds-write-code-execution/
https://jhalon.github.io/chrome-browser-exploitation-1/
https://whenderson.dev/blog/webgl-garbage-collection/
https://v8.dev/
https://github.com/Uniguri/CVE-nday/tree/master/Chrome/V8/CVE-2024-0517
