---
title: "Detect Suspicious launchctl Load Patterns | Security Cookbook"
description: "Detect and block launchctl loading LaunchAgents from temp directories or with random plist names, stopping malware persistence on macOS."
doc_version: "1"
last_updated: "2026-05-22"
canonical: "https://northpole.security/cookbook/detect-malicious-launchctl"
---
[Back](https://northpole.security/cookbook)

### Idea

Malware establishes persistence on macOS by creating LaunchAgents or LaunchDaemons and loading them with `launchctl load`. While [monitoring file creation](https://northpole.security/cookbook/monitor-launch-items) in Launch directories provides one layer of defense, attackers may create malicious plists in other locations and load them directly.

Malware families that use launchctl for persistence include:

-   OSX.CoinMiner: Creates mining daemons in `/Library/LaunchDaemons/`
-   Lazarus APT Group: Establishes persistence via LaunchAgents
-   OSX.Dummy: Uses LaunchAgents with random names
-   FinFisher: Sophisticated spyware with LaunchAgent persistence
-   Silver Sparrow: Mysterious malware with persistence mechanisms
-   XCSSET: Targets Xcode developers with LaunchAgent backdoors

Common malware patterns:

-   Loading plists from temporary directories (`/tmp/`, `/var/tmp/`)
-   Random-looking plist filenames (verx.plist, init\_verx.plist)
-   Hex string filenames (`a3f5c8d2.plist`)
-   Loading from user-writable locations outside standard directories

This rule detects suspicious launchctl patterns that are unlikely to be legitimate software but common in malware.

### Solutions

ExecutionBlock launchctl Load from Suspicious Locations

Block launchctl load from temporary and non-standard locations

Identifier

platform:com.apple.launchctl

Rule Type

SIGNINGID

Policy

CEL

CEL Expression

(
  args.exists(arg, arg in \['load', 'enable'\]) &&
  !args.join(" ").matches(".\*/System/Library/Launch(Agents|Daemons)/.\*") &&
  (
    args.join(" ").contains("/tmp/") ||
    args.join(" ").contains("/var/tmp/") ||
    args.join(" ").contains("/private/tmp/")
  )
) ? BLOCKLIST : ALLOWLIST

Copy

Custom Message

Loading LaunchAgents from temporary directories is not allowed

ExecutionDetect Random-Looking Plist Names

Flag suspicious random-looking LaunchAgent names for audit

Identifier

platform:com.apple.launchctl

Rule Type

SIGNINGID

Policy

CEL

CEL Expression

(
  args.exists(arg, arg in \['load', 'enable'\]) &&
  args.join(" ").matches(".\*/(verx|init\_verx|\[a-f0-9\]{8,}|\\\\.\[a-z\]{4,})\\\\.(plist|agent).\*")
) ? BLOCKLIST : ALLOWLIST

Copy

Custom Message

Suspicious LaunchAgent name detected - please contact security team

ExecutionAudit All Non-System launchctl Loads

Audit mode to analyze launchctl patterns before blocking

Identifier

platform:com.apple.launchctl

Rule Type

SIGNINGID

Policy

CEL

CEL Expression

(
  args.exists(arg, arg in \['load', 'enable', 'bootstrap'\]) &&
  !args.join(" ").matches(".\*/System/Library/.\*")
) ? AUDIT: ALLOWLIST

Copy

Custom Message

Non-system LaunchAgent load detected (audit mode)

### Mitre Attack

Tactics

[Persistence](https://attack.mitre.org/tactics/TA0003/)[Privilege Escalation](https://attack.mitre.org/tactics/TA0004/)

Techniques

[T1543.001: Launch Agent](https://attack.mitre.org/techniques/T1543/001/)[T1543.004: Launch Daemon](https://attack.mitre.org/techniques/T1543/004/)

### Tags

launchctlpersistencelaunchagentlaunchdaemonmalware

### Deployment Notes

Deployment approach:

1.  Start with the Audit rule to understand launchctl patterns in your environment
2.  Analyze audit data for 1-2 weeks to identify legitimate non-system loads
3.  Add exceptions for legitimate software installers
4.  Deploy blocking rules for suspicious locations first
5.  Consider the random-name detection rule for advanced protection

Legitimate launchctl load patterns:

-   System installers loading from `/Library/LaunchAgents/` or `/Library/LaunchDaemons/`
-   User applications loading from `~/Library/LaunchAgents/`
-   Enterprise management tools (Jamf, Munki, etc.)

The rule allows all System Library loads and only flags non-standard locations.

### False Positive Guidance

Legitimate software may trigger these rules:

-   Developer testing: Loading test plists during development
-   Enterprise software: Some enterprise tools use non-standard plist names
-   Package installers: Installers may stage plists in temp locations during installation
-   Migration scripts: System migration tools may load plists temporarily

Before blocking:

-   Run in audit mode first
-   Review Santa events for launchctl usage patterns
-   Identify legitimate software in your environment
-   Add signing ID exceptions for approved software

Consider allowing specific signing IDs for enterprise management tools.

### Testing Instructions

1.  Create a test plist in /tmp/:
    
    ```
    cat > /tmp/test.plist <<EOF
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0"><dict></dict></plist>
    EOF
    ```
    
2.  Try to load it: `launchctl load /tmp/test.plist` (should be blocked)
    
3.  Test legitimate load: `launchctl load ~/Library/LaunchAgents/com.example.legitimate.plist` (should work if exists)
    
4.  Test random name detection: `touch /tmp/a3f5c8d2.plist` `launchctl load /tmp/a3f5c8d2.plist` (should be blocked)
    

### Detection Methods

Monitor CEL execution events for launchctl blocks. Focus on:

Suspicious indicators:

-   Loads from `/tmp/`, `/var/tmp/`
-   Random hex string filenames
-   Unusual plist names (verx, init\_verx)
-   Loads outside standard directories

Investigation steps:

1.  Examine the plist contents (may be deleted by malware)
2.  Check parent process - what triggered launchctl?
3.  Review user account - is this a compromised account?
4.  Look for related file system activity around the same time
5.  Check if the plist references suspicious executables

Legitimate loads typically:

-   Come from known installers
-   Use descriptive, brand-related names
-   Target standard LaunchAgent directories
-   Are signed by known developers

### Resources

[The Evil Bit: Launch(Agents|Daemons) PersistenceTake a look](https://theevilbit.github.io/beyond/beyond_0010/)[MITRE ATT&CK - Launch AgentTake a look](https://attack.mitre.org/techniques/T1543/001/)

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

Persistence PreventionFile Access

#### Prevent Cron and At Job Persistence

Stop attackers from gaining macOS persistence through cron and at jobs. Workshop file access rules block writes to crontab and at job directories.



](https://northpole.security/cookbook/prevent-cron-persistence)[

Defense EvasionExecution

#### Prevent Unloading of Security Services

Block launchctl from unloading EDR, MDM, and observability LaunchDaemons, preventing attackers from disabling your security stack after compromise.



](https://northpole.security/cookbook/prevent-security-service-unload)

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