Active Directory Shadow Credentials: msDS-KeyCredentialLink Abuse

Active Directory Shadow Credentials: msDS-KeyCredentialLink Abuse

Overview & Threat Landscape#

In enterprise Windows environments, Active Directory Domain Services (AD DS) serves as the centralized identity provider and security boundary. To transition enterprise fleets toward modern, passwordless authentication models, Microsoft introduced Windows Hello for Business (WHfB) and FIDO2 integration starting with Windows Server 2016. To facilitate passwordless authentication without requiring extensive schema overhauls or deploying third-party public key infrastructure (PKI), Microsoft introduced the msDS-KeyCredentialLink attribute on Active Directory user and computer objects.

This attribute allows accounts to register raw public keys directly within the directory. When a user authenticates via Windows Hello or a security key, the Domain Controller's Key Distribution Center (KDC) verifies the cryptographic assertion against the public key stored in msDS-KeyCredentialLink using Kerberos PKINIT (RFC 4556), issuing a valid Ticket Granting Ticket (TGT) without requiring a traditional password.

However, this architecture introduces a profound privilege escalation and persistence mechanism known as Shadow Credentials. If an adversary achieves write access over an Active Directory object's Access Control List (DACL)—via permissions such as GenericAll, GenericWrite, WriteOwner, WriteDacl, or explicit WritePropertyonmsDS-KeyCredentialLink—the attacker can craft a synthetic public key credential and append it to the target's attribute via LDAP. The attacker then initiates a standard Kerberos PKINIT exchange using their locally held private key. The KDC validates the key, assumes the identity belongs to the target principal, and issues a fully functional Kerberos TGT.

[!WARNING] Shadow Credentials fundamentally alters Active Directory offensive operations. Unlike traditional password resets (which alert the victim, sever active sessions, and trigger Security Event ID 4724), injecting an alternative public key leaves the victim's existing password completely intact. Furthermore, endpoint detection and response (EDR) sensors on workstations remain completely blind, as the entire attack is conducted remotely over LDAP (TCP 389/636) and Kerberos (TCP/UDP 88) directly against the Domain Controller.


Vulnerability & Attack Root-Cause Analysis#

To deconstruct the root cause of Shadow Credentials, we must inspect the binary serialization format of the credential link attribute and the authentication logic implemented by the KDC.

In Active Directory, msDS-KeyCredentialLink is defined as a DNwithBinary attribute syntax. The attribute stores an ASN.1 Distinguished Name accompanied by a binary payload prefixed with the byte length:

PLAINTEXT
B:<length>:<binary_data>:<distinguished_name>

The underlying binary payload is structured as a KEYCREDENTIALLINK_BLOB. According to Microsoft Technical Specification [MS-ADTS] Section 2.2.20, this binary blob is structured as an array of length-value structures:

C
// Conceptual C Representation of KEYCREDENTIALLINK_BLOB structure
typedef struct _KEYCREDENTIALLINK_BLOB {
    DWORD dwVersion;       // Must be 0x00000100 (Version 1.0)
    DWORD dwEntryCount;    // Number of KEYCREDENTIALLINK_ENTRY structures
    // Followed by dwEntryCount serialized KEYCREDENTIALLINK_ENTRY items
} KEYCREDENTIALLINK_BLOB;

typedef struct _KEYCREDENTIALLINK_ENTRY {
    WORD wLength;          // Length of the value data
    BYTE bIdentifier;      // Type identifier of the entry
    BYTE bData[wLength];   // Raw entry payload
} KEYCREDENTIALLINK_ENTRY;

EachKEYCREDENTIALLINK_ENTRY holds specific metadata required to bind the public key to the account. The critical entry identifiers include:

Identifier Symbolic Name Size Purpose & Technical Description
0x01 KeyID 32 Bytes SHA-256 hash of the raw public key material used as a lookup index by the KDC.
0x02 KeyMaterial Variable Raw public key encoded as a Microsoft Cryptography Next Generation (CNG) BCRYPT_RSAKEY_BLOBorBCRYPT_ECCKEY_BLOB.
0x03 KeyUsage 1 Byte Bitmask defining key purpose: 0x01 specifies NGC (Next Generation Credential / WHfB); 0x02 specifies FIDO.
0x04 KeySource 1 Byte Credential origin: 0x00 specifies Active Directory DS; 0x01 specifies Azure AD / Entra ID.
0x05 DeviceId 16 Bytes Unique device identifier represented as a binary GUID.
0x06 CustomKeyInformation Variable Optional flags controlling key enforcement and attestation checks.

When Active Directory processes an LDAP ModifyRequest to write to msDS-KeyCredentialLink, the domain schema validates only that the caller holds write permissions to the attribute and that the binary blob parses according to the specification. Crucially, the directory does not perform hardware attestation, enrollment authorization, or require administrative approval before accepting the key.

Kerberos PKINIT Authentication Workflow (RFC 4556)#

Once the rogue public key is committed to the directory, the attacker invokes Kerberos Public Key Cryptography for Initial Authentication (PKINIT per RFC 4556 and [MS-PKCA]):

  1. Client AS-REQ Generation: The attacker generates a Kerberos AS-REQ targeting the KDC. The request includes the PA-PK-AS-REQ pre-authentication data structure (pre-auth type 16or17).
  2. AuthPack Signing: Inside PA-PK-AS-REQ, the attacker includes a signed AuthPack structure containing a CMS (Cryptographic Message Syntax) signature generated with their local private key.
  3. KDC Verification: The KDC retrieves the target user or computer object from ntds.dit. Instead of querying an enterprise Certificate Authority (CA) revocation list, the KDC inspects msDS-KeyCredentialLink. It matches the KeyID from the AuthPack, retrieves the KeyMaterial CNG blob, and cryptographically verifies the signature on the AuthPack.
  4. TGT Issuance with PAC: Upon successful signature validation, the KDC constructs an authentic Kerberos Ticket Granting Ticket (TGT). The KDC generates a Privilege Attribute Certificate (PAC) enumerating the target's security groups (including Domain Admins, Enterprise Admins, or Schema Admins), signs the PAC using the domain krbtgt key, and transmits the AS-REP containing the encrypted TGT back to the caller.

Exploit Architecture & Protocol Flow#

The following sequence diagram illustrates the entire Shadow Credentials exploitation lifecycle, from DACL abuse to full Kerberos authentication and subsequent NTLM credential recovery via PKINIT:

sequenceDiagram
    autonumber
    participant Attacker as Unprivileged Domain Foothold
    participant LDAP as Domain Controller (LDAP Port 389/636)
    participant KDC as Domain Controller (KDC Port 88)
    participant Target as Target Account (e.g. VIP / Domain Admin)

    Note over Attacker: Step 1: Discover DACL Write Permission on Target
    Attacker->>LDAP: Query object DACL (GenericWrite on Target)
    LDAP-->>Attacker: Confirm write access to msDS-KeyCredentialLink

    Note over Attacker: Step 2: Generate Asymmetric Keypair & KEYCREDENTIALLINK_BLOB
    Attacker->>Attacker: Generate local RSA-2048 private/public keypair
    Attacker->>Attacker: Compute KeyID SHA-256 and assemble BCRYPT_RSAKEY_BLOB

    Note over Attacker: Step 3: Inject Shadow Credential via LDAP
    Attacker->>LDAP: LDAP ModifyRequest(ADD, msDS-KeyCredentialLink, BinaryBlob)
    LDAP->>Target: Commit KEYCREDENTIALLINK_BLOB to account in NTDS.dit
    LDAP-->>Attacker: LDAP ModifyResponse(SUCCESS)

    Note over Attacker: Step 4: Execute Kerberos PKINIT Pre-Authentication
    Attacker->>KDC: Kerberos AS-REQ with PA-PK-AS-REQ (Signed by Private Key)
    KDC->>Target: Retrieve msDS-KeyCredentialLink from NTDS.dit
    KDC->>KDC: Verify signature using injected public KeyMaterial
    KDC-->>Attacker: Kerberos AS-REP containing TGT and signed PAC

    Note over Attacker: Step 5: UnPAC-the-Hash / TGS Service Impersonation
    Attacker->>KDC: Kerberos TGS-REQ (U2U) with PAC_CREDENTIAL_INFO
    KDC-->>Attacker: TGS-REP containing decrypted NTLM Hash of Target

    Note over Attacker: Step 6: Covert Cleanup
    Attacker->>LDAP: LDAP ModifyRequest(DELETE, msDS-KeyCredentialLink)
    LDAP-->>Attacker: LDAP ModifyResponse(SUCCESS)

Attack Path Step-by-Step#

To evaluate the operational mechanics of Shadow Credentials, we examine how an adversary transitions from discovering delegated rights to achieving domain takeover.

Step 1: Identifying Misconfigured Object DACLs#

Active Directory environments frequently accumulate over-privileged ACLs due to delegated administration, legacy tiering migration, or automation service accounts. An attacker enumerates objects where their low-privileged user or group holds modifying authority:

# Active Directory PowerShell: Inspecting DACL for msDS-KeyCredentialLink write rights
$TargetUser = "CN=da-svc-backup,OU=AdminAccounts,DC=corp,DC=internal"
$ACL = Get-Acl "AD:$TargetUser"

## Filter access rules for GenericAll, WriteDacl, or WriteProperty targeting KeyCredentialLink
$ACL.Access | Where-Object {
    $_.IdentityReference -match "Domain Users|Tier2-Admins" -and (
        $_.ActiveDirectoryRights -match "GenericAll|GenericWrite|WriteDacl" -or
        $_.ObjectType -eq "5b47d60f-6090-40b2-9f37-2a4de45f4263" # Schema GUID for msDS-KeyCredentialLink
    )
} | Select-Object IdentityReference, ActiveDirectoryRights, AccessControlType

Step 2: Binary Blob Assembly & Key Injection#

The attacker generates a fresh RSA-2048 or NIST P-256 ECC key pair locally. The public key is formatted into the CNG BCRYPT_RSAKEY_BLOB specification, hashed with SHA-256 to derive the KeyID, and serialized into the binary blob:

# Conceptual structure of the injected LDAP change record (LDIF)
dn: CN=da-svc-backup,OU=AdminAccounts,DC=corp,DC=internal
changetype: modify
add: msDS-KeyCredentialLink
msDS-KeyCredentialLink: B:192:0100000000000000...RAW_HEX_KEYCREDENTIALLINK_BLOB...:CN=da-svc-backup,OU=AdminAccounts,DC=corp,DC=internal
-

When committed via standard LDAP or LDAPS, the Domain Controller appends the key to the target's multivalue attribute without validating if the client machine is an enrolled Windows Hello device.

Step 3: Requesting Kerberos TGT via PKINIT#

With the shadow credential active in ntds.dit, the attacker initiates an RFC 4556 AS-REQ to the KDC, specifying the target account as the client principal and signing the PA-PK-AS-REQ with the private key:

# Requesting TGT via Kerberos PKINIT using the shadow certificate/keypair
python3 gettgtpkinit.py \
  -cert-pfx /tmp/shadow_cred.pfx \
  -pfx-pass "ShadowPassword123!" \
  corp.internal/da-svc-backup \
  /tmp/da_backup.ccache

The KDC reads the public key directly from msDS-KeyCredentialLink, validates the CMS signature, generates a full TGT, and bundles the PAC representing the victim's group memberships.

Step 4: UnPAC-the-Hash (NTLM Credential Recovery)#

A critical byproduct of Kerberos PKINIT in Windows Active Directory is the PAC_CREDENTIAL_DATA structure. When an account authenticates using certificates, the KDC includes the user's NTLM hash encrypted under the Kerberos session key inside the PAC to facilitate legacy NTLM authentication to older network services.

By conducting a User-to-User (U2U) TGS request using the newly issued TGT, the attacker decrypts the PAC credential info and extracts the victim's static NTLM hash:

# Extracting the target's NTLM hash via Kerberos PKINIT U2U exchange
python3 unpac-hash.py \
  -kdc dc01.corp.internal \
  corp.internal/da-svc-backup \
  /tmp/da_backup.ccache

Extracted Output:

PLAINTEXT
[*] Successfully decrypted PAC_CREDENTIAL_INFO
[*] Account: da-svc-backup
[*] NTLM Hash: aad3b435b51404eeaad3b435b51404ee:32ed87b7a3129209cbc5e0b7ceacd135

With the NTLM hash recovered, the attacker can execute Pass-the-Hash (PtH) attacks, authenticate across SMB/WMI, or pivot laterally into administrative systems without leaving persistent Kerberos artifacts.

Step 5: Surgical Trace Removal#

Because Shadow Credentials operate as an additive property, an attacker who extracts the TGT or NTLM hash can instantly delete their injected entry from msDS-KeyCredentialLink via LDAP. The issued TGT remains valid for its entire 10-hour lifetime, leaving no trace of the rogue credential upon inspection.


Fast Cyber Defense Morning Takeaways#

The Shadow Credentials attack demonstrates why perimeter defenses and traditional credential rotation policies fail to guarantee Active Directory integrity. When directory attributes can be manipulated directly via LDAP, authentication trust boundaries collapse from within.

  1. DACL Control Equals Complete Identity Control: In Active Directory, object-level permissions (GenericAll, GenericWrite, WriteOwner, WriteDacl, WriteProperty) must be treated with the same severity as full credential theft. Any principal capable of modifying msDS-KeyCredentialLink can impersonate the target at will.
  2. Password Changes Provide False Assurance: Organizations that enforce 90-day password rotation or perform emergency password resets during incident response will not revoke an attacker's access if a shadow credential remains planted on the account.
  3. Silent Lateral Movement: Shadow Credentials do not generate Event ID 4724 (Password Reset Attempt) or Event ID 4738 (User Account Modified with Password Last Set changes). Detection requires inspecting Directory Service Changes (Event ID 5136) and anomalous PKINIT Kerberos Ticket Requests (Event ID 4768 with pre-auth type 16/17).
  4. Bridge to Evening Defense Guide: In tonight's Edition 2 post, we will publish the complete blue team engineering guide for defeating Shadow Credentials, including:
    • Deploying SACLs on msDS-KeyCredentialLink to generate high-fidelity Event ID 5136 telemetry.
    • Implementing validated Sigma rules to detect unauthorized LDAP modifications and non-WHfB PKINIT authentications.
    • PowerShell automation scripts to audit and purge rogue KEYCREDENTIALLINK_BLOB entries across all user and computer objects.
    • Hardening Active Directory Tier-0 delegation models to eradicate accidental write permissions.

Authoritative Technical References#

Comments