Prevent Unloading of Security Services
Block launchctl from unloading EDR, MDM, and observability LaunchDaemons, preventing attackers from disabling your security stack after compromise.
Idea
Once malware gains a foothold on a system, a common next step is disabling the security tools that would detect or contain it. On macOS, those tools — EDR sensors, MDM agents, DLP clients, observability daemons — typically run as LaunchDaemons managed by launchd, and any process running as root can stop them with a single launchctl unload (or bootout) command.
Santa is a natural place to enforce a policy against this. Santa ships with active anti-tampering mechanisms that defend its own security-critical processes and resources from being disabled, removed, or modified; this rule extends that same hardening to the rest of the security stack you deploy alongside it.
Common protection targets:
- EDR sensors (CrowdStrike Falcon, SentinelOne, Microsoft Defender for Endpoint, Sophos, Carbon Black)
- Mac management agents (Jamf, Mosyle, Kandji)
- Observability / audit daemons (osquery, Elastic Agent, Splunk forwarders)
- DLP / CASB clients (Zscaler, Netskope)
The rule below blocks the launchctl subcommands that can stop or remove a service (unload, bootout, remove, disable) when invoked against a representative starter list of security tools. Adapt the bundle-ID list to whatever your fleet runs.
Solutions
- Signing ID
- CEL Expression
- Custom Message
Mitre Attack
Tactics
Techniques
Tags
Deployment Notes
This rule blocks launchctl invocations that would stop a protected security service. Customize the bundle-ID list in the CEL expression to match the tools your fleet actually runs.
Subcommands blocked: unload, bootout, remove, disable. Read-only and lifecycle operations like list, print, start, kickstart, and load remain unaffected. (kill — which signals a running service without unloading it — is intentionally not in the list; consider a separate rule if that's a concern for your environment.)
Bundle-ID prefixes to consider, depending on what you deploy:
- EDR:
com.crowdstrike.falcon,com.sentinelone,com.microsoft.fresno,com.sophos.endpoint,com.vmware.carbonblack - MDM agents:
com.jamf,com.mosyle,com.kandji - Observability / audit:
io.osquery.agent,co.elastic.endpoint - DLP / CASB: vendor-specific (Zscaler, Netskope, etc.)
Note: This rule does not prevent direct plist removal from /Library/LaunchDaemons/. For complete protection, pair it with the launch-items monitoring rule to catch tampering with the on-disk service definitions themselves.
False Positive Guidance
Legitimate reasons to unload security services:
- System administrators troubleshooting issues
- Upgrading security software (may require unload/reload cycle)
- Decommissioning hosts before reimaging
Consider:
- Using Workshop tags to exempt IT admin accounts
- Creating approval workflows for unload operations
- Temporarily disabling the rule during planned maintenance windows
For software updates, vendors should use proper upgrade mechanisms that don't require manual service unloading.
Testing Instructions
Substitute one of the bundle IDs your fleet actually protects. The examples below use io.osquery.agent (osquery) as a stand-in.
- Try to unload a protected service:
sudo launchctl unload /Library/LaunchDaemons/io.osquery.agent.plist(should be blocked) - Try the modern subcommand form:
sudo launchctl bootout system/io.osquery.agent(should also be blocked) - Try to load a protected service:
sudo launchctl load /Library/LaunchDaemons/io.osquery.agent.plist(should work —loadis not in the blocked subcommand list) - Try to unload an unprotected service:
sudo launchctl unload /Library/LaunchDaemons/com.example.test.plist(should work) - Verify the protected service is still running:
sudo launchctl list | grep osquery(should show the service active)
Detection Methods
Monitor for CEL execution events showing blocked launchctl commands. Any attempt to unload security services should generate an alert.
Investigate:
- Who attempted the unload (user account)
- What binary called launchctl (direct terminal or script?)
- Timing (during business hours or off-hours?)
- Parent process (was it an automated script or manual command?)
False positives are rare - most unload attempts are either malicious or unauthorized troubleshooting.
Resources
Related Rules
Monitor Launch Item Creation
Audit all writes to LaunchAgent and LaunchDaemon directories to surface persistence attempts with complete process context for incident response.
Prevent Gatekeeper from Being Disabled
Block spctl from disabling macOS Gatekeeper protections, stopping attackers and social engineering attempts from weakening signature enforcement.
Prevent TCC Database Tampering
Restrict TCC.db writes to Apple tccd daemon, blocking malware from granting itself camera, microphone, and contacts access on macOS.