InstallHistory.plist: macOS Software Install Log
InstallHistory.plist and the package receipt database show which apps, packages and system updates were installed on a Mac, by which process and when.
- Location
- /Library/Receipts/InstallHistory.plist
- Proves
- Which software packages and updates were installed, when, and through which installer process
- Timestamps
- Plist date values (UTC); receipt install-time in Unix epoch seconds
- Access
- Readable by all users; InstallHistory.plist is writable by the admin group
- Retention
- Until deleted; receipts until pkgutil --forget or removal
- Collection
- UAC, Aftermath, mac_apt, ditto
Tools
Compare all tools- mac_aptCLI · open source
- PlasoCLI · open source
- pkgutilCLI · built into macOS
- plutilCLI · built into macOS
What it is
macOS keeps two records of software installation. InstallHistory.plist is an append-only array with one entry per install session: macOS updates, App Store apps, security data updates (XProtect, Gatekeeper, MRT) and every flat package run through Installer or the installer command. Separately, the Installer "receipt" database keeps a .plist and a .bom (bill of materials) per installed package identifier, which pkgutil queries.
Together with /var/log/install.log they answer when a package arrived and which files it laid down. Apps installed by dragging an .app bundle to /Applications do not appear in either; for those, rely on quarantine events, Spotlight metadata and file system times.
Where it lives
| Artifact | Path | Notes |
|---|---|---|
| Install history | /Library/Receipts/InstallHistory.plist | Binary or XML plist, array of dictionaries |
| Third-party receipts | /private/var/db/receipts/<package-id>.plist and .bom | Written by Installer for non-Apple packages |
| Apple component receipts | /Library/Apple/System/Library/Receipts/ | From 10.15 Catalina: XProtect, MRT, Gatekeeper data, Rosetta, Command Line Tools |
| Legacy system receipts | /System/Library/Receipts/ | Before Catalina; now on the read-only system volume and empty |
| Installer log | /private/var/log/install.log | See system logs |
All of these are world-readable on current releases. On a dead-box image look under /System/Volumes/Data/. Since Catalina, full macOS updates no longer leave per-package receipts or BOM files, so OS updates are visible in InstallHistory.plist and install.log but not in the receipt database.
What it proves
- A named package or update (
displayName,displayVersion) was installed at a specific time. - Which process performed it (
processName), separating user-driven installs from background updates. - Which package identifiers were part of it (
packageIdentifiers), the key into the receipt database. - Via the receipt: the install volume and prefix, the installing process, and via the BOM, every file and folder the package installed with owner, mode and size.
It does not prove that the installed software was ever launched, that it is still present, or who approved the install. Uninstalling an app usually leaves both the history entry and the receipt behind.
Key fields
InstallHistory.plist entries:
| Key | Meaning |
|---|---|
date | Install time |
displayName | Product or package name |
displayVersion | Version string |
packageIdentifiers | Array of package IDs (absent for some entries) |
processName | Installing process |
contentType | Present for some entries, e.g. config-data for security data updates |
processName values seen on current releases include installer (command line), Installer (GUI), softwareupdated, appstoreagent, XProtectUpdateService, bootinstalld and CoreServicesUIAgent. A processName of installer often means a script, MDM agent or an attacker ran installer -pkg.
Receipt plist keys: PackageIdentifier, PackageVersion, PackageFileName, InstallDate, InstallPrefixPath, InstallProcessName.
Timestamps
date and InstallDate are plist date values, stored in binary plists as Mac absolute time (seconds since 2001-01-01 UTC) and shown in UTC by plutil -p. pkgutil --pkg-info prints install-time as Unix epoch seconds:
pkgutil --pkg-info com.example.agent
date -u -r 1773637885 # convert install-time to UTC
Retention
InstallHistory.plist is not rotated and keeps growing, and it survives major upgrades (a Mac upgraded from 15 Sequoia to 26 Tahoe keeps its Sequoia-era entries), so it can reach back to the initial setup of the system. Receipts stay until the package is forgotten (pkgutil --forget <id>, which keeps the installed files) or the file is deleted. Receipts need root to change, but InstallHistory.plist is root:admin mode 664 on current releases, so any admin account can edit it without sudo. Older copies may exist in APFS snapshots and Time Machine.
Collection
sudo ditto /Library/Receipts/InstallHistory.plist ./case/
sudo ditto /private/var/db/receipts ./case/receipts
sudo ditto /Library/Apple/System/Library/Receipts ./case/apple_receipts
pkgutil --pkgs > ./case/pkgutil_pkgs.txt # live only
- UAC collects
InstallHistory.plist(files/packages/installed_applications.yaml) and runspkgutil --packageslive. - Aftermath parses
InstallHistory.plistinto CSV in its system recon module. - mac_apt
INSTALLHISTORYplugin extracts and parses it from an image.
Parsing
python3 - <<'PY'
import plistlib
for e in plistlib.load(open('InstallHistory.plist', 'rb')):
print(e['date'].isoformat(), e.get('processName'), e.get('displayName'),
e.get('displayVersion'), ','.join(e.get('packageIdentifiers', [])), sep='\t')
PY
pkgutil --files com.example.agent # file list from the BOM
lsbom -p MUGsf /private/var/db/receipts/com.example.agent.bom
- mac_apt:
python mac_apt.py -o out E01 mac.E01 INSTALLHISTORY. - Plaso
macos_install_historyplist plugin:log2timeline.py --parsers 'plist/macos_install_history' --storage-file ih.plaso InstallHistory.plist. pkgutil --volume /Volumes/Image --pkgslists receipts on a mounted image.
Investigator tips
- Sort by
dateand look at third-party packages installed withprocessName = installeraround the incident: packaged malware and remote-management tools often arrive that way. - Resolve
packageIdentifiersto the receipt and BOM to list every file the package dropped, especially under/Library/LaunchDaemons,/Library/LaunchAgentsand/Library/PrivilegedHelperTools. Pivot to launchd jobs. - Match the time against
install.log: theInstallerprocess recordsOpened from: <path>.pkg, which gives the package's original location, andpackage_script_servicelines show preinstall and postinstall script output. - Tie the downloaded
.pkgto quarantine events and tosyspolicydassessments in the Unified Logs. XProtectUpdateServiceandconfig-dataentries date security data updates, which helps establish which XProtect version protected the Mac at a given time.- Because admins can rewrite
InstallHistory.plistwithoutsudo, check its modification time and compare entries withinstall.logand receipts. A receipt without a matching history entry, or a history entry for a package ID with no receipt, suggestspkgutil --forgetor manual deletion.