Chapter 11

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.

Scenario

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.

BrowserArtifactFile path
ChromeHistory (URLs + visits)%LOCALAPPDATA%\Google\Chrome\User Data\Default\History
ChromeDownloadsSame History database, downloads table
ChromeCookies%LOCALAPPDATA%\Google\Chrome\User Data\Default\Network\Cookies
ChromeLogin Data (saved passwords)%LOCALAPPDATA%\Google\Chrome\User Data\Default\Login Data
ChromeBookmarks%LOCALAPPDATA%\Google\Chrome\User Data\Default\Bookmarks (JSON)
EdgeHistory%LOCALAPPDATA%\Microsoft\Edge\User Data\Default\History
EdgeCookies%LOCALAPPDATA%\Microsoft\Edge\User Data\Default\Network\Cookies
FirefoxHistory (URLs + visits)%APPDATA%\Mozilla\Firefox\Profiles\{profile}\places.sqlite
FirefoxDownloadsSame places.sqlite, moz_annos table
FirefoxCookies%APPDATA%\Mozilla\Firefox\Profiles\{profile}\cookies.sqlite
FirefoxLogin Data%APPDATA%\Mozilla\Firefox\Profiles\{profile}\logins.json
Mental model: browser timestamps are in microseconds since January 1, 1601

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

SQLchrome-history.sql
-- 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;
SQLchrome-cookies.sql
-- 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

SQLfirefox-history.sql
-- 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

ToolWhat 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 SQLiteGUI 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: ChromeHistoryAutomated extraction and parsing of Chrome history as part of KAPE triage collection
volatility3 chromium_historyExtract browser history directly from memory image (no disk needed if browser was open)
Bashhindsight-chrome.sh
# 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.