AD CS ESC9/ESC10: weak certificate mapping

Domain controllers still accept weak certificate mappings, so whoever can rename one account can obtain a certificate that signs in as another.

Category
Attack Path, or Hygiene when unverified.
Severity
At least High, or Medium.
Typical owner
PKI / AD CS administrators and the AD team (domain controller settings)
Data needed
AD CS objects, write access to enrolling accounts, and two registry values on every domain controller (StrongCertificateBindingEnforcement and CertificateMappingMethods). Reading those needs admin rights on the domain controllers: SharpHound run as a Domain Admin gets them, a plain collection account doesn't. Instead, the domain controllers' admins can run the read-only Get-PathwardenTierZeroSettings.ps1 on each and put its files beside the collection. Without the values the finding is unverified Medium hygiene, not an attack path.
MITRE ATT&CKT1649
On this page

In plain language

When you sign in with a certificate, the domain controller has to decide which account it belongs to. Older, weak mappings match on names such as the user principal name (UPN), which an account's managers can change.

If someone can edit one ordinary account, they can briefly rename it to look like a Domain Admin, request a certificate, rename it back, and sign in as the admin. Microsoft's KB5014754 changes enforce strong, SID-based mapping. Pathwarden checks the domain controllers' settings, and says so when it couldn't read them. These are ESC9 and ESC10.

What Pathwarden flags

Domain controllers still accept weak certificate mappings, and an authentication template lets the subject come from the enrolling account's own userPrincipalName (or dNSHostName). Accounts and groups outside Tier Zero can write that attribute on an account that can enroll. They can rename the account's UPN to match a privileged account, enroll, rename it back, and the resulting certificate maps to the privileged account at logon.

  • ESC9 (Kerberos): the template has the "no security extension" flag (CT_FLAG_NO_SECURITY_EXTENSION), so certificates carry no SID, and DCs accept weak mappings: StrongCertificateBindingEnforcement 0 or 1 (until the September 2025 update), unset (until the February 2025 update), or any value before the May 2022 update, when the KDC checked no mapping.
  • ESC10 (Schannel): DCs map client certificates by UPN for TLS logons (CertificateMappingMethods includes 0x4, or is unset on a DC without the May 2022 update, when the default was 0x1F).

How attackers use it

These are ESC9 and ESC10 from the follow-up research to SpecterOps' "Certified Pre-Owned". With write access over one ordinary enrolling account (often helpdesk delegation or GenericWrite on a user), tools such as Certipy automate the rename, enroll and restore steps and yield a certificate for a Domain Admin or the domain controller itself.

Find it with Pathwarden

Data needed: AD CS objects, write access to enrolling accounts, and two registry values on every domain controller (StrongCertificateBindingEnforcement and CertificateMappingMethods). Reading those needs admin rights on the domain controllers: SharpHound run as a Domain Admin gets them, a plain collection account doesn't. Instead, the domain controllers' admins can run the read-only Get-PathwardenTierZeroSettings.ps1 on each and put its files beside the collection. Without the values the finding is unverified Medium hygiene, not an attack path.

In the web UI or the HTML report, open the Findings tab and expand PW-020: each finding shows who can use it (its exposure), the attack path drawn out, the affected objects, and the fix. The remediation path tells you whether a single plan step closes it. On the command line:

Shell
# every finding of this rule, with its path and fix
pathwarden findings -i .\collect --rule PW-020
# the step-by-step guide on this page, offline
pathwarden guide PW-020
# after the fix: collect again, then check it is gone
pathwarden retest -i .\collect-after --rule PW-020

Exit codes make retest easy to script: 0 Fixed, 1 Still open, 3 Can't tell. See findings lifecycle and retest.

Before you change anything

  • Check what DCs enforce today on every DC: HKLM\SYSTEM\CurrentControlSet\Services\Kdc\StrongCertificateBindingEnforcement (2 = Full Enforcement). Recent Windows updates move DCs to Full Enforcement; see the KB5014754 timeline for your patch level.
  • Before enforcing, review the System log on DCs for Kdcsvc events 39, 40 and 41: they list certificates that would fail under Full Enforcement. Those certificates must be re-issued (with the SID extension) or strongly mapped first.
  • Find templates with the flag: certutil -dstemplate <Template> msPKI-Enrollment-Flag (0x80000 = CT_FLAG_NO_SECURITY_EXTENSION). Find out why it was set; it is rarely needed.
  • Review who has write access over the enrolling accounts named in the finding (the "victims").

How to fix it

  1. A DC the finding names by its build (older than the May 2022 or February 2025 update) needs the current Windows updates first: before May 2022 the KDC ignores the value, and from the September 2025 update DCs enforce strong mapping whatever it says.
  2. After events 39/40/41 are clean, set StrongCertificateBindingEnforcement = 2 on every DC (Group Policy Preferences registry item, or the command below).
  3. KB5014754 describes how each Windows Server version applies the value; if in doubt, make the change in a maintenance window and restart the DC.

Option B: Remove the "no security extension" flag from the template (ESC9)

  1. Clear CT_FLAG_NO_SECURITY_EXTENSION with PowerShell (below); there is no checkbox for it in certtmpl.msc.
  2. New certificates then carry the requester's SID and cannot be remapped by renaming the UPN.

Option C: Remove UPN mapping for Schannel (ESC10)

  1. On every DC set HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\CertificateMappingMethods to 0x18 (Microsoft's recommended value), removing the UPN bit 0x4. A DC the finding names by its build has no value set and predates the May 2022 update, so it uses the old default 0x1F: install the current Windows updates. Only the updates close it on a DC older than May 2022: 0x18 maps through the KDC, which checks no mapping before that update.
  2. Test TLS client-certificate logons (for example LDAPS with client certificates) first.

In all cases, also remove unnecessary write permissions over enrolling accounts (the accounts and groups listed in the finding).

PowerShell / certutil

PowerShell
# On each DC: check, then enforce strong certificate binding (ESC9)
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\Kdc" -Name StrongCertificateBindingEnforcement
Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\Kdc" -Name StrongCertificateBindingEnforcement -Value 2 -Type DWord

# Look for certificates that would fail full enforcement (Kdcsvc events 39, 40, 41)
Get-WinEvent -FilterHashtable @{LogName = 'System'; ProviderName = 'Kdcsvc'; Id = 39, 40, 41} -MaxEvents 50

# Template flag: show, then clear only CT_FLAG_NO_SECURITY_EXTENSION (0x80000)
$cfg = (Get-ADRootDSE).configurationNamingContext
$dn  = "CN=<Template>,CN=Certificate Templates,CN=Public Key Services,CN=Services,$cfg"
$tpl = Get-ADObject $dn -Properties 'msPKI-Enrollment-Flag'
'0x{0:X}' -f $tpl.'msPKI-Enrollment-Flag'            # note this value for rollback
Set-ADObject $dn -Replace @{ 'msPKI-Enrollment-Flag' = ($tpl.'msPKI-Enrollment-Flag' -band -bnot 0x80000) }

# On each DC: remove UPN mapping for Schannel (ESC10)
Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel" -Name CertificateMappingMethods -Value 0x18 -Type DWord

Verify the fix

  • Every DC reports StrongCertificateBindingEnforcement = 2 and (for ESC10) CertificateMappingMethods without 0x4.
  • The template's msPKI-Enrollment-Flag no longer contains 0x80000.
  • Collect again (or use Retest on the finding) and confirm the finding ID is gone.
  • Pathwarden needs the DC registry values to decide. SharpHound reads them when it has admin rights on the DCs. Without them, the finding is "unverified": Pathwarden neither confirms nor clears it.
  • To supply them without giving the collector admin rights on DCs, have each DC's admins run scripts\Get-PathwardenTierZeroSettings.ps1 (in the release) on the DC. It is read-only and writes pathwarden-tierzero-<HOST>.<domain>.json. Put the files beside the collection (folder or zip), or in a Tier Zero settings folder (Settings). The report's "Tier Zero settings" row then shows every DC as read. See docs/tier-zero-settings.md.
  • One DC with a weak value is enough for the finding; closing it needs strong values on every DC of the forest.
  • A file read more than 90 days before the collection, or with no read time, is stale: its weak values still count, its strong values don't. Run the script again after fixing the DCs.
  • An unset value means what the DC's patch level makes it mean, so Pathwarden reads the OS build the script records: unset CertificateMappingMethods is weak before the May 2022 update, unset StrongCertificateBindingEnforcement before the February 2025 update, and from the September 2025 update binding 0 or 1 no longer allows weak mappings (KB5014754). Without a build (SharpHound's data alone, or an OS Pathwarden has no update list for), an unset value is unverified. A stale file's build can open a finding, never close one.

Roll back

Set StrongCertificateBindingEnforcement back to 1 (compatibility mode, where still supported by your patch level), restore the previous CertificateMappingMethods value, or restore the template flag by setting msPKI-Enrollment-Flag back to the value you noted (Set-ADObject $dn -Replace @{ 'msPKI-Enrollment-Flag' = <OldValue> }).

References

  1. Microsoft KB5014754, certificate-based authentication changes on domain controllerssupport.microsoft.com
  2. SpecterOps, Certified Pre-Ownedspecterops.io
  3. SpecterOps BloodHound docs: ADCSESC9abloodhound.specterops.io
  4. SpecterOps BloodHound docs: ADCSESC10abloodhound.specterops.io
  5. Certipy wiki: privilege escalation techniquesgithub.com
  6. MITRE ATT&CK T1649: Steal or Forge Authentication Certificatesattack.mitre.org

↑↓ to moveEnter to openEsc to close