---
title: "Prevent Unloading of Security Services | Security Cookbook"
description: "Block launchctl from unloading EDR, MDM, and observability LaunchDaemons, preventing attackers from disabling your security stack after compromise."
doc_version: "1"
last_updated: "2026-05-22"
canonical: "https://northpole.security/cookbook/prevent-security-service-unload"
---
[Back](https://northpole.security/cookbook)

### 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

ExecutionBlock Security Service Unloading

Prevent launchctl from unloading critical security services

Signing ID

platform:com.apple.launchctl

CEL Expression

args.exists(a, a \== 'unload' || a \== 'bootout' || a \== 'remove' || a \== 'disable') &&
args.exists(a,
  a.contains('com.crowdstrike.falcon') ||
  a.contains('com.sentinelone') ||
  a.contains('com.microsoft.fresno') ||
  a.contains('com.sophos.endpoint') ||
  a.contains('com.vmware.carbonblack') ||
  a.contains('com.jamf') ||
  a.contains('com.mosyle') ||
  a.contains('com.kandji') ||
  a.contains('io.osquery.agent') ||
  a.contains('co.elastic.endpoint')
) ? BLOCKLIST : ALLOWLIST

Copy

Custom Message

Unloading security services is not allowed

### Mitre Attack

Tactics

[Defense Impairment](https://attack.mitre.org/tactics/TA0112/)

Techniques

[T1685: Disable or Modify Tools](https://attack.mitre.org/techniques/T1685/)

### Tags

launchctllaunchdaemondefense-evasionedr

### 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](https://northpole.security/cookbook/monitor-launch-items) 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.

1.  Try to unload a protected service: `sudo launchctl unload /Library/LaunchDaemons/io.osquery.agent.plist` (should be blocked)
2.  Try the modern subcommand form: `sudo launchctl bootout system/io.osquery.agent` (should also be blocked)
3.  Try to load a protected service: `sudo launchctl load /Library/LaunchDaemons/io.osquery.agent.plist` (should work — `load` is not in the blocked subcommand list)
4.  Try to unload an unprotected service: `sudo launchctl unload /Library/LaunchDaemons/com.example.test.plist` (should work)
5.  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

[MITRE ATT&CK - Disable or Modify ToolsTake a look](https://attack.mitre.org/techniques/T1685/)[Objective-See: Disabling macOS Security ToolsTake a look](https://objective-see.com/blog/blog_0x6A.html)

### Related Rules

[

Persistence PreventionFile Access

#### Monitor Launch Item Creation

Audit all writes to LaunchAgent and LaunchDaemon directories to surface persistence attempts with complete process context for incident response.



](https://northpole.security/cookbook/monitor-launch-items)[

Defense EvasionExecution

#### Prevent Gatekeeper from Being Disabled

Block spctl from disabling macOS Gatekeeper protections, stopping attackers and social engineering attempts from weakening signature enforcement.



](https://northpole.security/cookbook/prevent-gatekeeper-disable)[

Privilege Escalation PreventionFile Access

#### Prevent TCC Database Tampering

Restrict TCC.db writes to Apple tccd daemon, blocking malware from granting itself camera, microphone, and contacts access on macOS.



](https://northpole.security/cookbook/prevent-tcc-tampering)

## Sitemap

- [Home](https://northpole.security/index.md)
- [Workshop](https://northpole.security/workshop.md)
- [Santa](https://northpole.security/santa.md)
- [Features](https://northpole.security/features.md)
- [Cookbook](https://northpole.security/cookbook.md)
- [Docs](https://northpole.security/docs.md)
- [Blog](https://northpole.security/blog.md)
- [Glossary](https://northpole.security/glossary.md)
- [About](https://northpole.security/about.md)
- [Contact](https://northpole.security/contact.md)
