← Back to writeups Persistence

Persistence by Privilege Level

What attackers can do at each access level - and where to hunt

// What is this?
The different ways malware ensures it restarts after a reboot — from obvious to nearly invisible.

When you restart, running programs stop. Persistence is how malware adds itself to the startup sequence. Options range from obvious (registry Run keys, startup folder) to stealthy (WMI subscriptions, service binary hijacking, SetupComplete.cmd). Each level requires different privileges. Higher-privilege persistence is harder to detect and remove but requires the attacker to already have elevated access.

PowerShell — Persistence Detection
# Registry Run keys
Get-ItemProperty "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue

# Scheduled tasks from user paths
schtasks /query /fo CSV /v 2>$null | ConvertFrom-Csv | Where-Object {
  $_."Task To Run" -match "Users|AppData|Temp|ProgramData"
} | Select-Object "TaskName", "Task To Run"

# WMI event subscriptions (APT-level)
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding
If this flags: Run keys should only contain legitimate software. SYSTEM tasks from user folders are hijackable. Any WMI subscription is advanced persistence.

Persistence mechanisms vary by privilege level. Understanding what's available at each level helps defenders prioritize hunting and attackers understand why escalation matters.

User Level (No Admin)

Limitation: Easy to find and remove by any admin. Doesn't survive password changes or profile deletions.

Admin Level

The smart move: Escalate to SYSTEM before installing persistence. SYSTEM-level persistence survives admin password changes.

SYSTEM Level

Why it matters: SYSTEM persistence is invisible to standard admin tools. Survives credential rotations. Requires specialized forensics to detect.

SetupComplete.cmd — Reset-Resistant Persistence

Windows runs C:\Windows\Setup\Scripts\SetupComplete.cmd automatically after any Windows setup completion, OEM customisation, or system reset (Windows Recovery). Writing persistence here survives a user-initiated "Reset this PC" — it executes before the new user session is created.

Requires admin or SYSTEM. Source: shellchain-phoenix C2 framework dual-persistence module (sanitized).

# PowerShell — create SetupComplete.cmd (Admin/SYSTEM required)
# Source: shellchain-phoenix/payload/stage_phoenix.ps1 (SANITIZE — operational paths removed)

$setupScripts = 'C:\Windows\Setup\Scripts'
New-Item -ItemType Directory -Force -Path $setupScripts | Out-Null

$cmdContent = @"
@echo off
copy "[PAYLOAD_PATH]" "C:\Users\Public\[PAYLOAD].vbs" /Y
icacls "C:\Users\Public\[PAYLOAD].vbs" /grant Everyone:F
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v [NAME] /t REG_SZ ^
    /d "wscript //nologo C:\Users\Public\[PAYLOAD].vbs" /f /reg:64
"@
Set-Content -Path "$setupScripts\SetupComplete.cmd" -Value $cmdContent -Encoding ASCII

Detection

Monitor C:\Windows\Setup\Scripts\ for new or modified files — legitimate post-setup scripts are uncommon outside enterprise provisioning. MITRE T1037.001. Alert on any write to SetupComplete.cmd outside of OS deployment tooling.

The Escalation Logic

Sophisticated attackers follow a pattern:

  1. Get initial access (usually user level)
  2. Install lightweight user persistence (backup)
  3. Escalate to admin, then SYSTEM
  4. Install SYSTEM-level persistence (primary)
  5. Remove or keep user-level as fallback

Even if defenders find and remove the user-level persistence, the SYSTEM-level persistence remains undetected.

Detection Strategy by Level

User level: Standard autoruns tools catch most. Check Sysinternals Autoruns, review HKCU Run keys, scheduled tasks.

Admin level: Services, AppInit_DLLs, HKLM keys. Requires admin access to review. Compare against known-good baselines.

SYSTEM level: Requires memory forensics, driver analysis, EDR with kernel visibility. Look for unsigned drivers, modified system binaries, unusual SSPs.

Cross-Platform Persistence

Persistence isn't Windows-only. Attackers target macOS and Linux using native scheduling and service management. Understanding these helps defenders protect multi-OS environments.

Linux Persistence Mechanisms

Detection: Compare crontab -l and systemctl list-unit-files against known baselines. Monitor /etc/cron.* directories for new files.

macOS Persistence Mechanisms

Detection: Review launchctl list output. Monitor plist creation in LaunchAgents/Daemons directories. Check ~/Library/Application Support/ for unexpected scripts.

Bash — Cross-Platform Persistence Audit
# Linux — cron and systemd user services
crontab -l 2>/dev/null
ls -la ~/.config/systemd/user/*.service 2>/dev/null
systemctl --user list-unit-files --state=enabled

# Linux — system-level (root required)
cat /etc/crontab
ls -la /etc/cron.d/
systemctl list-unit-files --state=enabled | grep -v "@"

# macOS — LaunchAgents and Daemons
launchctl list | grep -v "com.apple"
ls -la ~/Library/LaunchAgents/
ls -la /Library/LaunchAgents/ /Library/LaunchDaemons/
If this flags: User cron jobs and LaunchAgents are legitimate but should be verified. System-level entries not matching installed software are suspicious. Any plist or service file created recently warrants investigation.

The Persistence Lifecycle

Source: 22nd Survey Division persistence research (concept flow — operational details removed).

# CONCEPT: Cross-platform persistence decision logic (PSEUDOCODE)
# Real implants check privilege level and choose appropriate mechanism

def install_persistence(privilege_level, platform):
    if platform == "windows":
        if privilege_level == "user":
            # HKCU\...\Run or Startup folder
            add_registry_run(HKCU, "[PAYLOAD_PATH]")
        elif privilege_level == "admin":
            # Scheduled task or HKLM Run
            create_schtask("[TASK_NAME]", "[PAYLOAD_PATH]")
        elif privilege_level == "system":
            # Service or SetupComplete.cmd
            create_service("[SVC_NAME]", "[PAYLOAD_PATH]")

    elif platform == "linux":
        if privilege_level == "user":
            # User cron or systemd user service
            add_cron_entry("@reboot [PAYLOAD_PATH]")
        elif privilege_level == "root":
            # System cron or systemd service
            create_systemd_service("[SVC_NAME]")

    elif platform == "darwin":  # macOS
        if privilege_level == "user":
            # ~/Library/LaunchAgents/*.plist
            create_launch_agent("[LABEL]", "[PAYLOAD_PATH]")
        elif privilege_level == "root":
            # /Library/LaunchDaemons/*.plist
            create_launch_daemon("[LABEL]", "[PAYLOAD_PATH]")

Why this matters: Defenders should check all three categories on a compromised host. An attacker with root/SYSTEM will install persistence at EVERY level they have access to — user-level as a fallback, system-level as primary.

Hunting Priorities

When investigating a compromised system:

MITRE ATT&CK: T1547 (Boot or Logon Autostart Execution), T1053 (Scheduled Task/Job), T1543.002 (Systemd Service), T1543.001 (Launch Agent/Daemon)