persist3-account-persistence-via-certificate-renewal.md (5169B)
1 --- 2 title: "PERSIST3 — Account Persistence via Certificate Renewal" 3 description: "Templates that allow renewal let a holder present their current certificate and receive a fresh one with a new validity window, authenticated by the…" 4 category: active-directory 5 subcategory: "ADCS & Certificates" 6 tags: ["active-directory", "adcs", "persistence"] 7 tools: ["Certipy", "OpenSSL", "PowerShell"] 8 difficulty: advanced 9 updated: "2026-08-10" 10 source: "vault:ActiveDirectory/ACL-ESC-Techniques/PERSIST3 — Account Persistence via Certificate Renewal.md" 11 --- 12 # PERSIST3 — Account Persistence via Certificate Renewal 13 14 ## Quick Reference 15 16 | Field | Value | 17 |-------|-------| 18 | **Category** | Account Persistence (renewal) | 19 | **Difficulty** | Low | 20 | **Pre-requisites** | An existing valid certificate + private key for the account; the template permits renewal | 21 | **Tools** | Certipy (`req -renew`), certreq | 22 | **OPSEC Noise** | Low — looks like normal certificate lifecycle | 23 | **One-liner** | Renew a certificate **before it expires** using only the existing cert/key — no account password needed — extending your access for another full validity period, indefinitely. | 24 25 *** 26 27 ## What Is PERSIST3? 28 29 Templates that allow **renewal** let a holder present their current certificate and receive a fresh one with a new validity window, authenticated *by the existing key* rather than by the user's password. An attacker who obtained a cert (via an ESC, THEFT, or PERSIST1) can therefore roll it forward forever, so long as they renew before each expiry. Password resets never break the chain because renewal never uses the password. 30 31 <figure class="flow plate corners"> 32 <figcaption class="flow__cap"><span class="flow__kind">Renewal persistence loop</span><span class="flow__dir">LR</span></figcaption> 33 <div class="flow__body"> 34 <svg class="flow-svg" viewBox="0 0 800 190" role="img" aria-label="cert v1 renews into cert v2, then cert v3, which renews back into the chain forever"> 35 <path class="fedge" d="M185,82 L323,82" marker-end="url(#flow-arrow)" /> 36 <path class="fedge" d="M475,82 L613,82" marker-end="url(#flow-arrow)" /> 37 <path class="fedge is-back" d="M690,106 L690,158 L110,158 L110,108" marker-end="url(#flow-arrow)" /> 38 <g class="fnode"><rect class="fnode__box" x="35" y="58" width="150" height="48" /><text class="fnode__label" x="110" y="79" text-anchor="middle">cert v1<tspan class="sub" x="110" dy="15">expires in 30d</tspan></text></g> 39 <g class="fnode"><rect class="fnode__box" x="325" y="58" width="150" height="48" /><text class="fnode__label" x="400" y="79" text-anchor="middle">cert v2<tspan class="sub" x="400" dy="15">fresh 1-2y</tspan></text></g> 40 <g class="fnode"><rect class="fnode__box" x="615" y="58" width="150" height="48" /><text class="fnode__label" x="690" y="86" text-anchor="middle">cert v3 …</text></g> 41 <g class="felabel"><rect class="felabel__box" x="216" y="74" width="76" height="16" /><text class="felabel__text" x="254" y="85" text-anchor="middle">renew with key</text></g> 42 <g class="felabel"><rect class="felabel__box" x="510" y="74" width="68" height="16" /><text class="felabel__text" x="544" y="85" text-anchor="middle">renew again</text></g> 43 <g class="felabel"><rect class="felabel__box" x="370" y="150" width="60" height="16" /><text class="felabel__text" x="400" y="161" text-anchor="middle">forever</text></g> 44 </svg> 45 </div> 46 </figure> 47 48 *** 49 50 ## Step 1 — Renew Before Expiry 51 52 ```bash 53 # Certipy — renew using the current pfx (no password required) 54 certipy-ad req -renew \ 55 -pfx current.pfx \ 56 -dc-ip $TARGET -ca 'DOMAIN-CA' 57 # -> renewed.pfx with a fresh validity window 58 ``` 59 60 ```powershell 61 # Windows — certreq renewal of an existing cert by thumbprint 62 certreq -enroll -user -q -PolicyServer * -cert <THUMBPRINT> Renew 63 ``` 64 65 *** 66 67 ## Step 2 — Track & Automate 68 69 ```bash 70 # Check remaining validity of a stashed cert 71 certipy-ad cert -pfx current.pfx -nokey -out /dev/stdout | grep -i 'Not After' 72 openssl pkcs12 -in current.pfx -nodes -nokeys | openssl x509 -noout -enddate 73 ``` 74 75 - Set a reminder a week before each expiry and re-run the renewal. 76 - Keep the private key material offline between renewals to reduce host footprint. 77 78 *** 79 80 ## OPSEC Considerations 81 82 | Action | Log | Noise | 83 | :-- | :-- | :-- | 84 | Renewal request | Event 4886/4887 on CA (looks routine) | 🟢 Low | 85 | PKINIT auth with renewed cert | Event 4768 on DC | 🟢 Low | 86 87 > [!note] Blends into normal lifecycle 88 > Renewals are indistinguishable from legitimate certificate maintenance, which is what makes this quiet persistence. 89 90 *** 91 92 ## Mitigation 93 94 - On compromise, **revoke** the certificate and its renewals, and disable renewal on sensitive templates. 95 - Reduce validity/overlap windows; require re-approval on renewal for high-value templates. 96 - Correlate renewals against expected owners and enrolment agents. 97 98 *** 99 100 ## See Also 101 102 - _ADCS Attack Methodology Guide · PERSIST1 — Active User Credential Theft via Certificates · PERSIST2 — Machine Account Persistence via Certificates 103 - Sources: SpecterOps *Certified Pre-Owned*; [Certipy Wiki](https://github.com/ly4k/Certipy/wiki)