Launchd Persistence
Malware drops a LaunchAgent or LaunchDaemon property list so macOS launchd starts its payload at login or boot, the dominant persistence mechanism on macOS.
Launchd is the macOS service manager and the parent of every process on the system. It starts and supervises jobs described by property-list (.plist) files: LaunchAgents run in a user's context at login, while LaunchDaemons run as root at boot before any user logs in. Because launchd is the sanctioned way to run background software on macOS, nearly all macOS malware — adware droppers, stealers, and backdoors alike — persists through it.
A persistence plist names the job, points ProgramArguments at the payload, and sets a trigger such as RunAtLoad (start immediately) or KeepAlive (restart on exit). The combination of RunAtLoad and KeepAlive gives both boot/login persistence and a watchdog. The location the plist is written to determines its privilege and scope.
How it works
Launchd reads plists from fixed directories, in increasing order of privilege:
~/Library/LaunchAgents/ per-user agent (no admin)
/Library/LaunchAgents/ all-user agent (admin)
/Library/LaunchDaemons/ root daemon, runs at boot (admin)
/System/Library/Launch*/ Apple-only (SIP-protected)A minimal malicious agent plist contains:
Label: com.apple.softwareupdate.helper
ProgramArguments: ["/Users/Shared/.update/agent"]
RunAtLoad: true
KeepAlive: trueThe Label is chosen to impersonate Apple or a trusted vendor (com.apple.*, com.adobe.*), and the payload is stashed in a hidden directory under /Users/Shared or the user's Library. Operators load the job with launchctl load (or bootstrap on current macOS) so it runs without a reboot, and the plist re-loads automatically at the next login or boot.
Detection & analysis
Static analysis:
- Enumerate the four non-Apple Launch* directories above on a live or imaged Mac and read each plist. Inspect
Label,ProgramArguments,RunAtLoad, andKeepAlive; a binary in/Users/Shared, a hidden dotted path, or/tmp, especially behind an Apple-looking label, is high-signal. - Convert binary plists to readable XML with
plutil -p <file>before triage. The plist's mtime and the payload's signing status (codesign -dv, often unsigned or ad-hoc) date and characterise the intrusion. - In a captured sample, look for writes to the LaunchAgents/LaunchDaemons paths, the
.plistextension, the keysProgramArguments/RunAtLoad, orexecveoflaunchctl.
Dynamic analysis:
- Run the sample under Endpoint Security /
fs_usageand watch for a.plistbeing written into any Launch* directory, followed by alaunchctl load/bootstrap. The writing process and the new label are the key artefacts. - At runtime,
launchdspawning an unexpected child from/Users/Sharedor a hidden path, or a new label appearing inlaunchctl list, confirms the persistence.
Detection rule hint:
Alert on creation or modification of any .plist under the writable Launch* directories by a process that is not Apple's installer/pkgutil. Flag plists whose ProgramArguments[0] resolves to /tmp, /Users/Shared, or a hidden directory, those whose Label impersonates com.apple.* while the binary is unsigned, and any pairing of RunAtLoad with KeepAlive outside a known-good baseline.