Nullpath

Pass the Key (PtK)

concept kerberosactive-directorycredential-accesslateral-movement updated 07 Sept 2026 · 3 min

Pass the Key (PtK)

Authenticate to Kerberos using an account’s decrypted key (the AES128, AES256, or RC4/NT key derived from the password) instead of a pre-built ticket. The key is used directly to compute the encrypted AS-REQ/TGS-REQ pre-authentication data, so the KDC issues a legitimate ticket that is then used normally. It sits between PtH (NTLM hash, no Kerberos) and PtT (replaying a captured ticket): you hold the raw key, not a ticket.

How it differs from PtH / PtT

MethodWhat you holdWhat the DC seesTicket
PtHNTLM hashNTLM challenge-response (or hash converted to key)none / NTLM
PtKAES/RC4 keya normal Kerberos AS-REQ with valid encrypted pre-authfresh, legitimate TGT
PtTcaptured TGT/TGSa replayed ticketstolen ticket

Because PtK produces a fresh ticket minted by the KDC, it leaves a normal 4768 event (no “forged ticket” lifetime anomalies), which is its main OPSEC advantage over golden-silver-tickets-style offline forgery. Its main disadvantage: you need the correct enctype key, and the target account must still be Kerberos-authenticating (not locked, etc.).

When you have the key

  • dcsyncsecretsdump.py/Mimikatz output gives you aes128-cts-hmac-sha1-96, aes256-cts-hmac-sha1-96, and rc4_hmac_nt for each account.
  • pkinit-unpac-the-hash / shadow-credentials — recovering an NT hash lets you derive all three keys (mkv5-style key schedule).
  • LSASS dump (lsass) — sekurlsa::logonpasswords shows Key (AES) and NTLM values.

Commands

# Rubeus — ask the KDC for a TGT using the raw key, inject it
Rubeus.exe asktgt /user:svc_sql /aes256:<64-hex> /ptt
Rubeus.exe asktgt /user:svc_sql /aes128:<32-hex> /ptt
Rubeus.exe asktgt /user:svc_sql /rc4:<ntlm-hash> /ptt
# then confirm
Rubeus.exe triage
whoami /all
# Impacket — getTGT.py takes a key and outputs a ccache you can use with -k
getTGT.py -spn cifs -dc-ip <dc-ip> DOMAIN/user:aes256:<64-hex>
# use it with any kerb-auth Impacket tool:
psexec.py -k -no-pass DOMAIN/user@<dc-ip>   # after exporting KRB5CCNAME
# Mimikatz — sekurlsa can build from a key (or use kerberos::golden offline)
privilege::debug
sekurlsa::ptt /id:<logon-id>        # if you dumped LSASS locally

Detection

  • Event 4768 (TGT requested) with the expected service ticket follows — the ticket is valid, so the tell is context: a service account authenticating from a workstation IP, or a burst of TGTs for one account.
  • The ticket options / encryption type in 4768 — if you forced RC4 on an AES domain, that’s a kerberos-encryption-types downgrade signal.
  • No 4624 Type 3 anomaly from the ticket itself (unlike golden-silver-tickets where a TGT outlives policy). Detection usually comes from where/when a normally service-bound account logs on.

Mitigations

  • gmsa — rotating, auto-managed keys so a captured key is short-lived.
  • ad-tiering-and-hardening — limit what a service-account key can reach.
  • Monitor 4768 for accounts that rarely (never) request TGTs from user workstations.

References