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

adcs-attack-methodology.md (15616B)


      1 ---
      2 title: "ADCS Attack Methodology"
      3 description: "ADCS/ESC attack index following Certified Pre-Owned taxonomy: ESC, THEFT, PERSIST, DPERSIST phases."
      4 category: active-directory
      5 subcategory: "ADCS & Certificates"
      6 tags: [active-directory, adcs, esc, certificates]
      7 tools: [Certipy, Certify]
      8 difficulty: advanced
      9 updated: "2026-08-09"
     10 source: "vault:ActiveDirectory/ACL-ESC-Techniques/_ADCS Attack Methodology Guide.md"
     11 ---
     12 
     13 # ADCS Attack Methodology
     14 
     15 > **Purpose —** How an ADCS engagement flows from enumeration through escalation, credential theft, and domain persistence. Follows the SpecterOps **Certified Pre-Owned** taxonomy: **ESC** (escalation), **THEFT** (credential theft), **PERSIST** (account persistence), **DPERSIST** (domain persistence).
     16 
     17 ## 1. The ADCS Attack Surface
     18 
     19 Active Directory Certificate Services binds a cryptographic identity (a certificate) to an AD principal. The moment a certificate can carry an *authentication* EKU and an attacker can influence *whose* identity is stamped into it, the certificate becomes a password-equivalent that survives password resets. Four things go wrong:
     20 
     21 <figure class="flow plate corners">
     22   <figcaption class="flow__cap"><span class="flow__kind">ADCS attack surface</span><span class="flow__dir">TD</span></figcaption>
     23   <div class="flow__body">
     24     <div class="flow__diagram" data-dir="td">
     25       <div class="flow-rank">
     26         <div class="flow-node">Template misconfig<span class="sub">ESC1-3, ESC9, ESC15</span></div>
     27         <div class="flow-node">Object/CA ACL abuse<span class="sub">ESC4, ESC5, ESC7</span></div>
     28         <div class="flow-node">CA/DC config or bug<span class="sub">ESC6, ESC8, ESC10-16, Certifried</span></div>
     29         <div class="flow-node">Existing cert theft<span class="sub">THEFT1-5</span></div>
     30       </div>
     31       <div class="flow-join"></div>
     32       <div class="flow-rank"><div class="flow-node is-goal">Attacker obtains a cert<span class="sub">for a privileged identity</span></div></div>
     33       <div class="flow-edge"></div>
     34       <div class="flow-rank"><div class="flow-node">PKINIT auth<span class="sub">TGT + NT hash</span></div></div>
     35       <div class="flow-edge"></div>
     36       <div class="flow-rank"><div class="flow-node is-danger">Persistence<span class="sub">PERSIST1-3, DPERSIST1-3</span></div></div>
     37     </div>
     38   </div>
     39 </figure>
     40 
     41 > **Why certificates are dangerous —** A stolen or forged authentication certificate is valid until it **expires** (often 1–5 years) or is **revoked**. Password changes do not invalidate it. Forged certs (Golden Certificate, rogue CA) are never seen by the CA's issuance pipeline, so they **cannot be revoked**.
     42 
     43 ## 2. Phase 1 — Enumeration
     44 
     45 Everything starts with `certipy find`. Do this before anything else.
     46 
     47 ```bash
     48 # Full enumeration, only show vulnerable, print to terminal + save JSON/BloodHound
     49 certipy-ad find -u 'lowpriv@domain.htb' -p 'Password123!' \
     50   -dc-ip $TARGET -vulnerable -stdout
     51 
     52 # Pass-the-hash variant
     53 certipy-ad find -u 'lowpriv@domain.htb' -hashes :NTHASH -dc-ip $TARGET -vulnerable -stdout
     54 
     55 # Enumerate everything (not just vulnerable) — useful for THEFT/PERSIST target hunting
     56 certipy-ad find -u 'lowpriv@domain.htb' -p 'Password123!' -dc-ip $TARGET -stdout
     57 ```
     58 
     59 Three registry/patch checks decide whether the mapping-based attacks work:
     60 
     61 ```bash
     62 # StrongCertificateBindingEnforcement on the DC (0=off, 1=compat[default], 2=full)
     63 nxc smb $TARGET -u user -p pass \
     64   -x 'reg query "HKLM\SYSTEM\CurrentControlSet\Services\Kdc" /v StrongCertificateBindingEnforcement'
     65 
     66 # CertificateMappingMethods (ESC10 — 0x4 = weak UPN mapping enabled)
     67 nxc smb $TARGET -u user -p pass \
     68   -x 'reg query "HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel" /v CertificateMappingMethods'
     69 
     70 # Patch level (Certifried KB5014754, EKUwu KB5044281)
     71 nxc smb $TARGET -u user -p pass -x 'wmic qfe list brief | findstr "KB5014754 KB5044281"'
     72 ```
     73 
     74 > **Tip — feed BloodHound.** `certipy find` writes a BloodHound-compatible zip. Import it to see ACL edges to PKI objects (drives ESC4/ESC5/ESC7 and DPERSIST3).
     75 
     76 ## 3. Phase 2 — Escalation (ESC)
     77 
     78 Classify the finding, then jump to the technique. The generic escalation loop is always the same three Certipy verbs:
     79 
     80 ```bash
     81 # 1. request a cert for a privileged identity (technique-specific flags)
     82 certipy-ad req  -u me -p pass -ca 'CA-NAME' -template 'TEMPLATE' -upn 'administrator@domain.htb'
     83 # 2. authenticate the cert -> TGT + NT hash (PKINIT + UnPAC-the-hash)
     84 certipy-ad auth -pfx administrator.pfx -dc-ip $TARGET
     85 # 3. use the TGT or hash
     86 export KRB5CCNAME=administrator.ccache && impacket-wmiexec -k -no-pass DC01.domain.htb
     87 ```
     88 
     89 The ESC family answers "how do I get a cert for someone I shouldn't." Quick routing:
     90 
     91 | If certipy shows... | Technique |
     92 | :-- | :-- |
     93 | `Enrollee Supplies Subject: True` + auth EKU | ESC1 — SAN specification in template |
     94 | `Any Purpose` / `No EKU` | ESC2 — Any Purpose EKU / No EKU (the Swiss certificate) |
     95 | `Certificate Request Agent` EKU | ESC3 — misconfigured enrollment agent templates |
     96 | Dangerous ACE on template | ESC4 — vulnerable certificate template access control |
     97 | Write ACE on PKI object | ESC5 — vulnerable PKI object access control |
     98 | `User Specified SAN: Enabled` (CA) | ESC6 — EDITF_ATTRIBUTESUBJECTALTNAME2 flag |
     99 | `ManageCA` / `ManageCertificates` | ESC7 — vulnerable CA access control |
    100 | `Web Enrollment: Enabled` | ESC8 — NTLM relay to ADCS HTTP web enrollment |
    101 | `No Security Extension` (template) | ESC9 — no security extension (template-level) |
    102 | Weak DC mapping registry | ESC10 — weak certificate mapping |
    103 | `IF_ENFORCEENCRYPTICERTREQUEST: Disabled` | ESC11 — NTLM relay to ADCS RPC (ICPR) |
    104 | YubiHSM on CA | ESC12 — shell access to CA with YubiHSM |
    105 | Issuance policy OID group link | ESC13 — issuance policy OID group link |
    106 | Weak `altSecurityIdentities` mapping | ESC14 — weak explicit certificate mapping |
    107 | Schema v1 + unpatched CA | ESC15 — EKUwu (CVE-2024-49019) |
    108 | `DisableExtensionList` on CA | ESC16 — security extension disabled on CA (globally) |
    109 | WSUS + Server Auth template | ESC17 — ADCS certificate spoofing vs HTTPS-enabled WSUS clients |
    110 | MachineAccountQuota ≥ 1 + unpatched DC | Certifried (CVE-2022-26923) |
    111 
    112 ## 4. Phase 3 — Credential Theft (THEFT)
    113 
    114 Once you have a foothold, harvest certificates that already exist rather than requesting new ones. Often quieter than an ESC, and yields keys for users/machines that have already enrolled.
    115 
    116 | Technique | Target | Note |
    117 | :-- | :-- | :-- |
    118 | THEFT1 — export cert + private key via CryptoAPI | Exportable (or force-exportable) keys in a user's store | |
    119 | THEFT2 — user certificate theft via DPAPI | `%APPDATA%\Microsoft\Crypto` + masterkeys | |
    120 | THEFT3 — machine certificate theft via DPAPI | SYSTEM store, machine masterkeys | |
    121 | THEFT4 — finding certificate files on disk | `.pfx .p12 .pem .key`, unattend.xml | |
    122 | THEFT5 — NT hash via PKINIT (UnPAC-the-hash) | Any cert you can authenticate with | |
    123 
    124 ## 5. Phase 4 — Using Certificates (PKINIT)
    125 
    126 A certificate with an authentication EKU is spent through **PKINIT** to obtain a Kerberos TGT, and optionally the account's NT hash via **UnPAC-the-hash** (U2U). This is the pivot between "I have a .pfx" and "I have a shell."
    127 
    128 ```bash
    129 # Certipy does PKINIT + UnPAC in one step
    130 certipy-ad auth -pfx target.pfx -dc-ip $TARGET
    131 #   -> target.ccache (TGT)  AND  the account's NT hash
    132 
    133 # Windows equivalent (Rubeus)
    134 .\Rubeus.exe asktgt /user:target /certificate:target.pfx /getcredentials /nowrap
    135 ```
    136 
    137 > **Warning — clock skew breaks PKINIT.** Certificate auth is Kerberos, so it is time-sensitive. If `certipy auth` throws `KRB_AP_ERR_SKEW`, wrap it with faketime:
    138 > ```bash
    139 > faketime -f '+7h30m' certipy-ad auth -pfx target.pfx -dc-ip $TARGET
    140 > ```
    141 
    142 > **Tip — Pass-the-Certificate.** If the DC has no PKINIT support but does support Schannel/LDAPS, authenticate the cert over LDAP (`certipy auth -ldap-shell`) instead of Kerberos. Shadow Credentials (msDS-KeyCredentialLink abuse) uses the same PKINIT path with a key you plant yourself.
    143 
    144 ## 6. Phase 5 — Persistence (PERSIST / DPERSIST)
    145 
    146 Certificates make excellent persistence because they outlive password resets.
    147 
    148 **Account persistence** — keep access to one or more principals:
    149 
    150 | Technique | Idea |
    151 | :-- | :-- |
    152 | PERSIST1 — active user cert | Enrol/steal a long-life cert for a user you control |
    153 | PERSIST2 — machine account cert | Cert for a computer account survives the 30-day machine password rotation |
    154 | PERSIST3 — certificate renewal | Renew before expiry using the existing key, no creds needed |
    155 
    156 **Domain persistence** — forge auth for *anyone*; requires you first reach the CA private key or high privilege:
    157 
    158 | Technique | Idea |
    159 | :-- | :-- |
    160 | DPERSIST1 — Golden Certificate | Steal CA private key, forge certs offline |
    161 | DPERSIST2 — Rogue CA / NTAuth | Add an attacker CA to `NTAuthCertificates` and Root store |
    162 | DPERSIST3 — malicious misconfiguration | Plant ESC4/5/7-style ACL backdoors on PKI objects |
    163 
    164 <figure class="flow plate corners">
    165   <figcaption class="flow__cap"><span class="flow__kind">Domain persistence routes</span><span class="flow__dir">LR</span></figcaption>
    166   <div class="flow__body">
    167     <svg class="flow-svg" viewBox="0 0 685 330" role="img" aria-label="Domain Admin or CA server access branches into DPERSIST1 steal CA key, DPERSIST2 rogue CA in NTAuth, and DPERSIST3 ACL backdoor; the first two forge a cert for any principal offline, the third re-runs ESC4/5/7 at will">
    168       <path class="fedge" d="M185,180 L265,84" marker-end="url(#flow-arrow)" />
    169       <path class="fedge" d="M185,180 L265,180" marker-end="url(#flow-arrow)" />
    170       <path class="fedge" d="M185,180 L265,276" marker-end="url(#flow-arrow)" />
    171       <path class="fedge" d="M415,84 L495,132" marker-end="url(#flow-arrow)" />
    172       <path class="fedge" d="M415,180 L495,132" marker-end="url(#flow-arrow)" />
    173       <path class="fedge" d="M415,276 L495,276" marker-end="url(#flow-arrow)" />
    174       <g class="fnode is-entry"><rect class="fnode__box" x="35" y="156" width="150" height="48" /><text class="fnode__label" x="110" y="177" text-anchor="middle">Domain Admin /<tspan class="sub" x="110" dy="15">CA server access</tspan></text></g>
    175       <g class="fnode"><rect class="fnode__box" x="265" y="60" width="150" height="48" /><text class="fnode__label" x="340" y="81" text-anchor="middle">DPERSIST1<tspan class="sub" x="340" dy="15">steal CA key</tspan></text></g>
    176       <g class="fnode"><rect class="fnode__box" x="265" y="156" width="150" height="48" /><text class="fnode__label" x="340" y="177" text-anchor="middle">DPERSIST2<tspan class="sub" x="340" dy="15">rogue CA in NTAuth</tspan></text></g>
    177       <g class="fnode"><rect class="fnode__box" x="265" y="252" width="150" height="48" /><text class="fnode__label" x="340" y="273" text-anchor="middle">DPERSIST3<tspan class="sub" x="340" dy="15">ACL backdoor</tspan></text></g>
    178       <g class="fnode is-goal"><rect class="fnode__box" x="495" y="108" width="150" height="48" /><text class="fnode__label" x="570" y="129" text-anchor="middle">forge cert for<tspan class="sub" x="570" dy="15">any principal, offline</tspan></text></g>
    179       <g class="fnode"><rect class="fnode__box" x="495" y="252" width="150" height="48" /><text class="fnode__label" x="570" y="273" text-anchor="middle">re-run ESC4/5/7<tspan class="sub" x="570" dy="15">at will</tspan></text></g>
    180     </svg>
    181   </div>
    182 </figure>
    183 
    184 ## 7. Attack Chaining
    185 
    186 Real engagements chain these. Common paths:
    187 
    188 <figure class="flow plate corners">
    189   <figcaption class="flow__cap"><span class="flow__kind">Common attack chains</span><span class="flow__dir">TD</span></figcaption>
    190   <div class="flow__body">
    191     <div class="flow__diagram" data-dir="td">
    192       <div class="flow-rank"><div class="flow-node is-entry">Low-priv creds</div></div>
    193       <div class="flow-edge"></div>
    194       <div class="flow-rank"><div class="flow-node">certipy find</div></div>
    195       <div class="flow-branches">
    196         <div class="flow-lane"><div class="flow-edge"><span class="flow-edge__label">vuln template</span></div><div class="flow-node">ESC1/ESC9/ESC15</div></div>
    197         <div class="flow-lane"><div class="flow-edge"><span class="flow-edge__label">ACL edge</span></div><div class="flow-node">ESC4/ESC5 -&gt; make a template vuln -&gt; ESC1</div></div>
    198         <div class="flow-lane"><div class="flow-edge"><span class="flow-edge__label">CA rights</span></div><div class="flow-node">ESC7 -&gt; approve own request / ESC6</div></div>
    199         <div class="flow-lane"><div class="flow-edge"><span class="flow-edge__label">relay vector</span></div><div class="flow-node">ESC8/ESC11 -&gt; relay DC$ -&gt; DA</div></div>
    200       </div>
    201       <div class="flow-join"></div>
    202       <div class="flow-rank"><div class="flow-node">certipy auth -&gt; DA TGT + hash</div></div>
    203       <div class="flow-edge"></div>
    204       <div class="flow-rank"><div class="flow-node is-goal">DPERSIST1/2/3<span class="sub">domain persistence</span></div></div>
    205     </div>
    206   </div>
    207 </figure>
    208 
    209 Worked chains:
    210 
    211 - **ESC5 → ESC1:** you hold a write ACE over a PKI object, so you edit a template to enable `Enrollee Supplies Subject`, turning it into ESC1, then request an Administrator cert.
    212 - **ESC8 → DA:** coerce the DC (PetitPotam) and relay its NTLM auth to the CA web-enrolment endpoint, minting a DC certificate, then DCSync.
    213 - **ESC7 → ESC6-like:** with `ManageCA` you flip the `EDITF_ATTRIBUTESUBJECTALTNAME2` flag on the CA, enabling SAN injection on any template.
    214 - **Shadow Credentials → PKINIT:** you have `GenericWrite` over a target, so you plant a Key Credential and authenticate as them without touching ADCS templates at all.
    215 
    216 ## 8. Defence & Detection
    217 
    218 > **Detection focus —** Certificate abuse is quiet at the AD layer but leaves CA and KDC traces.
    219 
    220 - **CA logs:** Event ID **4886** (request) and **4887** (issued). Alert when the SAN/UPN differs from the requester.
    221 - **KDC logs:** Event ID **4768** (TGT) with certificate info; a 4768 without a preceding smart-card enrolment is suspicious.
    222 - **Enforce strong binding:** `StrongCertificateBindingEnforcement = 2` on all DCs (blocks ESC1/6/9/10/16 UPN tricks).
    223 - **Audit PKI ACLs:** review `NTAuthCertificates`, the Enrollment Services container, CA objects, and template DACLs for non-admin write (kills ESC4/5/7 and DPERSIST3).
    224 - **Restrict enrolment:** remove `Domain Users` / `Authenticated Users` from authentication-capable templates; require manager approval where SAN is needed.
    225 - **Protect the CA key:** HSM-backed keys defeat DPERSIST1 offline forgery.
    226 - **Monitor** `msDS-KeyCredentialLink` writes (Shadow Credentials) and `altSecurityIdentities` writes (ESC14).
    227 
    228 ## 9. Full Technique Map
    229 
    230 | Phase | Techniques | Prereq | Outcome |
    231 | :-- | :-- | :-- | :-- |
    232 | **Escalate** | ESC1–17, Certifried | Low-priv + a misconfig/bug/ACL | Cert for a privileged identity |
    233 | **Steal** | THEFT1–5 | Host foothold | Existing keys / NT hash |
    234 | **Use** | PKINIT auth | A `.pfx` with auth EKU | TGT + NT hash → shell |
    235 | **Persist (account)** | PERSIST1–3 | Enrolment or a stolen cert | Long-life access to principals |
    236 | **Persist (domain)** | DPERSIST1–3 | CA key or high privilege | Forge auth for anyone |
    237 | **Adjacent** | Shadow Credentials | `GenericWrite`/`GenericAll` over target | Auth as target via planted key |
    238 
    239 ## Sources
    240 
    241 - SpecterOps *Certified Pre-Owned* (Schroeder & Christensen)
    242 - Certipy Wiki — https://github.com/ly4k/Certipy/wiki
    243 - The Hacker Recipes — ADCS — https://www.thehacker.recipes/ad/movement/ad-cs/