Browser Artifacts
Browser history, downloads, cached credentials, cookies, and session data are some of the richest user-activity artifacts available. This chapter covers Chrome, Edge (Chromium), and Firefox forensics — where the databases are, what they contain, and how to extract them for investigation.
A BEC (Business Email Compromise) investigation: an attacker accessed a finance manager's webmail through AiTM (Adversary-in-the-Middle) phishing, stole the session cookie, and used it to impersonate the manager and approve a fraudulent wire transfer. Your investigation needs to establish: what phishing site was visited, when the session was stolen, what actions were performed while logged in as the victim. The browser's SQLite databases — history, cookies, downloads — plus the browser's saved session state give you a timeline of exactly what the victim's browser did in the hours around the breach. This chapter shows how to extract that evidence.
Browser SQLite Databases
Both Chrome and Edge (Chromium-based) store most artifacts in SQLite databases. These are regular files that can be queried with any SQLite tool.
| Browser | Artifact | File path |
|---|---|---|
| Chrome | History (URLs + visits) | %LOCALAPPDATA%\Google\Chrome\User Data\Default\History |
| Chrome | Downloads | Same History database, downloads table |
| Chrome | Cookies | %LOCALAPPDATA%\Google\Chrome\User Data\Default\Network\Cookies |
| Chrome | Login Data (saved passwords) | %LOCALAPPDATA%\Google\Chrome\User Data\Default\Login Data |
| Chrome | Bookmarks | %LOCALAPPDATA%\Google\Chrome\User Data\Default\Bookmarks (JSON) |
| Edge | History | %LOCALAPPDATA%\Microsoft\Edge\User Data\Default\History |
| Edge | Cookies | %LOCALAPPDATA%\Microsoft\Edge\User Data\Default\Network\Cookies |
| Firefox | History (URLs + visits) | %APPDATA%\Mozilla\Firefox\Profiles\{profile}\places.sqlite |
| Firefox | Downloads | Same places.sqlite, moz_annos table |
| Firefox | Cookies | %APPDATA%\Mozilla\Firefox\Profiles\{profile}\cookies.sqlite |
| Firefox | Login Data | %APPDATA%\Mozilla\Firefox\Profiles\{profile}\logins.json |
Chrome/Edge store timestamps as microseconds since January 1, 1601 (Windows FILETIME-adjacent). A raw value like 13348765432100000 means nothing until you convert it. Firefox uses Unix epoch (microseconds since 1970). Always convert before reporting. In SQL: datetime(visit_time / 1000000 + (strftime('%s', '1601-01-01')), 'unixepoch') converts Chrome timestamps. Use a tool like DB Browser for SQLite with the datetime conversion built in to avoid manual calculation errors.
Querying Chrome History
-- Query Chrome History database (copy the file first -- Chrome locks it while open)
-- Open with DB Browser for SQLite or sqlite3
-- Recent browsing history with human-readable timestamps
SELECT
urls.url,
urls.title,
urls.visit_count,
datetime(visits.visit_time / 1000000 - 11644473600, 'unixepoch', 'localtime') AS visit_time_local,
urls.typed_count -- > 0 means user typed the URL directly (vs clicked a link)
FROM visits
JOIN urls ON visits.url = urls.id
ORDER BY visits.visit_time DESC
LIMIT 500;
-- Downloads
SELECT
target_path,
tab_url, -- page the download was initiated from
referrer, -- HTTP referrer (where they came from)
total_bytes,
datetime(start_time / 1000000 - 11644473600, 'unixepoch', 'localtime') AS download_started,
datetime(end_time / 1000000 - 11644473600, 'unixepoch', 'localtime') AS download_ended,
state -- 1=in progress, 2=complete, 3=cancelled
FROM downloads
ORDER BY start_time DESC;
-- Search for specific domains (e.g., known phishing infrastructure)
SELECT
url,
datetime(visit_time / 1000000 - 11644473600, 'unixepoch') AS visit_time_utc
FROM visits
JOIN urls ON visits.url = urls.id
WHERE url LIKE '%suspicious-domain.com%'
ORDER BY visit_time;
Cookie Forensics for Session Hijacking Investigations
-- Query Chrome Cookies database
-- Note: Cookie values are encrypted with DPAPI (Windows) — raw value not visible
-- But metadata (domain, name, timestamps) is unencrypted and forensically valuable
SELECT
host_key, -- domain the cookie belongs to
name, -- cookie name (e.g., "sessionid", "PHPSESSID", ".AspNetCore.Session")
path,
datetime(creation_utc / 1000000 - 11644473600, 'unixepoch') AS created,
datetime(last_access_utc / 1000000 - 11644473600, 'unixepoch') AS last_accessed,
datetime(expires_utc / 1000000 - 11644473600, 'unixepoch') AS expires,
is_httponly,
is_secure,
is_persistent -- 0 = session cookie (deleted on browser close)
FROM cookies
WHERE host_key LIKE '%target-domain.com%'
ORDER BY creation_utc DESC;
-- For decrypting cookie values (requires DPAPI — must run as the user):
-- pip install pycookiecheat
-- python3 -c "from pycookiecheat import chrome_cookies; print(chrome_cookies('https://example.com'))"
Firefox History Analysis
-- Firefox places.sqlite — history and bookmarks
-- Timestamps are microseconds since Unix epoch (1970-01-01)
-- Browsing history
SELECT
url,
title,
visit_count,
datetime(last_visit_date / 1000000, 'unixepoch') AS last_visited,
typed -- 1 = user typed URL directly
FROM moz_places
WHERE visit_count > 0
ORDER BY last_visit_date DESC
LIMIT 500;
-- Downloads (stored in moz_annos table)
SELECT
moz_places.url AS downloaded_url,
moz_annos.content AS local_path_or_status,
datetime(moz_annos.dateAdded / 1000000, 'unixepoch') AS download_date,
moz_annos.anno_attribute_id
FROM moz_annos
JOIN moz_places ON moz_annos.place_id = moz_places.id
WHERE moz_annos.anno_attribute_id IN (
SELECT id FROM moz_anno_attributes WHERE name LIKE '%download%'
);
Automated Browser Forensics Tools
| Tool | What it does |
|---|---|
| hindsight (Obsidian Forensics) | Chrome/Edge forensic timeline. Handles encrypted cookies, parses all Chrome databases, outputs to CSV/HTML. pip install hindsight |
| DB Browser for SQLite | GUI SQLite browser for manual Chrome/Firefox database queries — datetime conversion built in |
| BrowsingHistoryView (NirSoft) | Windows GUI tool that reads all browsers' history and outputs unified CSV — good for quick cross-browser survey |
| KAPE module: ChromeHistory | Automated extraction and parsing of Chrome history as part of KAPE triage collection |
| volatility3 chromium_history | Extract browser history directly from memory image (no disk needed if browser was open) |
# Hindsight: automated Chrome forensics timeline
# pip3 install hindsight
# Analyze Chrome profile from KAPE collection
python3 -m hindsight \
-i "D:/evidence/C/Users/jsmith/AppData/Local/Google/Chrome/User Data" \
-o chrome_analysis \
-f xlsx # output format: xlsx, csv, sqlite, json
# Output includes:
# Web History sheet — URLs, visit times, typed vs clicked
# Downloads sheet — file downloads with referrers
# Cookies sheet — cookie metadata (values encrypted)
# Autofill sheet — form field data saved by browser
# Saved Passwords sheet — login data (encrypted values + domains)
# Extensions sheet — installed browser extensions
# Cache Analysis — URLs found in browser cache records
Q & A
Q: The user cleared their browser history before you could image the system. Is any history still recoverable?
Often yes — multiple avenues: (1) SQLite WAL (Write-Ahead Log) file — Chrome uses WAL mode, meaning recent uncommitted transactions may exist in the History-wal file even after the main database was cleared. (2) Browser cache — cached web resources (images, scripts, HTML files) are stored separately from history; clearing history in most browsers doesn't clear cache. Chrome cache is in %LOCALAPPDATA%\Google\Chrome\User Data\Default\Cache\ and contains URL references. (3) VSS — if a Volume Shadow Copy predates the clearing, mount it and extract the pre-clearing history. (4) NTFS unallocated space — the old SQLite records may exist in unallocated clusters on disk if the database file was deleted and overwritten rather than cleared internally. (5) Prefetch — shows chrome.exe run times even if history was cleared. (6) DNS cache (captured during live response) shows recently resolved domains. (7) Proxy/SIEM logs — browser history clearing doesn't remove proxy logs of outbound HTTP requests.