commit 7dd3a0f7cbe1b9cb0828ce88360c6f5f5c2f93bf
parent a66d99ebde37c48de68a58284fc18f74da282855
Author: DAEMON <zer0sec.xp@icloud.com>
Date: Sun, 20 Sep 2026 01:51:40 +0100
Add native Active Directory enumeration cheat sheet page
New active-directory/ad-enumeration-native.md: native Get-AD* and LDAP
enumeration, -Filter/-LDAPFilter fine-tuning, deleted-object / AD Recycle
Bin recovery, ADSI fallback, and a native-vs-PowerView map.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat:
1 file changed, 226 insertions(+), 0 deletions(-)
diff --git a/src/content/sheets/active-directory/ad-enumeration-native.md b/src/content/sheets/active-directory/ad-enumeration-native.md
@@ -0,0 +1,226 @@
+---
+title: "Active Directory Enumeration — Native Tooling"
+description: "Native Get-AD* and LDAP enumeration: fine-tuning -Filter/-LDAPFilter, finding deleted accounts in the AD Recycle Bin, ADSI when RSAT is missing — without reaching for PowerView."
+category: active-directory
+subcategory: "Tooling & Recon"
+tags: [active-directory, enumeration, powershell, ldap, recycle-bin, deleted-objects]
+tools: ["ActiveDirectory module (Get-AD*)", "ldapsearch", "ADSI / adsisearcher", "dsquery", "setspn", "nltest"]
+difficulty: intermediate
+updated: "2026-09-20"
+source: "vault:06PdfCheatSheets/Active Directory/AD-Enumeration-Native"
+---
+
+# Active Directory Enumeration — Native Tooling
+
+The Microsoft-signed **`ActiveDirectory`** module (and, when it is missing, raw **ADSI / LDAP**) answers almost every enumeration question on a domain-joined box — who, what, where, which rights, which stale or **deleted** object — with no dropped tooling. PowerView is powerful, but for read-only recon it is an extra artefact you rarely need. This card is about driving `-Filter` and `-LDAPFilter` **precisely**, so you stop pulling the whole directory and grepping, and pull exactly the objects you want.
+
+> [!tip] When you do NOT need PowerView
+> If the task is "find objects / read attributes / list membership / find stale or deleted objects", the native `Get-AD*` cmdlets do it, are already present on any host with RSAT (always on a DC), are signed, and are quieter. Reach for PowerView only for what it genuinely adds: quick object-ACL enumeration (`Get-DomainObjectAcl`), GPO-to-OU mapping, and one-liner delegation/trust hunts — and even those have native equivalents (see the last table).
+
+## Setup — get the module loaded
+
+```powershell
+Get-Module -ListAvailable ActiveDirectory # present?
+Import-Module ActiveDirectory # load it
+Get-ADDomain # sanity check: you can reach a DC
+(Get-ADDomain).DomainSID # domain SID (handy for RID math)
+```
+
+No RSAT on the box? Install the capability (admin), or skip to the ADSI section:
+
+```powershell
+# Windows 10/11 client:
+Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0
+# Windows Server:
+Install-WindowsFeature -Name RSAT-AD-PowerShell
+```
+
+> [!info] Target a specific DC / creds
+> Every `Get-AD*` cmdlet takes `-Server <dc.fqdn>` and `-Credential (Get-Credential)`. From a non-domain host (with `runas /netonly`), that is how you query without being joined.
+
+## The core cmdlets
+
+| Cmdlet | Answers |
+|---|---|
+| `Get-ADUser` | users + attributes (SPNs, UAC flags, lastLogon, description) |
+| `Get-ADGroup` / `Get-ADGroupMember` | groups; who is IN a group (`-Recursive` for nested) |
+| `Get-ADComputer` | computers; OS, delegation, lastLogon |
+| `Get-ADObject` | **any** object by LDAP filter — the universal tool (incl. deleted) |
+| `Get-ADDomain` / `Get-ADForest` | functional level, naming contexts, FSMO |
+| `Get-ADTrust` | trust relationships (direction, transitivity) |
+| `Get-ADServiceAccount` | (g)MSAs and who may retrieve the password |
+| `Get-ADOrganizationalUnit` | OU tree (targets for `-SearchBase`) |
+
+```powershell
+Get-ADUser -Identity alfred -Properties * # everything on one user
+Get-ADGroupMember "Domain Admins" -Recursive | ft name,objectClass
+Get-ADComputer -Filter * -Properties OperatingSystem | ft name,OperatingSystem
+Get-ADObject -Filter "name -eq 'alfred'" -Properties * # any object, any class
+```
+
+## Fine-tuning with `-Filter` (PowerShell syntax)
+
+`-Filter` takes a PowerShell-ish expression the module translates to LDAP. Quote the whole filter; single-quote literal values; compare booleans to `$true`/`$false`; `*` is the wildcard with `-like`.
+
+Operators: `-eq -ne -lt -gt -le -ge -like -notlike -and -or -not -bor -band`
+
+```powershell
+Get-ADUser -Filter "Enabled -eq '$true'" # enabled users
+Get-ADUser -Filter "Description -like '*pass*'" # creds in descriptions
+Get-ADUser -Filter {ServicePrincipalName -like "*"} -Properties ServicePrincipalName # kerberoastable
+Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} # AS-REP roastable
+Get-ADUser -Filter "PasswordNeverExpires -eq '$true' -and Enabled -eq '$true'"
+Get-ADUser -Filter "adminCount -eq 1" # protected / privileged (AdminSDHolder)
+Get-ADComputer -Filter {TrustedForDelegation -eq $true} # unconstrained delegation
+Get-ADUser -Filter "LastLogonDate -lt '$((Get-Date).AddDays(-90))'" # stale accounts
+```
+
+> [!warning] Filter gotchas
+> - Default properties are few — if you **filter or display** an attribute that isn't returned by default (SPN, `lastLogon`, `userAccountControl`), add it with `-Properties`.
+> - Braces `{ }` let you use `$true` bare; the string form needs `'$true'`. Both work — be consistent.
+> - `-Filter *` means "everything" — fine for computers, heavy for a big user base. Prefer a real predicate.
+
+## Fine-tuning with `-LDAPFilter` (raw LDAP)
+
+When the PowerShell filter fights you — bitwise UAC flags, negation, exact attribute names — drop to raw LDAP. Operators sit at the front: `(&(a)(b))` = AND, `(|(a)(b))` = OR, `(!(a))` = NOT, `*` = wildcard/presence.
+
+```powershell
+Get-ADObject -LDAPFilter "(servicePrincipalName=*)" -Properties servicePrincipalName # SPNs
+Get-ADObject -LDAPFilter "(&(objectClass=user)(objectCategory=person))"
+Get-ADObject -LDAPFilter "(&(objectClass=group)(!(member=*)))" # empty groups
+Get-ADUser -LDAPFilter "(memberOf=CN=Domain Admins,CN=Users,DC=htb,DC=local)"
+```
+
+`userAccountControl` bits use the bitwise matching rule OID `1.2.840.113556.1.4.803`:
+
+```powershell
+# disabled accounts (bit 0x2)
+Get-ADObject -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=2)"
+# password never expires (0x10000 = 65536)
+Get-ADUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=65536)"
+# don't require pre-auth (0x400000 = 4194304) -> AS-REP roast
+Get-ADUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=4194304)"
+# trusted for delegation (0x80000 = 524288) -> unconstrained
+Get-ADObject -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)"
+```
+
+| UAC bit | Value | Meaning |
+|---|---|---|
+| 0x0002 | 2 | Account disabled |
+| 0x0020 | 32 | Password not required |
+| 0x0200 | 512 | Normal account |
+| 0x10000 | 65536 | Password never expires |
+| 0x80000 | 524288 | Trusted for delegation (unconstrained) |
+| 0x100000 | 1048576 | Not delegated (sensitive) |
+| 0x400000 | 4194304 | Does not require Kerberos pre-auth |
+
+## Scope and attributes — pull only what you need
+
+```powershell
+# only the columns you want, not the whole object
+Get-ADUser -Filter * -Properties SamAccountName,Description,LastLogonDate |
+ Select SamAccountName,Description,LastLogonDate
+
+# restrict the search to one OU (and its children)
+Get-ADUser -SearchBase "OU=Servers,DC=htb,DC=local" -Filter *
+
+# scope: Base (this object) / OneLevel (direct children) / Subtree (default)
+Get-ADObject -SearchBase "DC=htb,DC=local" -SearchScope OneLevel -LDAPFilter "(objectClass=organizationalUnit)"
+```
+
+> [!info] Read it back with the right tool
+> `Select`, `Format-Table (ft)`, `Format-List (fl)` and `Sort-Object` shape output; `Export-Csv` banks it for the report. Pull `-Properties *` once on an interesting object to learn its real attribute names, then filter precisely.
+
+## Deleted / tombstoned objects & the AD Recycle Bin
+
+This is the case people reach for PowerView on and shouldn't — **deleted objects are native `Get-ADObject` territory**. When something is removed, its tombstone lingers, and if the AD Recycle Bin is enabled it can be **restored with attributes intact**.
+
+List every deleted object with its old parent and SID:
+
+```powershell
+Get-ADObject -Filter 'isDeleted -eq $true -and name -ne "Deleted Objects"' `
+ -IncludeDeletedObjects -Properties objectSid,lastKnownParent,whenChanged,msDS-LastKnownRDN |
+ ft name,objectSid,lastKnownParent
+```
+
+Inspect one deleted object fully, then restore it (Recycle Bin must be enabled):
+
+```powershell
+Get-ADObject -LDAPFilter "(msDS-LastKnownRDN=alfred)" -IncludeDeletedObjects -Properties *
+Restore-ADObject -Identity "<distinguishedName-with-\0ADEL:GUID>"
+
+# is the Recycle Bin feature on?
+Get-ADOptionalFeature -Filter "name -like 'Recycle Bin Feature'" | ft name,EnabledScopes
+```
+
+> [!note] Why this matters on a box
+> A deleted object can hold a still-valid SID/attributes, a group it belonged to, or a service account that was "removed" but restorable — reading its attributes (or restoring it) can hand you an identity or a path the live tree hides. That is exactly the fine-tuned, native query PowerView is not needed for.
+
+## Service accounts, gMSA & delegation
+
+```powershell
+Get-ADServiceAccount -Filter * -Properties PrincipalsAllowedToRetrieveManagedPassword
+# who can read a gMSA password -> if it's you/your group, you can pull it
+Get-ADServiceAccount -Identity gmsaSvc -Properties * |
+ Select Name,PrincipalsAllowedToRetrieveManagedPassword
+
+# constrained delegation targets
+Get-ADObject -LDAPFilter "(msDS-AllowedToDelegateTo=*)" -Properties msDS-AllowedToDelegateTo
+# resource-based constrained delegation (who can act on this computer)
+Get-ADComputer -Filter * -Properties PrincipalsAllowedToDelegateToAccount |
+ Where-Object {$_.PrincipalsAllowedToDelegateToAccount}
+```
+
+## No RSAT? ADSI / LDAP without the module
+
+`[adsisearcher]` and `System.DirectoryServices` ship on stock Windows — no module, no dropped binary.
+
+```powershell
+([adsisearcher]"(servicePrincipalName=*)").FindAll() # SPNs
+$s = [adsisearcher]"(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))"
+$s.PropertiesToLoad.AddRange(@("samaccountname")); $s.FindAll().Properties.samaccountname
+```
+
+Built-in binaries when PowerShell is awkward:
+
+```bash
+dsquery user -limit 0 # all users (if dsquery present)
+net user /domain ; net group "Domain Admins" /domain # quick membership
+nltest /dclist:htb.local ; nltest /domain_trusts # DCs and trusts
+setspn -T htb.local -Q */* # SPNs (kerberoast targets)
+```
+
+## From Linux — same filters, different client
+
+`ldapsearch` speaks the exact same LDAP filters as `-LDAPFilter`:
+
+```bash
+ldapsearch -x -H ldap://$DC -D 'HTB\alfred' -w 'Pass' -b 'DC=htb,DC=local' \
+ '(servicePrincipalName=*)' sAMAccountName servicePrincipalName
+# AS-REP roastable
+ldapsearch -x -H ldap://$DC -D 'HTB\alfred' -w 'Pass' -b 'DC=htb,DC=local' \
+ '(userAccountControl:1.2.840.113556.1.4.803:=4194304)' sAMAccountName
+# deleted objects need the Show Deleted Objects control:
+ldapsearch -x -H ldap://$DC -D 'HTB\alfred' -w 'Pass' -b 'DC=htb,DC=local' \
+ -E '!1.2.840.113556.1.4.417' '(isDeleted=TRUE)' msDS-LastKnownRDN lastKnownParent
+```
+
+> [!info] Collectors
+> `nxc ldap $DC -u u -p p --query "<ldapfilter>" "<attrs>"` runs the same filters at scale; BloodHound / RustHound-CE ingest the whole graph when you want relationships rather than a targeted lookup.
+
+## Native vs PowerView — quick map
+
+| Task | Native | PowerView |
+|---|---|---|
+| Find users/computers | `Get-ADUser` / `Get-ADComputer` `-Filter`/`-LDAPFilter` | `Get-DomainUser` / `Get-DomainComputer` |
+| Group membership | `Get-ADGroupMember -Recursive` | `Get-DomainGroupMember -Recurse` |
+| SPNs (kerberoast) | `(servicePrincipalName=*)` | `Get-DomainUser -SPN` |
+| AS-REP roastable | UAC bit `4194304` filter | `Get-DomainUser -PreauthNotRequired` |
+| Deleted objects | `Get-ADObject -IncludeDeletedObjects` | *(n/a — use native)* |
+| Trusts | `Get-ADTrust -Filter *` | `Get-DomainTrust` |
+| Delegation | `msDS-AllowedToDelegateTo` / `TrustedForDelegation` | `Get-DomainComputer -Unconstrained` |
+| Object ACLs | `Get-Acl "AD:\<DN>"` | `Get-DomainObjectAcl` *(easier)* |
+| GPO to OU mapping | `Get-GPO` / `Get-GPInheritance` (GroupPolicy module) | `Get-DomainGPO` / `Get-DomainOU` |
+
+> [!tip] The habit to build
+> Ask "what object and which attribute do I actually want?", write the `-LDAPFilter` for it, add `-Properties` for the attributes, and `-SearchBase` to scope it. That single targeted query — native, signed, already present — is almost always the answer, **deleted objects included**.