macOS launchd deep audit LaunchAgent / LaunchDaemon plist deep audit (L1-L7) — what the generic persistence detector misses.
The generic persistence_outlier detector flags
launchd plists whose programs live in writable temp dirs.
MacosLaunchdDetector covers the macOS-launchd-specific
patterns it doesn't.
How it works
The macos.launchd collector already emits one Artifact
per plist (parsing ProgramArguments / RunAtLoad / KeepAlive /
WatchPaths / QueueDirectories / Label / etc); this detector runs
deeper rules against those fields. Apple's
/System/Library/Launch{Daemons,Agents}/* plists with a
com.apple.* Label are SIP-protected and skipped.
Detection layers (L1–L7)
| ID | Severity | What it catches | MITRE |
|---|---|---|---|
| L1 | high → critical | Network-fetch in ProgramArguments. Critical when piped to shell. The Silver Sparrow / Shlayer / OSAMiner / BundloreRunner downloader pattern. | T1543.001 |
| L2 | medium | Long base64 / hex blob in arguments. Common hiding place for shellcode or a one-line Python downloader. | T1027 |
| L3 | medium | Label / filename mismatch. Plist file com.evil.malware.plist declares Label = com.apple.softwareupdate — documented masquerade in OSX/Silver Sparrow, OSX/Cocyer, JaskaGO. | T1036 |
| L4 | high | WatchPaths or QueueDirectories on user-writable dir. Anyone with write access to that path fires the payload — "drop a file, get RCE" persistence chain. | T1546 |
| L5 | medium | Empty or missing Label key. Most legitimate plists set Label; missing is a malware-behavior signature. | T1543.001 |
| L6 | high | Interpreter (/bin/sh, /bin/bash, python, perl, ruby) + KeepAlive=true = launchd respawn loop = daemon-shaped malware. | T1059.004 |
| L7 | medium | Plist runs osascript. AppleScript can drive other apps via AppleEvents, prompt for credentials, scrape browser state — privilege-escalation primitive. Legit users exist (Bartender, Hammerspoon) but rare in a daemon. | T1059.002 |
No new CLI needed
The existing macos.launchd collector already runs as
part of digger collect; the detector runs at
digger scan time.