Browser extensions forensics (Chrome & Firefox)
2026-06-21 · 3 min
Installed extensions are an under-used source of both intent and risk. A malicious or sideloaded extension can read every page the user visits, exfiltrate cookies, or inject scripts — and unlike a dropped binary, it survives reboots and rides along with profile sync. The list of what was installed, when, with which permissions, and from where is recorded in plain JSON. You just have to know which file to open.
Chrome / Edge: Preferences and Secure Preferences
Chromium records each extension under the extensions.settings object
inside the profile's Preferences and Secure Preferences files (both
JSON). Every extension is keyed by its 32-character id:
| Field | Meaning |
|---|---|
manifest.name / manifest.version | Display name and version |
manifest.permissions | Requested capabilities (e.g. tabs, cookies, <all_urls>) |
install_time | WebKit/Chrome microseconds since 1601-01-01 UTC |
state | Enabled (1) or disabled (0) |
from_webstore | true = Web Store; false = sideloaded/dev |
Secure Preferences additionally carries a protection.macs tree —
HMACs over selected preference values, including extension entries.
Chrome uses these to detect off-browser tampering (an attacker silently
adding an extension by editing the file). Mismatched or missing MACs are
themselves an indicator: either the entry was hand-edited, or it was
written by a process that did not have the seed to recompute the MAC.
The extension's actual code lives separately, on disk:
…\<profile>\Extensions\<id>\<version>\
Pull the manifest.json and background/content scripts from there
when you need to read what the extension actually does, not just what it
declared.
Firefox: extensions.json
Firefox keeps its inventory in extensions.json at the profile root.
Each add-on entry gives you:
| Field | Meaning |
|---|---|
id | Add-on identifier (often an email-style or UUID id) |
defaultLocale.name | Display name |
version | Installed version |
type | extension, theme, dictionary, … |
active | Currently enabled or not |
installDate | Unix epoch milliseconds |
sourceURI | Where the add-on was installed from |
sourceURI is the high-value field: addons.mozilla.org means the
official store, while a file:// path or an arbitrary HTTPS host points
to a sideloaded or enterprise-pushed add-on — a classic persistence and
data-theft vector.
Reading the forensic signal
- Permissions = capability.
<all_urls>pluscookiesorwebRequestmeans the extension could read and exfiltrate anything the browser sees. Triage by capability, not by name. - Source = provenance.
from_webstore: false(Chrome) or a non-AMOsourceURI(Firefox) flags sideloading — common in IR for credential stealers and ad-injectors that never touched a store review. - Install time = timeline. Feed
install_time/installDateinto your master timeline; an extension appearing minutes after a phishing click or a fresh logon is a strong lead. See browser timestamp formats for the microseconds-since-1601 vs Unix-ms conversions. - State. A disabled extension is still evidence — it was installed deliberately at some point and may have been left behind after use.
In the tool
BrowserForensics parses Chrome Preferences and Secure Preferences
(both the profile metadata and the extensions.settings block) and
Firefox extensions.json into sortable tables — id, name, version,
permissions, source, install time, and enabled state — entirely in your
browser, with no upload. That makes it quick to spot the one entry with
<all_urls> and a file:// source among twenty benign theme add-ons.