
摘要
本報告根據近期研究,探討一種新穎的技術,可透過強行載入任意擴充功能來入侵 Chromium 瀏覽器。該方法利用擴充功能載入機制的特定弱點,特別是使用者偏好設定檔案的完整性保護。本報告透過分析 Chromium 擴充功能的結構、其安裝過程和底層的載入機制,說明具備本機管理權限的Threat actor如何繞過現有的安全策略。報告重點在於攻擊的技術細節,包括設定檔案的操縱以及對瀏覽器安全的影響。概念性的程式碼分析和技術圖表說明了攻擊流程,並強調此漏洞在 domain-joined 和 non-domain-joined 環境之間的區別。

1. 簡介
資安環境不斷演進,持續促使Threat actor尋求新的入侵途徑。隨著傳統作業系統組件的強化,網頁瀏覽器因其作為敏感資料和企業雲端服務閘道的核心角色,已成為關鍵的攻擊目標。瀏覽器處理著大量的機密資訊,包括tokens和Credentials,因此其一旦被入侵,將對組織安全構成重大威脅。本報告深入探討一種特定技術,可透過強制瀏覽器載入未經授權的擴充功能,從而在 Chromium 瀏覽器中植入後門 [1]。這種方法一旦成功執行,可能導致瀏覽器環境的完全淪陷,賦予Threat actor對使用者資料和系統功能的大範圍存取權。本文旨在提供對此攻擊途徑的詳細技術分析,著重於所涉及的機制以及可利用的條件。
2. Chromium 擴充功能的結構
Chromium 擴充功能是一種模組化組件,旨在客製化和強化瀏覽體驗。這些擴充功能使用標準的網頁技術(如 HTML、CSS 和 JavaScript)建構,並透過一套明確定義的 APIs 與瀏覽器互動。瞭解其基本結構對於理解如何將其用於malicious purposes至關重要 [1]。
2.1 核心組件
Chromium 擴充功能的主要組成部分包括:
-
manifest.json:
這是每個 Chromium 擴充功能的基礎設定檔案。它包含重要的metadata,例如擴充功能的名稱、版本和所需的權限。至關重要的是,它決定了哪些腳本會在不同Context中執行(例如,背景或內容腳本),並指定 HTML 檔案的呈現位置(例如,彈出視窗、新分頁)。
manifest.json
充當了擴充功能的藍圖,定義其整體結構和行為。 - Service Worker scripts: 這些是在任何特定網頁之外獨立運行的背景 JavaScript 檔案。它們擁有對 Chrome 擴充功能 APIs 的廣泛存取權限,使其能夠管理瀏覽器設定、處理網路Request、促進各種擴充功能組件之間的通訊,並執行敏感操作。此類操作可能包括攔截網路流量、擷取螢幕截圖或存取所有cookies,因此一旦被入侵,將構成重大的安全風險。
-
Content scripts:
這些 JavaScript 檔案被注入到
manifest.json
中定義的特定網頁。在網頁的Context中運行,content scripts 可以直接與文件物件模型(Document Object Model, DOM)互動。此功能讓它們能夠讀取、修改和操縱頁面內容,使其適合於改變頁面外觀、注入malicious forms或擷取敏感資料(包括儲存在本機或session storage中的Credentials)等任務。 - HTML Files: 擴充功能還可以包含標準的 HTML 檔案來建構使用者介面。這些介面通常用於各種目的,包括使用者點擊擴充功能圖示時出現的彈出視窗、客製化的新分頁,或用於設定擴充功能的專用選項頁面。
擴充功能在 Chrome 環境中運行,可直接存取其 APIs,從而與瀏覽器處理的資料互動。這包括可能受作業系統層級 App-Bound Encryption 保護的資訊,因為瀏覽器本身處理其正常操作的解密。因此,載入的擴充功能可以存取和操縱敏感資料、攔截網路流量、重新導向使用者以及擷取螢幕內容,突顯了它們一旦被入侵的深遠影響 [1]。
3. Chromium 擴充功能安裝機制
Chromium 擴充功能通常以一種專門的Package格式發布,其副檔名為
.crx
。此
.crx
檔案本質上是一個經過數位簽章的壓縮封存檔,其中包含所有擴充功能的組成檔案,例如manifest、scripts和圖示,以確保其完整性和可驗證的來源 [1]。
3.1 常見的安裝方法
擴充功能通常透過以下幾種主要機制進行安裝:
-
Chrome Web Store:
最常見的安裝方法是透過官方的 Chrome 線上應用程式商店。在此,瀏覽器會自動下載對應的
.crx
Package,驗證其數位簽章,並確認其來源為可信。透過商店發布的擴充功能在使用者可用之前,也會經過 Google 嚴格的審查程序。 -
Unpacked或自簽章擴充功能:
在開發或測試階段,可以直接從本機資料夾載入擴充功能。此程序需要先在瀏覽器的擴充功能管理頁面中啟用「開發人員模式」,然後選擇「載入未封裝項目」,並指向擴充功能的來源目錄。或者,使用者可以將
.crx
Package拖曳到瀏覽器視窗中進行安裝。然而,如果Package是自簽章且非來自官方商店,Chrome 可能會顯示警告,甚至根據已設定的政策設定阻止安裝。 -
命令列:
擴充功能可以在瀏覽器啟動時,透過使用
--load-extension
旗標並指定其目錄來載入。對於 Chromium 137 及以上版本,此旗標預設為停用。要重新啟用此功能並透過命令列載入擴充功能,需要額外使用一個旗標,即--disable-features=DisableLoadExtensionCommandLineCommandLineSwitch
。
從Threat actor的角度來看,利用 Chrome 線上應用程式商店的難度要高得多,因為 Google 有嚴格的審查和批准程序。儘管複雜的技術(如混淆或 WebAssembly)偶爾可能逃避偵測,但相較於其他部署方法(例如本機安裝或企業大量部署),商店仍構成更高的障礙。命令列方法雖然可行,但卻是一種眾所周知的技術。其高可見性以及在近期 Chromium 版本中預設停用,使其較不適合於隱密和持久的操作。此外,此方法經常被惡意Threat actor利用,包括像 Chromeloader 這樣的 infostealers,使其易於被偵測 [1]。因此,底層研究的重點轉向瞭解較少被記錄的擴充功能載入途徑,尤其是那些可以繞過常規限制並提供更隱密方式來進行未經授權擴充功能安裝的方法 [1]。
4. Chromium 擴充功能載入機制
成功安裝後,Chromium 瀏覽器擴充功能會在 Windows 作業系統的特定使用者偏好設定檔案中被細緻地註冊。這些關鍵檔案通常位於使用者的 AppData 目錄中,用於設定儲存庫(Configuration repositories),用來列舉所有已安裝的擴充功能及其相關設定 [1]。檔案的選擇取決於系統的 domain join 狀態:
-
%USERNAME%/AppData/Local/Google/User Data/Default/Secure preferences
:此檔案用於未加入 domain 的 Windows 裝置。 -
%USERNAME%/AppData/Local/Google/User Data/Default/Preferences
:此檔案用於已加入 domain 的 Windows 裝置。
這兩個檔案都以 JSON 格式編排,並包含一個擴充功能 Dictionary,其中每個條目詳細說明了擴充功能 ID、安裝路徑和其相關 metadata。
Preferences
檔案通常更為廣泛,並包含更廣泛的詳細設定。研究強調的一個關鍵區別是,
Preferences
檔案受到cryptographic signature的保護,而
Secure preferences
檔案則缺乏此完整性保護。這個差異對於攻擊技術至關重要 [1]。
4.1 擴充功能hash產生
當安裝擴充功能時,Chromium 會為其產生一個獨特的 ID。此 ID 來自於擴充功能公開金鑰的 SHA-256 hash。hash 的前 128 位元被用於建立 ID。以下來自 Chromium 原始碼的 C++ 程式碼說明了此程序:
- // chromium/src/components/crx_file/id_util.cc:59
- std::string GenerateIdForPath(const base::FilePath& path) {
- base::FilePath new_path = MaybeNormalizePath(path);
- std::string path_bytes =
- std::string(reinterpret_cast<const char*="">(new_path.value().data()),
- new_path.value().size() * sizeof(base::FilePath::CharType));
- return GenerateId(path_bytes);
- }
- // std::string GenerateId(std::string& input)
- std::string GenerateId(std::string& input) {
- uint8 hash[kIDSize];
- // Compute SHA256 hash of the input (public key or path)
- crypto::SHA256HashString(input, hash, sizeof(hash));
- std::string output = base::ToLowerASCII(base::HexEncode(hash, sizeof(hash)));
- // Convert hexadecimal hash to the custom alphabet (a-p)
- ConvertHexadecimalToIDAlphabet(&output);
- return output;
- }
此 C++ 程式碼展示了產生擴充功能 ID 的程序。對於未封裝的擴充功能,會使用擴充功能安裝目錄的絕對路徑作為輸入。然後,`GenerateId` 函式接收此輸入,計算其 SHA-256 hash,將其轉換為十六進位制,最後將前 32 個字元映射到一個客製化的字母表(a-p),以產生獨特的擴充功能 ID。
以下 Python 腳本實際示範了如何產生擴充功能 ID 以及對應的公開和私密金鑰。Threat actor可以使用此腳本預先計算malicious extension的擴充功能 ID,以確保其在注入到
Secure preferences
檔案時格式相符。
- import base64
- import hashlib
- from cryptography.hazmat.primitives import serialization
- from cryptography.hazmat.primitives.asymmetric import rsa
- from cryptography.hazmat.backends import default_backend
- def generate_extension_id(public_key_bytes):
- # Calculate SHA256 hash of the public key (DER-encoded SubjectPublicKeyInfo)
- sha256_hash = hashlib.sha256(public_key_bytes).digest()
- # Take the first 16 bytes (128 bits) of the hash
- hash_bytes = sha256_hash[:16]
- # Convert to hexadecimal string
- hex_hash = hash_bytes.hex()
- # Map hexadecimal characters to the custom alphabet (a-p)
- # This is a simplified representation; actual mapping is more complex
- extension_id = "".join([chr(ord('a') + int(c, 16)) for c in hex_hash])
- return extension_id
- # Generate a new RSA private key
- private_key = rsa.generate_private_key(
- public_exponent=65537,
- key_size=2048,
- backend=default_backend()
- )
- # Get the public key in SubjectPublicKeyInfo format (DER encoding)
- public_key_der = private_key.public_key().public_bytes(
- encoding=serialization.Encoding.DER,
- format=serialization.PublicFormat.SubjectPublicKeyInfo
- )
- # Generate the extension ID
- ext_id = generate_extension_id(public_key_der)
- print(f"Generated Extension ID: {ext_id}")
- # For demonstration, here's how to get the public key in base64 for manifest.json
- public_key_base64 = base64.b64encode(public_key_der).decode("utf-8")
- print(f"Public Key (Base64 for manifest.json): {public_key_base64}")
- # Example of how the private key might be used (e.g., for signing .crx)
- private_key_pem = private_key.private_bytes(
- encoding=serialization.Encoding.PEM,
- format=serialization.PrivateFormat.PKCS8,
- encryption_algorithm=serialization.NoEncryption()
- ).decode("utf-8")
- print(f"Private Key (PEM):\n{private_key_pem}")
此 Python 腳本首先定義了一個名為 `generate_extension_id` 的函式,該函式模擬了 Chromium 的邏輯:它接收 DER 編碼的公開金鑰bytes,計算 SHA256 hash,取其前 16 bytes,將其轉換為十六進位制,然後將這些十六進位制字元映射到客製化字母表(a-p)以形成擴充功能 ID。接著,腳本產生一個 RSA key pair,以所需格式擷取公開金鑰,並用它來推導範例擴充功能 ID。它也展示了公開金鑰如何編碼以納入 `manifest.json` 檔案,以及私密金鑰如何表示,這對於簽署擴充功能至關重要。
5. 透過操縱偏好設定檔案來繞過 Chromium 策略
此漏洞的核心在於具備足夠權限的Threat actor能夠修改
Secure preferences
檔案。由於此檔案在 non-domain-joined 的機器上未受到cryptographic signature的保護,Threat actor可以注入一個新的malicious extension條目。此條目將指向檔案系統上Threat actor已放置malicious extension檔案的目錄。當瀏覽器下次啟動時,它會讀取這個修改後的檔案,並發現這個新條目,然後繼續載入這個malicious extension,就如同它是合法的一樣。這提供了一種強大而隱密的方法,可以獲得持久性並控制瀏覽器。
5.1 攻擊流程
攻擊可以分解為以下步驟:
- 取得本機管理權限: Threat actor首先必須取得目標機器的管理存取權限。這是修改使用者 AppData 資料夾的先決條件。
- 放置惡意擴充功能: Threat actor將malicious extension的檔案放置在本機檔案系統的一個目錄中。
-
修改 Secure Preferences 檔案:
Threat actor以程式設計方式在
Secure preferences
檔案中添加一個新條目。此條目包含預先計算的擴充功能 ID 和malicious extension的路徑。 -
啟動瀏覽器:
當使用者啟動瀏覽器時,它會讀取修改後的
Secure preferences
檔案並載入malicious extension。
下圖說明了攻擊流程:
6. 結論
對「The Phantom Extension」漏洞的研究突顯了 Chromium 瀏覽器安全模型中的一個重大弱點,尤其是在 non-domain-joined 的系統上。
Secure preferences
檔案缺乏完整性保護,使得具備本機管理權限的Threat actor能夠繞過正常的擴充功能安裝程序並載入malicious extension。這為實現持久性並獲得對使用者瀏覽環境的控制提供了一個強大的機制。本報告詳細介紹了此漏洞的技術方面,從 Chromium 擴充功能的結構到攻擊的細節。所提供的程式碼範例和圖表說明了Threat actor為利用此弱點而採取的實際步驟。由於瀏覽器持續成為Threat actor的主要目標,開發人員和資安專業人員必須保持警惕並解決此類漏洞,以保護使用者免受這些不斷演進的威脅。