When to use / when not to use
- Use: extension files (crx/xpi/zip), malicious extension behavior analysis, extension permission auditing
- Use: determining whether an extension is over-privileged, uses remote code, or update poisoning (clean first version, malicious later version)
- Do not use: web page JS obfuscation ([[re-script-deob]] covers pure scripts)
- Do not use: exploitation of Chrome/Edge browser itself (extensions and browser vulnerabilities are two different objects)
- Note: dynamically loading an extension to run malicious behavior counts as executing unknown code—do it in a sandbox ([[re-sandbox]], [[re-analyze/platform-tips]] highest principle)
Tool preparation
unzip / 7z / python zipfile (unpacking)
- For installation and verification see [[re-doc-malware]] tool preparation (7z)
- crx = "Cr24" header (version number + header length; crx2 contains public key and signature, crx3 contains protobuf header and proof) + trailing zip data—
unzip/7zunpacking directly will report aextra bytes at beginningwarning but the result is correct;python3 -m zipfile -e sample.crx ext/no warning - xpi is just a plain zip, unpack it directly
jq (manifest parsing)
- Linux:
apt install jq;macOS:brew install jq; Windows: official build - Verification:
jq --version
node / js-beautify (script recovery)
- Linux:
apt install nodejs;macOS:brew install node; Windows: nodejs.org official installer; verification:node --version - js-beautify:
npm install -g js-beautify, verifyjs-beautify --version(format minified JS before reading)
Browser load verification (dynamic, sandbox)
- Chromium family:
chrome://extensionsdeveloper mode → Load unpacked; Edge same (edge://extensions) - Firefox:
about:debugging#/runtime/this-firefox→ Load Temporary Add-on (unsigned temporary loading) - Verification: the extension appears in the extension list, and the background script/page can be opened
Operating steps
Execute in order, archive each step's artifacts (path + sha256, see [[re-triage]]).
-
Unpacking and structure:
unzip -o sample.crx -d ext/ 2>/dev/null || python3 -m zipfile -e sample.crx ext/ jq '.manifest_version, .name, .version' ext/manifest.json jq '.permissions, .host_permissions' ext/manifest.json # host_permissions 为 MV3 字段;MV2 站点权限在 permissions 内(含 URL 匹配模式)- Structure: manifest.json + background script (MV3 service worker / MV2 background page) + content scripts (content_scripts) + pages (options/popup) + resources (web_accessible_resources)
- First confirm
manifest_version(2 or 3)—the permission model and capability differences between MV2/MV3 determine all subsequent judgments (see [[gotchas]]) - Other key fields:
update_url(self-hosted update manifest, poisoning surface),key(extension ID derivation),externally_connectable(external page message channel),oauth2(token),optional_permissions(request permissions at runtime),minimum_chrome_version(compatibility boundary) - Secondary structure:
_locales/(i18n messages, can hide strings),icons/、options_page/options_ui、commands(keyboard shortcuts, behavior trigger entry points),incognito(incognito window behavior declaration)
-
Permission auditing (capability boundary):
-
High-risk API permissions:
tabs(read tab URL/title),storage(data collection cache),webRequest(traffic observation; MV3 blocking capability is only an exception for enterprise policy deployment, generally switching todeclarativeNetRequest)、scripting(MV3 dynamic injection),clipboardRead(clipboard),cookies(reading site cookies),history、downloads、nativeMessaging(communicating with the host program),debugger -
Site permissions (host_permissions / URL patterns in MV2 permissions) determine which websites' data can be accessed—
<all_urls>or*://*/*are high-risk signals -
Permissions vs. declared purpose: has
storage+tabs+<all_urls>installed but the functionality is only changing the theme—privilege escalation signal -
Permission risk quick reference (declaration equals capability, no need to look at the code):
Permission Capability Malicious use tabs+<all_urls>Read tab URL/title Browsing habit collection storageLocal persistence Collected data caching / configuration delivery webRequest/declarativeNetRequestTraffic observation / rule modification Monitoring, injection, interception scripting/content_scriptsPage injection Keylogging, form theft clipboardReadRead clipboard Clipboard content theft (e.g., wallet addresses) cookiesRead/write site cookies Session hijacking nativeMessagingCommunicate with the host program Drop executables / exfiltrate data downloadsRead/write downloads Download poisoning / tampering with downloaded files
-
-
Malicious behavior localization:
- Data exfiltration: fetch/XHR/
navigator.sendBeacon, image pixel exfiltration (new Image().src)、chrome.storagecollected then batch uploaded (connects to [[re-netcap]]) - Injection surface: content_scripts matching sites, dynamic injection (MV3
chrome.scripting.executeScript;MV2tabs.executeScript)、web_accessible_resourcesopening pages for third-party sites to call - Keylogging: content script listening to keydown/input and exfiltrating—search
addEventListener('keydown'andvaluereads - Trigger timing:
chrome.runtime.onInstalled/onStartup(runs on install/startup),chrome.alarms/setInterval(scheduled tasks),chrome.runtime.onMessage(message-driven)—reconstruct the call chain by trigger point, don't just look at top-level code - The background script is the core: MV3's service worker / MV2's background page; the MV3 service worker is terminated after about 30 seconds of idle and woken by events—state persists in
chrome.storageor IndexedDB, don't look for persistent state in global variables
- Data exfiltration: fetch/XHR/
-
Obfuscation recovery:
- Connects to [[re-script-deob]] advanced obfuscation countermeasures (bootstrap/string table/CFF); beautify minified code with js-beautify before reading
- Remote code: MV2 allows remote JS (
eval/dynamic loading); MV3 prohibits remote code (all code must be bundled)—remote fetching appearing in MV3 = review bypass signal, flag it prominently - Binary payload: analyze after downloading Wasm/ArrayBuffer (switch to [[re-binary-core]] for initial reconnaissance)
- String localization:
grep -rniE 'http|sendBeacon|fetch|XMLHttpRequest|chrome\.' ext/scan the whole package; i18n strings are in_locales/*/messages.json(real URLs hidden under the guise of messages are common)
-
Update mechanism and version poisoning:
- Chrome family:
update_urlself-hosted update manifest (XML pointing to a new crx) is a poisoning entry point; store auto-update is the same - Version comparison: diff the old version extension against the current version (file list/hashes/manifest/scripts) to locate "when the malicious was introduced" ([[re-variant]] approach)
- Change attribution: classify diff results by file (manifest permission changes / newly added scripts / rewrites of existing scripts); permission expansion during an update is a poisoning signal
- Firefox: updates only go through the AMO channel,
update_urldoes not apply — different poisoning surface
- Chrome family:
-
Dynamic verification(sandbox):
- Load into browser (tool preparation), configure proxy/packet capture ([[re-netcap]]), trigger functionality and observe network and storage changes
- Analyze by context: background / content script / extension page — three isolated contexts; locate API call sites by context (Gotcha 4)
- Extension pages/popups can open DevTools directly (
chrome-extension://<id>/page F12) — combined with breakpoints, inspect global state and network calls inside the service worker - Connect behavior recording with [[re-ioc]] indicator extraction (C2 domains, command formats)
Cross-domain combination
- [[re-managed]] gateway: ownership of this skill (selection tree "Browser Extension" branch)
- [[re-script-deob]]: deobfuscation
- [[re-netcap]]: network behavior linkage (exfiltration/remote retrieval)
- [[re-variant]]: extension version comparison (malicious detection after update)
- [[re-triage]]: hash/initial survey evidence preservation of unpacked artifacts
Common Gotchas and Pitfalls
- manifest version differences: symptom — MV2 analysis approach fails when applied to MV3; cause — different permission model (MV3 prohibits remote code / background changed to service worker); countermeasure — first confirm manifest_version and analyze by version branch (see [[gotchas]])
- Remote code strategy: symptom — static analysis cannot find malicious logic; cause — code retrieved remotely (MV2); countermeasure — capture on the network side ([[re-netcap]]) then deobfuscate
- Dynamic injection bypasses static analysis: symptom — content_scripts has no malicious code but behavior is abnormal; cause — runtime injection; countermeasure — hook the injection APIs (scripting.executeScript / tabs.executeScript)
- Sandbox and page isolation: symptom — hard to locate extension API call sites; cause — extension context is isolated from page context; countermeasure — analyze by context (background/content/page), and determine which context executes based on API ownership
- Malicious after update: symptom — first version is clean; cause — malicious injected via version update; countermeasure — version diff ([[re-variant]])
- service worker state loss: symptom — persistent data cannot be found in the MV3 background script; cause — service worker is terminated when idle, state is in the storage layer; countermeasure — check
chrome.storage/IndexedDB persisted data - Firefox MV3 differences: symptom — manifest specifies
background.service_workerbut the background does not run in Firefox; cause — Firefox MV3 does not support the service_worker field, it usesbackground.scriptsevent pages; countermeasure — dual-field approach (service_worker for Chrome, scripts for Firefox), verify separately per browser - For more MV2/MV3 differences, crx format details, and review bypass boundaries, see [[gotchas]] and [[decision-tree]]