CrowdStrike MacOS plutil Novel Plist Modification (Anomaly Detection)


Description

Detects when plutil performs modification operations (insert, replace, remove, create) on plist files it hasn't modified in the previous 30 days. Uses behavioral filtering to exclude read-only operations (convert, print, lint). This anomaly-based approach reduces noise from legitimate repeated operations while catching novel persistence attempts, even in /Applications/.

Query · python

def rule(event):
    # The scheduled query returns only new plist files that haven't been modified before
    # Add additional filtering here if needed
    plist_file = event.get("plist_file", "")

    # Filter out empty, malformed, or invalid results
    if not plist_file:
        return False

    # Filter out whitespace-only strings
    if isinstance(plist_file, str) and not plist_file.strip():
        return False

    # Filter out placeholder/error values from query
    if plist_file == "<UNKNOWN_FILE>":
        return False

    # Any valid result from the query should trigger an alert
    return True


def dedup(event):
    # Group alerts by device and plist file
    device_id = event.get("device_id", "<UNKNOWN_DEVICE>")
    plist_file = event.get("plist_file", "<UNKNOWN_FILE>")
    return f"{device_id}:{plist_file}"


def title(event):
    plist_file = event.get("plist_file", "<UNKNOWN_FILE>")
    return f"Crowdstrike: plutil modified new plist file: {plist_file}"


def severity(event):
    plist_file = event.get("plist_file", "")

    # Critical: System-level persistence with elevated privileges
    if any(
        loc in plist_file for loc in ["/System/Library/LaunchDaemons/", "/Library/LaunchDaemons/"]
    ):
        return "HIGH"

    # High: System-level LaunchAgents or system directory modifications
    if "/Library/LaunchAgents/" in plist_file or "/System/" in plist_file:
        return "HIGH"

    # Default: Medium for all other novel modifications
    return "DEFAULT"


def alert_context(event):
    plist_file = event.get("plist_file", "<UNKNOWN_FILE>")

    # Determine risk indicators based on plist location
    risk_indicators = []
    if any(loc in plist_file for loc in ["/Library/LaunchAgents/", "/Library/LaunchDaemons/"]):
        risk_indicators.append("System-level persistence location")
    elif any(loc in plist_file for loc in ["LaunchAgents/", "LaunchDaemons/"]):
        risk_indicators.append("User-level persistence location")
    elif "/Applications/" in plist_file:
        risk_indicators.append("Application bundle modification (potential tampering)")
    elif "/System/" in plist_file:
        risk_indicators.append("System directory modification (elevated privileges)")

    return {
        "device_id": event.get("device_id", "<UNKNOWN_DEVICE>"),
        "plist_file": plist_file,
        "detection_type": "Anomaly - New Modification Detected",
        "baseline_period": "30 days",
        "risk_indicators": risk_indicators if risk_indicators else ["Non-standard plist location"],
    }

Analyst notes

  1. Verify the plist modification was authorized. Query CrowdStrike for the full plutil command line on the affected device to determine the exact operation performed (insert, replace, remove, create) and identify the parent process that executed the command.
  2. Review the modification context including the user account, source process, and timing. Assess the risk level based on the plist location - LaunchAgents/LaunchDaemons indicate persistence attempts (high risk), while /Applications/ modifications could be legitimate software updates (medium risk). Check for suspicious parent processes such as bash, python, curl, or remote shell activity.
  3. If the modification is suspicious or unauthorized, quarantine the device and analyze the modified plist content for malicious payloads or references to external scripts. Review all processes currently running that may have been launched by the modified plist, revoke any persistence mechanisms, and hunt for related IOCs across other endpoints. Escalate for forensic analysis if compromise is confirmed.
Raw source CrowdStrike MacOS plutil Novel Plist Modification (Anomaly Detection) · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: scheduled_rule
Filename: crowdstrike_macos_plutil_newfiles.py
DisplayName: CrowdStrike MacOS plutil Novel Plist Modification (Anomaly Detection)
Enabled: false
Status: Experimental
Description: |
  Detects when plutil performs modification operations (insert, replace, remove, create) on plist
  files it hasn't modified in the previous 30 days. Uses behavioral filtering to exclude read-only
  operations (convert, print, lint). This anomaly-based approach reduces noise from legitimate
  repeated operations while catching novel persistence attempts, even in /Applications/.
Severity: Medium
DedupPeriodMinutes: 1440
Threshold: 1
Reference: https://attack.mitre.org/techniques/T1547/011/
Runbook: |
  1. Verify the plist modification was authorized. Query CrowdStrike for the full plutil command line on the affected device to determine the exact operation performed (insert, replace, remove, create) and identify the parent process that executed the command.
  2. Review the modification context including the user account, source process, and timing. Assess the risk level based on the plist location - LaunchAgents/LaunchDaemons indicate persistence attempts (high risk), while /Applications/ modifications could be legitimate software updates (medium risk). Check for suspicious parent processes such as bash, python, curl, or remote shell activity.
  3. If the modification is suspicious or unauthorized, quarantine the device and analyze the modified plist content for malicious payloads or references to external scripts. Review all processes currently running that may have been launched by the modified plist, revoke any persistence mechanisms, and hunt for related IOCs across other endpoints. Escalate for forensic analysis if compromise is confirmed.
Tags:
  - Anomaly Detection
  - macOS
  - Persistence
  - CrowdStrike
  - T1547.011
InlineFilters:
  - All: []
ScheduledQueries:
  - CrowdStrike MacOS plutil Novel Plist Modification
RuleID: Crowdstrike.Macos.Plutil.NewFiles
Tests:
  - Name: Anomaly - New LaunchAgent modified for first time (insert operation)
    ExpectedResult: true
    Log:
      {
        "device_id": "abc123def456789",
        "plist_file": "/Library/LaunchAgents/com.suspicious.agent.plist"
      }
  - Name: Anomaly - New LaunchAgent modified for first time (replace operation)
    ExpectedResult: true
    Log:
      {
        "device_id": "abc123def456789",
        "plist_file": "/Library/LaunchAgents/com.attacker.plist"
      }
  - Name: Anomaly - New LaunchDaemon modified for first time (remove operation)
    ExpectedResult: true
    Log:
      {
        "device_id": "xyz789ghi012345",
        "plist_file": "/Library/LaunchDaemons/com.evil.daemon.plist"
      }
  - Name: Anomaly - New plist created for first time (create operation)
    ExpectedResult: true
    Log:
      {
        "device_id": "xyz789ghi012345",
        "plist_file": "/Users/user/Library/LaunchAgents/com.new.persistence.plist"
      }
  - Name: Anomaly - User LaunchAgent never modified before on this device
    ExpectedResult: true
    Log:
      {
        "device_id": "abc123def456789",
        "plist_file": "/Users/john.doe/Library/LaunchAgents/com.malware.backdoor.plist"
      }
  - Name: Anomaly - System LaunchDaemon first modification in 30 days
    ExpectedResult: true
    Log:
      {
        "device_id": "xyz789ghi012345",
        "plist_file": "/System/Library/LaunchDaemons/com.evil.daemon.plist"
      }
  - Name: Anomaly - New application Info.plist modification (potential tampering)
    ExpectedResult: true
    Log:
      {
        "device_id": "mno345pqr678901",
        "plist_file": "/Applications/CustomApp.app/Contents/Info.plist"
      }
  - Name: Anomaly - First-time modification of Homebrew app plist
    ExpectedResult: true
    Log:
      {
        "device_id": "def456ghi789012",
        "plist_file": "/usr/local/Cellar/suspicious-tool/1.0/Info.plist"
      }
  - Name: Anomaly - User preferences plist modified for first time
    ExpectedResult: true
    Log:
      {
        "device_id": "ghi789jkl012345",
        "plist_file": "/Users/jane.smith/Library/Preferences/com.custom.config.plist"
      }
  - Name: Anomaly - Previously unseen plist file in non-standard location
    ExpectedResult: true
    Log:
      {
        "device_id": "stu901vwx234567",
        "plist_file": "/opt/custom/config.plist"
      }
  - Name: Invalid - Empty plist file path
    ExpectedResult: false
    Log:
      {
        "device_id": "abc123def456789",
        "plist_file": ""
      }
  - Name: Invalid - Whitespace only plist path
    ExpectedResult: false
    Log:
      {
        "device_id": "abc123def456789",
        "plist_file": "   "
      }
  - Name: Invalid - Missing plist_file field
    ExpectedResult: false
    Log:
      {
        "device_id": "abc123def456789"
      }
  - Name: Invalid - Unknown placeholder value from query
    ExpectedResult: false
    Log:
      {
        "device_id": "abc123def456789",
        "plist_file": "<UNKNOWN_FILE>"
      }
  - Name: Invalid - Null plist_file value
    ExpectedResult: false
    Log:
      {
        "device_id": "jkl012mno345678",
        "plist_file": null
      }
  - Name: Invalid - Empty result object
    ExpectedResult: false
    Log: {}


# ------ paired body: crowdstrike_macos_plutil_newfiles.py ------

def rule(event):
    # The scheduled query returns only new plist files that haven't been modified before
    # Add additional filtering here if needed
    plist_file = event.get("plist_file", "")

    # Filter out empty, malformed, or invalid results
    if not plist_file:
        return False

    # Filter out whitespace-only strings
    if isinstance(plist_file, str) and not plist_file.strip():
        return False

    # Filter out placeholder/error values from query
    if plist_file == "<UNKNOWN_FILE>":
        return False

    # Any valid result from the query should trigger an alert
    return True


def dedup(event):
    # Group alerts by device and plist file
    device_id = event.get("device_id", "<UNKNOWN_DEVICE>")
    plist_file = event.get("plist_file", "<UNKNOWN_FILE>")
    return f"{device_id}:{plist_file}"


def title(event):
    plist_file = event.get("plist_file", "<UNKNOWN_FILE>")
    return f"Crowdstrike: plutil modified new plist file: {plist_file}"


def severity(event):
    plist_file = event.get("plist_file", "")

    # Critical: System-level persistence with elevated privileges
    if any(
        loc in plist_file for loc in ["/System/Library/LaunchDaemons/", "/Library/LaunchDaemons/"]
    ):
        return "HIGH"

    # High: System-level LaunchAgents or system directory modifications
    if "/Library/LaunchAgents/" in plist_file or "/System/" in plist_file:
        return "HIGH"

    # Default: Medium for all other novel modifications
    return "DEFAULT"


def alert_context(event):
    plist_file = event.get("plist_file", "<UNKNOWN_FILE>")

    # Determine risk indicators based on plist location
    risk_indicators = []
    if any(loc in plist_file for loc in ["/Library/LaunchAgents/", "/Library/LaunchDaemons/"]):
        risk_indicators.append("System-level persistence location")
    elif any(loc in plist_file for loc in ["LaunchAgents/", "LaunchDaemons/"]):
        risk_indicators.append("User-level persistence location")
    elif "/Applications/" in plist_file:
        risk_indicators.append("Application bundle modification (potential tampering)")
    elif "/System/" in plist_file:
        risk_indicators.append("System directory modification (elevated privileges)")

    return {
        "device_id": event.get("device_id", "<UNKNOWN_DEVICE>"),
        "plist_file": plist_file,
        "detection_type": "Anomaly - New Modification Detected",
        "baseline_period": "30 days",
        "risk_indicators": risk_indicators if risk_indicators else ["Non-standard plist location"],
    }

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.