Spotlight Forensics: Metadata Stores, mdls and Parsing
How to use the Spotlight index, mdls, mdfind and com.apple.metadata xattrs to recover download origins, usage counts and file history on macOS.
Spotlight is usually thought of as a search bar, but for an investigator it is a metadata database that macOS maintains continuously about almost every file on indexed volumes. It can answer questions that the file system alone cannot: where a file was downloaded from, when it was added to a folder, when and how often it was opened, and sometimes what a file contained or that it existed at all after it was deleted. This article covers where the Spotlight stores live, which attributes matter, how to query them on a live system, and how to parse them offline from a collected image.
Where Spotlight data lives
Spotlight keeps one index store per volume, in a hidden directory at the volume root. On macOS Catalina and later, where the boot disk is split into System and Data volumes (see APFS snapshots and timestamps), the index that matters for user files is on the Data volume.
| Location | Contents | Notes |
|---|---|---|
/System/Volumes/Data/.Spotlight-V100/ | Index store(s) for the Data volume | Root-owned and privacy-protected; requires root and Full Disk Access to read on a live system |
/.Spotlight-V100/ on external or secondary volumes | Per-volume index | Its presence on a USB drive shows the drive was mounted and indexed on a Mac |
.Spotlight-V100/Store-V2/<UUID>/ | store.db, .store.db, dbStr-*.map.* files, journals | The dbStr map files hold strings referenced by the database and are needed for offline parsing |
.Spotlight-V100/VolumeConfiguration.plist | Volume indexing settings | Commonly includes the privacy exclusion list configured in System Settings; verify on your target version |
~/Library/Metadata/CoreSpotlight/ | Per-user CoreSpotlight indexes used by apps donating content | Layout varies by macOS release; treat as a separate source |
store.db and .store.db are both copies of the metadata database; the hidden one is often a slightly older or alternate version, and parsing both can recover entries that differ between them. The files use a proprietary format, not SQLite, so sqlite3 will not open them.
A volume containing a .metadata_never_index file at its root is not indexed, and indexing can also be disabled per volume. The absence of a store is therefore not proof that nothing happened on that volume.
Key attributes for investigations
Spotlight attributes use the kMDItem prefix. Dozens exist, but a handful carry most of the investigative value.
| Attribute | What it tells you | Typical source |
|---|---|---|
kMDItemWhereFroms | Download URL and, often, the referring page | Written by browsers, Mail, AirDrop and other apps as an xattr |
kMDItemDateAdded | When the file was placed in its current directory | Maintained by the file system/Spotlight; updates on move |
kMDItemLastUsedDate | Last time the file was opened through LaunchServices | Backed by the com.apple.lastuseddate#PS xattr on recent systems |
kMDItemUseCount | Number of recorded uses | Not always present; increments are not guaranteed for every open |
kMDItemUsedDates | Array of dates (day granularity) on which the file was used | Useful to show repeated access over time |
kMDItemFSCreationDate / kMDItemFSContentChangeDate | File system birth and modification times as indexed | Compare with live stat output to spot changes after indexing |
kMDItemContentCreationDate / kMDItemContentModificationDate | Content-level dates, sometimes from embedded document metadata | May differ from file system dates, which is itself informative |
kMDItemAuthors, kMDItemCreator, kMDItemTitle | Embedded document metadata | Document and media files |
kMDItemContentType / kMDItemContentTypeTree | Uniform Type Identifier of the file | Helps find executables or disk images renamed with misleading extensions |
"Used" in Spotlight terms means opened via LaunchServices, for example double-clicked in Finder or opened from an app's Open dialog. Reads performed by a script with cat or by a process opening the file directly will not update these fields. Treat them as evidence of user-level interaction, not of every access.
Querying on a live system
mdls
mdls prints the indexed attributes of a file. Restricting the output with -name keeps it readable:
mdls -name kMDItemWhereFroms \
-name kMDItemDateAdded \
-name kMDItemLastUsedDate \
-name kMDItemUseCount \
-name kMDItemUsedDates \
~/Downloads/example-installer.dmg
Dates are displayed in UTC with a +0000 suffix. If a value shows (null), the attribute is absent in the index for that file, which is different from the file never having been used; the store may simply not have recorded it.
mdfind
mdfind runs queries against the live index. It is fast and useful for sweeping a whole system for a pattern:
# Files downloaded from a given domain
mdfind 'kMDItemWhereFroms == "*example.com*"'
# Disk images anywhere on indexed volumes
mdfind 'kMDItemContentType == "com.apple.disk-image-udif"'
# Files added to the user's Downloads folder in the last 7 days
mdfind -onlyin ~/Downloads 'kMDItemDateAdded >= $time.today(-7)'
# Name search
mdfind -name "invoice"
Because mdfind only returns what the index currently knows, results are bounded by exclusions, indexing lag and anything the user or an attacker removed. Use it for triage and lead generation, then confirm with file-level evidence.
mdutil
mdutil -s / reports whether indexing is enabled for a volume and is safe to run. mdutil -i off disables indexing and mdutil -E erases and rebuilds the store. Both destroy or change evidence and should never be run before the store has been collected. Conversely, finding that indexing was recently disabled on a suspect machine is itself worth noting; check the Unified Logs for mds and mdutil activity around that time.
The com.apple.metadata extended attributes
Some Spotlight attributes are not generated by the indexer but stored on the file itself as extended attributes named com.apple.metadata:<attribute>. The most important is com.apple.metadata:kMDItemWhereFroms, a binary property list containing an array of strings, typically the download URL followed by the referring page.
# List extended attributes
xattr -l ~/Downloads/example-installer.dmg
# Decode kMDItemWhereFroms from hex to a readable plist
xattr -px com.apple.metadata:kMDItemWhereFroms ~/Downloads/example-installer.dmg \
| xxd -r -p > /tmp/wherefroms.plist
plutil -p /tmp/wherefroms.plist
[
0 => "https://downloads.example.com/files/example-installer.dmg"
1 => "https://www.example.com/download"
]
Because the attribute lives on the file, it survives copies within APFS and HFS+ volumes, and it can be read directly from a mounted image or collected with tools that preserve xattrs. It is also frequently accompanied by the com.apple.quarantine attribute, which links the file to the QuarantineEventsV2 database; see Quarantine events and Gatekeeper and the quarantine attribute glossary entry. ls -l@ gives a quick overview of which attributes are present.
Offline parsing of the Spotlight store
For dead-box analysis or when the index must be preserved exactly, collect the whole .Spotlight-V100 directory (including the dbStr-* map files) and parse it on an analysis machine.
- spotlight_parser (Yogesh Khatri) reads
store.dband.store.dband writes the metadata for each indexed item, keyed by inode number, with an option to reconstruct full paths. It runs on any OS with Python. - mac_apt includes a
SPOTLIGHTplugin that processes the store from an image or mounted volume and outputs to SQLite (with optional spreadsheet or delimited-text export), alongside other plugins such as the one for Spotlight search shortcuts (the terms users typed into the Spotlight bar and the result they chose, where that file exists on the target version).
# Example: run the mac_apt SPOTLIGHT plugin against a mounted image
python3 mac_apt.py -o /cases/MBP-IR-01/out MOUNTED /Volumes/evidence SPOTLIGHT
Timestamps inside the store are recorded in Mac Absolute Time (seconds since 2001-01-01 UTC, i.e. Unix time minus 978307200). Both tools convert them for you, but keep the epoch in mind when you cross-check raw values.
The main advantage of offline parsing is that the store can retain entries for files that are no longer present on disk until the indexer purges them. An entry for a deleted tool in /private/tmp or a user's Downloads folder, with its kMDItemWhereFroms and dates, can be the only remaining trace of it. Correlate such entries with FSEvents records for the same path to establish when the deletion occurred.
Investigative workflow
- Collect
.Spotlight-V100from every relevant volume, together with the user's~/Library/Metadatadirectory, using a tool with Full Disk Access (see forensic acquisition). - On the live system, if appropriate, run targeted
mdlson files of interest and record output before anything else touches them. - Parse the stores offline and filter by path (Downloads, Desktop,
/tmp,/Users/Shared) and by content type (applications, disk images, scripts, archives). - Pivot on
kMDItemWhereFromsvalues to the Safari or Chrome history (see Safari browser forensics) and the quarantine database. - Place
kMDItemLastUsedDateandkMDItemUsedDatesalongside KnowledgeC and Biome app usage to show what was opened and with which application.
Pitfalls and caveats
- Exclusions and disabled indexing. Folders added to Spotlight privacy, volumes with
.metadata_never_index, and volumes where indexing was turned off leave gaps. CheckVolumeConfiguration.plistandmdutil -soutput before drawing conclusions from absence. - Indexing lag. The indexer processes changes asynchronously. Very recent files may not yet be in the store, and very short-lived files may never be indexed.
- Not every access is recorded. Usage attributes reflect LaunchServices opens. Command-line or programmatic access does not update them, and
kMDItemUsedDateshas day-level granularity. - Attributes can be removed.
xattr -dorxattr -cremoveskMDItemWhereFromsfrom a file, and tools such ascurlnever write it. A missing origin does not mean the file was created locally. - Format changes. The store format and the CoreSpotlight layout evolve across macOS releases. Validate parser output against a known test system on the same version before relying on it in a report.
- Destructive commands.
mdutil -Eandmdutil -i offmodify evidence. Even opening a mounted image read-write can trigger indexing of that image, so mount evidence read-only.
Frequently asked questions
Can Spotlight metadata show where a file was downloaded from?
Often, yes. Browsers and other quarantine-aware apps write kMDItemWhereFroms as a com.apple.metadata extended attribute on the file, and Spotlight indexes it. mdls shows the value on a live system, and the xattr travels with the file on APFS and HFS+ copies, but it can be stripped or never written by some tools.
Do I need a live system to analyze Spotlight data?
No. mdls and mdfind query the live index, but the per-volume store under .Spotlight-V100 can be collected and parsed offline with spotlight_parser or the mac_apt SPOTLIGHT plugin, which can also surface metadata for files that no longer exist on disk.
Does running mdutil change evidence?
mdutil -s only reports status, but mdutil -E erases and rebuilds the index and mdutil -i off disables indexing. Both alter or destroy the store, so never run them on a system under investigation before the .Spotlight-V100 data has been collected.