User activityNetworkFile access
Browser Profiles on Linux: Firefox and Chrome History
Firefox and Chrome/Chromium profile databases on Linux, including snap and Flatpak paths: history, downloads, epochs and how to query them offline.
- Location
- ~/.mozilla/firefox/<profile>/places.sqlite
- Proves
- Which sites a user visited, what they downloaded and searched for, and when
- Timestamps
- Firefox PRTime (usec since 1970 UTC); Chrome/Chromium WebKit time (usec since 1601-01-01 UTC)
- Access
- Any user for own profile; root for other users
- Retention
- Chrome: visits expire after 90 days; Firefox: size-based expiration of old, low-frecency pages
- Collection
- UAC, Velociraptor, cp -a, tar
Tools
Compare all toolsWhat it is
Browsers keep a per-user profile directory with SQLite databases and JSON files that record history, downloads, bookmarks, cookies, form data and session state. On Linux the layout is the same as on other platforms, but the profile location depends on how the browser was installed: distribution package, vendor package, snap or Flatpak. Missing the right directory is the most common reason browser evidence is "not found" on Ubuntu desktops, where Firefox and Chromium ship as snaps.
Where it lives
| Browser / packaging | Profile root |
|---|---|
| Firefox (deb/rpm or tarball) | ~/.mozilla/firefox/<profile>/ |
| Firefox 147+ new installs | ~/.config/mozilla/firefox/<profile>/ (XDG layout; an existing ~/.mozilla keeps being used) |
| Firefox snap (Ubuntu default) | ~/snap/firefox/common/.mozilla/firefox/<profile>/ |
| Firefox Flatpak | ~/.var/app/org.mozilla.firefox/.mozilla/firefox/<profile>/ |
| Firefox cache | ~/.cache/mozilla/firefox/<profile>/; snap: ~/snap/firefox/common/.cache/mozilla/firefox/ |
| Google Chrome | ~/.config/google-chrome/ (-beta, -unstable for other channels) |
| Chromium (deb/rpm) | ~/.config/chromium/ |
| Chromium snap | ~/snap/chromium/common/chromium/ |
| Chrome / Chromium Flatpak | ~/.var/app/com.google.Chrome/config/google-chrome/, ~/.var/app/org.chromium.Chromium/config/chromium/ |
| Edge, Brave | ~/.config/microsoft-edge/, ~/.config/BraveSoftware/Brave-Browser/ |
| Chromium-family cache | ~/.cache/google-chrome/, ~/.cache/chromium/ and so on |
$XDG_CONFIG_HOME or $CHROME_CONFIG_HOME can move Chrome's root. Inside a Chromium-family root, profiles are Default, Profile 1, Profile 2...; Local State describes them. Firefox lists its profiles in profiles.ini and installs.ini next to the profile folders.
These paths are the same on Debian/Ubuntu, RHEL/Fedora and other families; the packaging choice (snap on Ubuntu, Flatpak when installed from Flathub) is what changes them.
What it proves
- Pages visited, how they were reached (typed, link, bookmark, redirect, download) and visit counts.
- Files downloaded, from which URL and referrer, where they were saved and whether they were opened (Chrome).
- Search terms typed into the address bar (Chrome
keyword_search_terms). - Open tabs and windows at a point in time (session files).
It does not prove who was at the keyboard, and synced profiles can contain visits made on another device (Chrome visits carries originator fields for synced rows). Private/incognito browsing is not written to these databases.
Key fields
Firefox places.sqlite
| Table.column | Meaning |
|---|---|
moz_places.url, title, visit_count, typed, last_visit_date, frecency | One row per URL |
moz_historyvisits.place_id, visit_date, visit_type, from_visit | One row per visit; visit_type 1 link, 2 typed, 3 bookmark, 4 embed, 5/6 redirect, 7 download, 8 framed link, 9 reload |
moz_bookmarks.fk, title, dateAdded, lastModified | Bookmarks (fk = moz_places.id) |
moz_annos + moz_anno_attributes (downloads/destinationFileURI, downloads/metaData) | Download target path and state |
Other Firefox files: cookies.sqlite, formhistory.sqlite, favicons.sqlite, sessionstore.jsonlz4 and sessionstore-backups/recovery.jsonlz4 (LZ4-compressed JSON), extensions.json, prefs.js.
Chrome / Chromium History
| Table.column | Meaning |
|---|---|
urls.url, title, visit_count, typed_count, last_visit_time, hidden | One row per URL |
visits.url (= urls.id), visit_time, from_visit, transition, visit_duration | One row per visit; transition & 0xFF is the core type (0 link, 1 typed, 2 auto bookmark, 7 form submit, 8 reload...) |
downloads.target_path, current_path, start_time, end_time, received_bytes, total_bytes, state, danger_type, opened, last_access_time, referrer, tab_url, mime_type | Download records |
downloads_url_chains.url | Full redirect chain of each download |
keyword_search_terms.term, url_id | Search terms typed in the omnibox |
Other Chromium files: Cookies, Web Data (autofill), Bookmarks (JSON), Preferences, Sessions/, Extensions/.
Timestamps
| Browser | Format | SQLite conversion |
|---|---|---|
| Firefox | PRTime, microseconds since 1970-01-01 UTC | datetime(visit_date/1000000, 'unixepoch') |
| Firefox bookmarks | PRTime | datetime(dateAdded/1000000, 'unixepoch') |
| Chrome/Chromium | Microseconds since 1601-01-01 UTC | datetime(visit_time/1000000 - 11644473600, 'unixepoch') |
All are UTC. visit_duration in Chrome is a duration in microseconds, not a timestamp.
Retention
- Chrome/Chromium: history entries older than 90 days are expired automatically (
kExpireDaysThreshold). - Firefox: no fixed age. Places expiration trims old, low-frecency pages when the database grows beyond an optimal size (capped at 75 MiB) or
places.history.expiration.max_pages. - Users can clear history, set "clear on close", or browse privately. Deleted rows often survive in SQLite free pages and in
-walfiles until the database is vacuumed or checkpointed.
Collection
Close the browser or acquire from an image: live databases are locked and recent changes sit in the -wal or -journal file. Always copy the companions with each database.
UAC's files/browsers/* artifacts (in the full profile, not in ir_triage) cover Firefox, Chrome, Chromium, Edge, Brave, Opera and Vivaldi, including snap and Flatpak locations (the Chromium artifact covers only the snap and Flatpak paths, so copy a deb/rpm ~/.config/chromium yourself); caches are excluded by default (files/browsers/cache.yaml). Check your UAC version for the Firefox 147 ~/.config/mozilla/firefox path.
sudo ./uac -p full /mnt/usb/case42
# or targeted, from a read-only mount
cd /mnt/evidence
find home root -maxdepth 8 \( -name 'places.sqlite*' -o -name 'History' -o -name 'History-journal' \
-o -name 'profiles.ini' -o -name 'Local State' -o -name '*.jsonlz4' \) -print0 | tar --null -T - -czf browsers.tgz
Parsing
- Hindsight (obsidianforensics/hindsight) parses Chromium-family profiles and, with
-b Firefox, Firefox history, cookies and form history, into XLSX, JSONL or SQLite output. Point-iat a copied profile directory, for examplehindsight.py -i ./google-chrome/Default -o case42-chrome -f jsonl. - Plaso
webhistpreset includessqlite/firefox_history,sqlite/firefox_downloads,sqlite/chrome_27_history,sqlite/chrome_66_cookiesand cache parsers. - sqlite3 on copies:
-- Firefox visits
SELECT datetime(v.visit_date/1000000,'unixepoch') AS utc, v.visit_type, p.url, p.title
FROM moz_historyvisits v JOIN moz_places p ON p.id = v.place_id ORDER BY v.visit_date;
-- Chrome downloads
SELECT datetime(start_time/1000000-11644473600,'unixepoch') AS utc, target_path, tab_url, referrer, received_bytes, opened
FROM downloads ORDER BY start_time;
Firefox jsonlz4 files start with the magic mozLz40\0; strip the 8-byte magic and 4-byte size, then LZ4-block-decompress (for example with the Python lz4 package).
Investigator tips
- On Ubuntu, check the snap paths first; a nearly empty
~/.mozillanext to a busy~/snap/firefoxis normal. - Tie downloads to the filesystem:
target_pathordownloads/destinationFileURIgives the path to check for inode times, and files in/tmpor/dev/shm(see tmp and shm). - Follow a file from download to use: download record, then recently-used.xbel for opening, then shell history for
chmod +xor execution. - A root-owned or service-account browser profile on a server is unusual and worth explaining.
- Run
PRAGMA freelist_count;and carve free pages when history looks cleared. - Browsers keep cookies and saved credentials in these profiles; collect them only when in scope, and do not attempt to decrypt credentials.