Launch Daemon
Adversaries may create or modify Launch Daemons to execute malicious payloads as part of persistence or privilege escalation. Launch Daemons are plist files that interact with Launchd, the macOS service management framework. They require elevated (root) privileges to install, execute before any user logs in, and run continuously in the background without user interaction. During macOS initialization, launchd loads parameters from plist files in /System/Library/LaunchDaemons/ and /Library/LaunchDaemons/. Required keys include Label (identifier), Program or ProgramArguments (executable path), and RunAtLoad (execute on boot). Adversaries install daemons with RunAtLoad=true and a Program key pointing to a malicious executable, frequently using KeepAlive=true to restart after crashes. Daemon names are commonly disguised to mimic legitimate macOS services (e.g., com.apple.syslogd.update) to blend with hundreds of legitimate system daemons. Additionally, path hijacking attacks are possible when third-party package managers (Homebrew, MacPorts) create globally writable directories like /usr/local/bin/. Known malware families using this technique include ThiefQuest (ransomware/spyware), LoudMiner (cryptominer using com.[random_name].plist naming), OSX_OCEANLOTUS.D (APT32 backdoor), Dacls (Lazarus Group), XCSSET (Xcode supply chain malware using SSH daemon), AppleJeus (North Korean cryptocurrency theft), Bundlore (adware), and Green Lambert (nation-state implant). The daemon inherits administrative permissions at execution time, making this a combined persistence and privilege escalation vector.
let TrustedDaemonInstallers = dynamic(["installd", "softwareupdate", "mdmclient", "system_installd", "launchd", "cfprefsd", "mds_stores"]);
let SuspiciousBinaryLocations = dynamic(["/tmp/", "/var/tmp/", "/private/tmp/", "/Users/Shared/", "/dev/"]);
let LaunchDaemonDirs = dynamic(["/Library/LaunchDaemons/", "/System/Library/LaunchDaemons/"]);
// Signal 1: plist file creation or modification in LaunchDaemon directories by non-trusted processes
let NewDaemonPlist = DeviceFileEvents
| where Timestamp > ago(24h)
| where OSPlatform has_any ("macOS", "Mac")
| where FolderPath has_any (LaunchDaemonDirs)
| where FileName endswith ".plist"
| where ActionType in ("FileCreated", "FileModified")
| where InitiatingProcessFileName !in~ (TrustedDaemonInstallers)
| extend DetectionType = "LaunchDaemon_PlistWrite"
| extend SuspiciousPayload = InitiatingProcessCommandLine has_any (SuspiciousBinaryLocations)
| project Timestamp, DeviceName, AccountName = InitiatingProcessAccountName,
DetectionType, Artifact = strcat(FolderPath, FileName),
InitiatingProcess = InitiatingProcessFileName,
CommandLine = InitiatingProcessCommandLine,
ParentProcess = InitiatingProcessParentFileName,
SHA256, SuspiciousPayload;
// Signal 2: launchctl explicitly loading or bootstrapping daemons from LaunchDaemon directories
let LaunchctlLoad = DeviceProcessEvents
| where Timestamp > ago(24h)
| where OSPlatform has_any ("macOS", "Mac")
| where FileName =~ "launchctl"
| where ProcessCommandLine has_any (LaunchDaemonDirs)
| where ProcessCommandLine has_any ("load ", "bootstrap ", "enable ", "kickstart ")
| where InitiatingProcessFileName !in~ (TrustedDaemonInstallers)
| extend DetectionType = "LaunchDaemon_LaunchctlLoad"
| extend SuspiciousPayload = ProcessCommandLine has_any (SuspiciousBinaryLocations)
| project Timestamp, DeviceName, AccountName,
DetectionType, Artifact = ProcessCommandLine,
InitiatingProcess = InitiatingProcessFileName,
CommandLine = ProcessCommandLine,
ParentProcess = InitiatingProcessParentFileName,
SHA256 = "", SuspiciousPayload;
// Union both detection signals
union NewDaemonPlist, LaunchctlLoad
| sort by Timestamp desc Data Sources
Required Tables
False Positives
- Legitimate software installers (PKG files) deploying system daemons — these are written by installd or system_installd which are excluded from the query
- Endpoint management solutions (Jamf Pro, Kandji, Mosyle) deploying daemon configurations via MDM enrollment profiles
- Developer tools installing local service daemons (Docker Desktop installs com.docker.vmnetd, Homebrew-managed services via brew services)
- IT configuration management platforms (Chef, Puppet, Ansible) deploying managed daemon configurations as part of infrastructure-as-code runs
- macOS major version upgrades that modify or recreate system daemons via softwareupdate or the migration assistant
References (9)
- https://attack.mitre.org/techniques/T1543/004/
- https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html
- https://www.sentinelone.com/blog/how-malware-persists-on-macos/
- https://bradleyjkemp.dev/post/launchdaemon-hijacking/
- https://www.real-world-systems.com/docs/launchdPlist.1.html
- https://www.virusbulletin.com/uploads/pdf/conference/vb2014/VB2014-Wardle.pdf
- https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1543.004/T1543.004.md
- https://objective-see.org/blog/blog_0x4C.html
- https://www.paloaltonetworks.com/content/dam/pan/en_US/assets/pdf/reports/Unit_42/unit42-wirelurker.pdf
Unlock Pro Content
Get the full detection package for T1543.004 including response playbook, investigation guide, and atomic red team tests.