Persistence Hunter Playbook

Externalized detailed command sequences from the agent definition per the few-shot-examples rule (#1

Persistence Hunter — Detection Playbook

Externalized detailed command sequences from the agent definition per the few-shot-examples rule (#1587, #1600). The agent references this on demand; it is not loaded into the system-prompt budget.

The agent definition keeps the enumerated detection targets, MITRE ATT&CK mappings, and escalation gates inline. This file carries the exact command sequences per OS/runtime.


Linux Persistence

1. Cron Persistence (T1053.003)

# System-wide crontabs
cat /etc/crontab
ls -la /etc/cron.d/
cat /etc/cron.d/*

# Hourly, daily, weekly, monthly
ls -la /etc/cron.hourly/ /etc/cron.daily/ /etc/cron.weekly/ /etc/cron.monthly/
cat /etc/cron.hourly/* /etc/cron.daily/* /etc/cron.weekly/* /etc/cron.monthly/* 2>/dev/null

# Per-user crontabs
ls -la /var/spool/cron/crontabs/ 2>/dev/null
for user in $(cut -d: -f1 /etc/passwd); do
  crontab -l -u "$user" 2>/dev/null && echo "--- $user ---"
done

# Recently modified cron files (high-value: modified during suspected intrusion window)
find /etc/cron* /var/spool/cron -newer /etc/passwd -ls 2>/dev/null

Flag any cron entry that contains: `curl`, `wget`, `bash -c`, `python`, `perl`, `nc`, `ncat`, encoded content (base64), or references to /tmp, /dev/shm, or /var/tmp.

2. Systemd Persistence (T1543.002)

# All unit files — look for recently created or modified ones
find /etc/systemd/system/ /lib/systemd/system/ /usr/lib/systemd/system/ \
  -name "*.service" -o -name "*.timer" -o -name "*.socket" | \
  xargs ls -la 2>/dev/null | sort -k6,7

# Units created recently (modify cutoff to match incident window)
find /etc/systemd/system/ -newer /etc/hostname -ls 2>/dev/null

# Enabled units — these survive reboot
systemctl list-unit-files --state=enabled 2>/dev/null

# Contents of suspicious units
# cat /etc/systemd/system/<suspicious>.service

# Timers (scheduled execution — like cron via systemd)
systemctl list-timers --all 2>/dev/null
find /etc/systemd/system/ -name "*.timer" -ls 2>/dev/null

# User-level systemd units (per-user persistence)
find /home /root -path "*.config/systemd/user*" -name "*.service" -ls 2>/dev/null

A legitimate service unit has a known package origin. An attacker unit often has a plausible name (`update-service.service`, `cachemanager.service`) but executes from an unusual path or runs a shell command.

3. SSH Key Persistence (T1098.004)

# All authorized_keys files across the system
find / -name "authorized_keys" -ls 2>/dev/null

# Contents of every authorized_keys file
find / -name "authorized_keys" -readable 2>/dev/null | while read f; do
  echo "=== $f ==="
  cat "$f"
  echo ""
done

# Recently modified authorized_keys files
find / -name "authorized_keys" -newer /etc/passwd -ls 2>/dev/null

# SSH daemon configuration — check for unexpected directives
cat /etc/ssh/sshd_config | grep -v "^#" | grep -v "^$"

# AuthorizedKeysFile directive — attacker may change this to a controlled path
grep "AuthorizedKeysFile" /etc/ssh/sshd_config

# Root's authorized_keys specifically
cat /root/.ssh/authorized_keys 2>/dev/null

# SSH known_hosts — who has this system connected to
cat /root/.ssh/known_hosts 2>/dev/null
find /home -name "known_hosts" -readable 2>/dev/null | xargs cat

Any key that does not match the system owner's known public keys is suspicious. Document the full key, its from= restriction (if any), and the key comment field — comments sometimes include attacker hostnames or email addresses.

4. LD_PRELOAD / Library Injection (T1574.006)

# /etc/ld.so.preload — global LD_PRELOAD for all processes
cat /etc/ld.so.preload 2>/dev/null && echo "WARNING: ld.so.preload exists"

# LD_PRELOAD in process environments
grep -l LD_PRELOAD /proc/*/environ 2>/dev/null | while read f; do
  pid=$(echo "$f" | grep -oP '\d+')
  echo "PID $pid: $(cat "$f" 2>/dev/null | tr '\0' '\
' | grep LD_PRELOAD)"
done

# Unexpected files in library directories
find /lib /lib64 /usr/lib /usr/lib64 -name "*.so*" -newer /etc/passwd -ls 2>/dev/null

# Libraries not associated with any package (unregistered shared objects)
find /lib /usr/lib -name "*.so*" | while read f; do
  dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || echo "UNPACKAGED: $f"
done 2>/dev/null | grep UNPACKAGED

Any entry in `/etc/ld.so.preload` is a critical finding. Legitimate systems rarely use this file. The presence of an unpackaged shared object in a library directory is a strong rootkit indicator.

5. PAM Module Tampering (T1556.003)

# PAM configuration files
ls -la /etc/pam.d/
cat /etc/pam.d/common-auth 2>/dev/null
cat /etc/pam.d/sshd 2>/dev/null
cat /etc/pam.d/sudo 2>/dev/null

# Recently modified PAM config
find /etc/pam.d/ -newer /etc/hostname -ls 2>/dev/null

# Installed PAM modules
find /lib/security/ /lib64/security/ /usr/lib/security/ -name "*.so" -ls 2>/dev/null

# Recently modified PAM modules
find /lib/security/ /lib64/security/ /usr/lib/security/ -newer /etc/passwd -ls 2>/dev/null

# PAM modules not associated with any package
find /lib/security/ /lib64/security/ /usr/lib/security/ -name "*.so" | while read f; do
  dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || echo "UNPACKAGED: $f"
done 2>/dev/null | grep UNPACKAGED

# Check for pam_exec (executes external commands on auth events)
grep -r "pam_exec" /etc/pam.d/ 2>/dev/null

An unpackaged PAM module is a critical finding. A `pam_exec` directive executing a script is a critical finding. Either warrants immediate escalation.

6. Kernel Module Persistence (T1547.006)

# Currently loaded modules
lsmod

# Modules auto-loaded at boot
cat /etc/modules 2>/dev/null
ls -la /etc/modules-load.d/
cat /etc/modules-load.d/*.conf 2>/dev/null

# Module files on disk — recently modified
find /lib/modules -name "*.ko" -newer /etc/passwd -ls 2>/dev/null

# Module files not associated with kernel package
# (attacker modules are typically not registered)
find /lib/modules -name "*.ko" | while read f; do
  dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || echo "UNPACKAGED: $f"
done 2>/dev/null | grep UNPACKAGED

# Modprobe blacklisting of security modules (attacker may blacklist audit or IDS modules)
cat /etc/modprobe.d/*.conf 2>/dev/null | grep -i blacklist

An unpackaged `.ko` file is a critical finding. A blacklisted security module (e.g., `auditd`, `apparmor`) is a persistence enabler that warrants immediate investigation.

7. Login Script Injection (T1546.004)

# System-wide login scripts
cat /etc/profile
ls -la /etc/profile.d/
cat /etc/profile.d/*.sh 2>/dev/null
cat /etc/bash.bashrc 2>/dev/null
cat /etc/environment

# Root's personal login scripts
cat /root/.bashrc 2>/dev/null
cat /root/.bash_profile 2>/dev/null
cat /root/.profile 2>/dev/null
cat /root/.bash_logout 2>/dev/null

# All users' login scripts
for home in $(awk -F: '$6 ~ /home|root/ {print $6}' /etc/passwd); do
  for script in .bashrc .bash_profile .profile .zshrc .zprofile .bash_logout; do
    [ -f "$home/$script" ] && echo "=== $home/$script ===" && cat "$home/$script"
  done
done

# Recently modified login scripts
find /etc/profile.d /home /root -name ".*rc" -o -name ".profile" -o \
  -name ".bash_*" -newer /etc/passwd 2>/dev/null | xargs ls -la 2>/dev/null

Flag any login script that contains: outbound network calls, base64 encoded commands, references to /tmp or /dev/shm, or additions made during the suspected intrusion window.

8. SUID/SGID Binary Analysis (T1548.001)

# Find all SUID binaries
find / -xdev -perm -4000 -type f 2>/dev/null

# Find all SGID binaries
find / -xdev -perm -2000 -type f 2>/dev/null

# Cross-reference with package manager
dpkg -S /path/to/binary 2>/dev/null || echo "NOT FROM PACKAGE"
rpm -qf /path/to/binary 2>/dev/null || echo "NOT FROM PACKAGE"

Red flags: recently modified SUID binaries, SUID binaries not owned by any package, SUID shells (e.g., `/bin/bash` with SUID set), and SUID copies of standard tools in unexpected locations (e.g., `/tmp/nmap` with SUID). Any SUID binary in `/tmp`, `/dev/shm`, or `/var/tmp` is a critical finding. ATT&CK: T1548.001 (Setuid and Setgid).


Windows Persistence

Windows provides numerous persistence locations spanning the registry, task scheduler, WMI, and service control manager. Attackers commonly layer multiple mechanisms — check all of them regardless of which one you find first.

Registry Run Keys (T1547.001)

# HKLM Run keys (all users, requires admin)
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices" -ErrorAction SilentlyContinue
Get-ItemProperty "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run"

# HKCU Run keys (current user context)
Get-ItemProperty "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
Get-ItemProperty "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"

Flag any entry pointing to `%TEMP%`, `%APPDATA%`, or `C:\Users\*\AppData\Local\Temp`. Flag encoded PowerShell invocations (`-enc`, `-EncodedCommand`).

Scheduled Tasks (T1053.005)

# List all scheduled tasks with details
schtasks /query /fo LIST /v 2>$null

# PowerShell — filter for non-Microsoft tasks
Get-ScheduledTask | Where-Object { $_.TaskPath -notlike "\Microsoft\*" } |
  Select-Object TaskName, TaskPath, State, @{n='Action';e={$_.Actions.Execute}}

# Show task XML for full detail (substitute task name)
Export-ScheduledTask -TaskName "<suspicious task>" | Out-String

Flag tasks with actions invoking `powershell.exe`, `cmd.exe`, `wscript.exe`, `cscript.exe`, or executables in user-writable paths. Flag tasks running as `SYSTEM` created by non-system accounts.

WMI Event Subscriptions (T1546.003)

# Event filters (the trigger condition)
Get-WMIObject -Namespace root\subscription -Class __EventFilter |
  Select-Object Name, Query, QueryLanguage

# Event consumers (the action taken)
Get-WMIObject -Namespace root\subscription -Class __EventConsumer

# Bindings (links filters to consumers)
Get-WMIObject -Namespace root\subscription -Class __FilterToConsumerBinding

Any event filter or consumer in `root\subscription` that is not from a known security or management product is a critical finding. WMI subscriptions survive reboots and are invisible to most AV tools.

Non-Microsoft Services (T1543.003)

# List all services with signature verification
Get-Service | Where-Object { $_.Status -eq "Running" } | ForEach-Object {
  $svc = $_
  $path = (Get-WmiObject Win32_Service | Where-Object { $_.Name -eq $svc.Name }).PathName
  $sig = Get-AuthenticodeSignature $path -ErrorAction SilentlyContinue
  [PSCustomObject]@{
    Name      = $svc.Name
    Status    = $svc.Status
    Path      = $path
    Publisher = $sig.SignerCertificate.Subject
    Valid     = $sig.Status
  }
} | Where-Object { $_.Publisher -notlike "*Microsoft*" -or $_.Valid -ne "Valid" }

Flag services with unsigned or invalidly signed binaries, services pointing to executables in user-writable directories, and services with generic or misspelled names mimicking legitimate services.

Startup Folder (T1547.001)

# Current user startup
explorer shell:startup

# All-users startup
explorer "shell:common startup"

# List contents programmatically
Get-ChildItem "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup"
Get-ChildItem "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup"

Any `.lnk`, `.bat`, `.vbs`, `.js`, or `.exe` in startup folders that is not part of a known installed application warrants investigation.

DLL Hijacking (T1574.001)

DLL hijacking exploits Windows DLL search order — the attacker places a malicious DLL earlier in the search path than the legitimate one.

# Use Process Monitor (Sysinternals) with these filters:
# Operation: Load Image
# Result: NAME NOT FOUND (DLL not found in earlier path locations)
# Path: ends with .dll

# Known-DLLs are immune — check which are registered
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs"

# Identify writable directories in system PATH
$env:PATH -split ";" | ForEach-Object {
  $acl = Get-Acl $_ -ErrorAction SilentlyContinue
  if ($acl) { [PSCustomObject]@{ Path=$_; ACL=$acl.AccessToString } }
}

Flag DLLs in user-writable directories that share names with system DLLs. Flag applications loading DLLs from `%TEMP%` or `%APPDATA%`.

COM Hijacking (T1546.015)

# HKCU COM registrations override HKLM — attackers register here without admin rights
Get-ChildItem "HKCU:\SOFTWARE\Classes\CLSID" |
  ForEach-Object {
    $clsid = $_.PSChildName
    $server = Get-ItemProperty "$($_.PSPath)\InprocServer32" -ErrorAction SilentlyContinue
    if ($server) {
      [PSCustomObject]@{ CLSID=$clsid; Server=$server."(default)" }
    }
  }

# Cross-reference against HKLM entries (HKCU overrides take precedence)
# Any CLSID in HKCU that also exists in HKLM is a hijack candidate

Flag HKCU `InprocServer32` entries pointing to non-system paths. Flag CLSIDs registered in HKCU that correspond to frequently loaded system COM objects.


macOS Persistence

macOS uses a layered launch system. LaunchAgents run as the user; LaunchDaemons run as root. Both survive reboots and are the primary persistence mechanism for macOS malware.

LaunchAgents (T1543.001)

# Per-user LaunchAgents (run as logged-in user)
ls -la ~/Library/LaunchAgents/
cat ~/Library/LaunchAgents/*.plist 2>/dev/null

# System-wide LaunchAgents (run for every user login)
ls -la /Library/LaunchAgents/
cat /Library/LaunchAgents/*.plist 2>/dev/null

# Recently modified agents
find ~/Library/LaunchAgents /Library/LaunchAgents -newer /etc/hosts -ls 2>/dev/null

Compare every `.plist` program path against known installed applications. Flag agents with `RunAtLoad` set to true and program arguments invoking shells, curl, or python. Flag plists with obfuscated or encoded command strings.

LaunchDaemons (T1543.004)

# System LaunchDaemons (run as root, no user session required)
ls -la /Library/LaunchDaemons/
cat /Library/LaunchDaemons/*.plist 2>/dev/null

# Compare against known Apple daemons (these are in /System/Library/LaunchDaemons/)
ls /System/Library/LaunchDaemons/ > /tmp/apple-daemons.txt
ls /Library/LaunchDaemons/ > /tmp/third-party-daemons.txt
diff /tmp/apple-daemons.txt /tmp/third-party-daemons.txt

# Recently modified daemons
find /Library/LaunchDaemons -newer /etc/hosts -ls 2>/dev/null

Daemons in `/System/Library/LaunchDaemons/` are Apple-owned (protected by SIP). Daemons in `/Library/LaunchDaemons/` are third-party. Any daemon in `/Library/LaunchDaemons/` with a non-vendor origin warrants investigation.

Login Items (T1547.015)

# Query login items via osascript
osascript -e 'tell application "System Events" to get the name of every login item'
osascript -e 'tell application "System Events" to get the path of every login item'

# Login items plist (pre-macOS 13)
cat ~/Library/Preferences/com.apple.loginitems.plist 2>/dev/null

# macOS 13+ Background Task Management
# Check System Settings > General > Login Items for GUI review

Any login item pointing to a path in `/tmp`, `~/Downloads`, or other user-writable locations outside of `/Applications` is suspicious. Cross-reference item paths against known installed software.

Authorization Plugins (T1547.002)

# Authorization plugins execute during authentication events — high-value target
ls -la /Library/Security/SecurityAgentPlugins/

# Review plugin bundles
find /Library/Security/SecurityAgentPlugins -name "*.bundle" -ls 2>/dev/null

# Verify against known Apple plugins (shipped in /System/Library/Security/SecurityAgentPlugins/)
ls /System/Library/Security/SecurityAgentPlugins/

Any plugin in `/Library/Security/SecurityAgentPlugins/` that is not from Apple or a known security vendor (e.g., endpoint agent) is a critical finding. These plugins have access to authentication credentials in cleartext.


Container Persistence

Container environments introduce unique persistence surfaces. Attackers target image layers, orchestrator configurations, and cluster-wide controllers that survive pod restarts and node replacements.

ENTRYPOINT Modification (T1525)

# Compare running container's ENTRYPOINT against the image manifest
docker inspect <container_id> --format '{{.Config.Entrypoint}}'
docker image inspect <image_name> --format '{{.Config.Entrypoint}}'

# Check for CMD overrides in running containers
docker inspect <container_id> --format '{{.Config.Cmd}}'

# Identify containers running unexpected commands
docker ps --no-trunc --format "table {{.Image}}\	{{.Command}}\	{{.Names}}"

Any discrepancy between a running container's command and its image definition indicates runtime injection or a modified image. Flag containers whose `CMD` or `ENTRYPOINT` includes curl, bash -c with encoded content, or references to external IPs.

Kubernetes DaemonSets

DaemonSets run a pod on every node — attackers use them to ensure persistence across all cluster nodes simultaneously.

# List all DaemonSets across all namespaces
kubectl get daemonsets --all-namespaces -o wide

# Inspect DaemonSet spec for unexpected images or commands
kubectl get daemonset <name> -n <namespace> -o yaml

# Compare against expected baseline (version control or CMDB)
kubectl get daemonsets --all-namespaces -o json | \
  jq '.items[] | {name: .metadata.name, namespace: .metadata.namespace, image: .spec.template.spec.containers[].image}'

Flag any DaemonSet in non-system namespaces (i.e., not `kube-system`) that was not deployed via your standard CI/CD pipeline. Flag DaemonSets using `hostPID: true`, `hostNetwork: true`, or privileged containers.

Kubernetes CronJobs (T1053.007)

# List all CronJobs across all namespaces
kubectl get cronjobs --all-namespaces -o wide

# Inspect schedule and command for each CronJob
kubectl get cronjob <name> -n <namespace> -o yaml

# Extract all schedules and images for review
kubectl get cronjobs --all-namespaces -o json | \
  jq '.items[] | {name: .metadata.name, namespace: .metadata.namespace, schedule: .spec.schedule, image: .spec.jobTemplate.spec.template.spec.containers[].image, command: .spec.jobTemplate.spec.template.spec.containers[].command}'

Flag CronJobs with frequent schedules (e.g., ` *`) that are not part of expected workloads. Flag jobs invoking shells with network callbacks or running privileged containers.

Init Containers

Init containers run before application containers start — attackers use them to stage payloads, modify the filesystem, or establish backdoors before the main container is monitored.

# List all pods with init containers across all namespaces
kubectl get pods --all-namespaces -o json | \
  jq '.items[] | select(.spec.initContainers != null) | {name: .metadata.name, namespace: .metadata.namespace, initContainers: [.spec.initContainers[] | {name: .name, image: .image, command: .command}]}'

# Inspect a specific pod's init containers
kubectl get pod <pod_name> -n <namespace> -o jsonpath='{.spec.initContainers}'

Flag init containers using images not from your organization's approved registry. Flag init containers that mount host paths (`hostPath` volumes) or modify files in shared volumes before the main container starts.