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

ad-enumeration-native.md (26044B)


      1 ---
      2 title: "Active Directory Enumeration — Native Tooling"
      3 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."
      4 category: active-directory
      5 subcategory: "Tooling & Recon"
      6 tags: [active-directory, enumeration, powershell, ldap, recycle-bin, deleted-objects, laps, gmsa, dcsync, delegation, trusts, kerberoasting, opsec]
      7 tools: ["ActiveDirectory module (Get-AD*)", "ldapsearch", "ADSI / adsisearcher", "dsquery", "setspn", "nltest", "netexec/nxc", "Get-Acl (AD PSDrive)"]
      8 difficulty: intermediate
      9 updated: "2026-09-25"
     10 source: "vault:06PdfCheatSheets/Active Directory/AD-Enumeration-Native"
     11 ---
     12 
     13 # Active Directory Enumeration — Native Tooling
     14 
     15 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.
     16 
     17 > [!tip] When you do NOT need PowerView
     18 > 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).
     19 
     20 ## Setup — get the module loaded
     21 
     22 ```powershell
     23 Get-Module -ListAvailable ActiveDirectory      # present?
     24 Import-Module ActiveDirectory                  # load it
     25 Get-ADDomain                                    # sanity check: you can reach a DC
     26 (Get-ADDomain).DomainSID                        # domain SID (handy for RID math)
     27 ```
     28 
     29 No RSAT on the box? Install the capability (admin), or skip to the ADSI section:
     30 
     31 ```powershell
     32 # Windows 10/11 client:
     33 Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0
     34 # Windows Server:
     35 Install-WindowsFeature -Name RSAT-AD-PowerShell
     36 ```
     37 
     38 > [!info] Target a specific DC / creds
     39 > 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.
     40 
     41 ## The core cmdlets
     42 
     43 | Cmdlet | Answers |
     44 |---|---|
     45 | `Get-ADUser` | users + attributes (SPNs, UAC flags, lastLogon, description) |
     46 | `Get-ADGroup` / `Get-ADGroupMember` | groups; who is IN a group (`-Recursive` for nested) |
     47 | `Get-ADComputer` | computers; OS, delegation, lastLogon |
     48 | `Get-ADObject` | **any** object by LDAP filter — the universal tool (incl. deleted) |
     49 | `Get-ADDomain` / `Get-ADForest` | functional level, naming contexts, FSMO |
     50 | `Get-ADTrust` | trust relationships (direction, transitivity) |
     51 | `Get-ADServiceAccount` | (g)MSAs and who may retrieve the password |
     52 | `Get-ADOrganizationalUnit` | OU tree (targets for `-SearchBase`) |
     53 
     54 ```powershell
     55 Get-ADUser -Identity alfred -Properties *                      # everything on one user
     56 Get-ADGroupMember "Domain Admins" -Recursive | ft name,objectClass
     57 Get-ADComputer -Filter * -Properties OperatingSystem | ft name,OperatingSystem
     58 Get-ADObject -Filter "name -eq 'alfred'" -Properties *         # any object, any class
     59 ```
     60 
     61 ## Fine-tuning with `-Filter` (PowerShell syntax)
     62 
     63 `-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`.
     64 
     65 Operators: `-eq -ne -lt -gt -le -ge -like -notlike -and -or -not -bor -band`
     66 
     67 ```powershell
     68 Get-ADUser -Filter "Enabled -eq '$true'"                       # enabled users
     69 Get-ADUser -Filter "Description -like '*pass*'"                # creds in descriptions
     70 Get-ADUser -Filter {ServicePrincipalName -like "*"} -Properties ServicePrincipalName  # kerberoastable
     71 Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true}          # AS-REP roastable
     72 Get-ADUser -Filter "PasswordNeverExpires -eq '$true' -and Enabled -eq '$true'"
     73 Get-ADUser -Filter "adminCount -eq 1"                          # protected / privileged (AdminSDHolder)
     74 Get-ADComputer -Filter {TrustedForDelegation -eq $true}       # unconstrained delegation
     75 Get-ADUser -Filter "LastLogonDate -lt '$((Get-Date).AddDays(-90))'"  # stale accounts
     76 ```
     77 
     78 > [!warning] Filter gotchas
     79 > - Default properties are few — if you **filter or display** an attribute that isn't returned by default (SPN, `lastLogon`, `userAccountControl`), add it with `-Properties`.
     80 > - Braces `{ }` let you use `$true` bare; the string form needs `'$true'`. Both work — be consistent.
     81 > - `-Filter *` means "everything" — fine for computers, heavy for a big user base. Prefer a real predicate.
     82 
     83 ## Fine-tuning with `-LDAPFilter` (raw LDAP)
     84 
     85 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.
     86 
     87 ```powershell
     88 Get-ADObject -LDAPFilter "(servicePrincipalName=*)" -Properties servicePrincipalName   # SPNs
     89 Get-ADObject -LDAPFilter "(&(objectClass=user)(objectCategory=person))"
     90 Get-ADObject -LDAPFilter "(&(objectClass=group)(!(member=*)))"                          # empty groups
     91 Get-ADUser   -LDAPFilter "(memberOf=CN=Domain Admins,CN=Users,DC=htb,DC=local)"
     92 ```
     93 
     94 `userAccountControl` bits use the bitwise matching rule OID `1.2.840.113556.1.4.803`:
     95 
     96 ```powershell
     97 # disabled accounts (bit 0x2)
     98 Get-ADObject -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=2)"
     99 # password never expires (0x10000 = 65536)
    100 Get-ADUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=65536)"
    101 # don't require pre-auth (0x400000 = 4194304) -> AS-REP roast
    102 Get-ADUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=4194304)"
    103 # trusted for delegation (0x80000 = 524288) -> unconstrained
    104 Get-ADObject -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)"
    105 ```
    106 
    107 | UAC bit | Value | Meaning |
    108 |---|---|---|
    109 | 0x0002 | 2 | Account disabled |
    110 | 0x0020 | 32 | Password not required |
    111 | 0x0200 | 512 | Normal account |
    112 | 0x10000 | 65536 | Password never expires |
    113 | 0x80000 | 524288 | Trusted for delegation (unconstrained) |
    114 | 0x100000 | 1048576 | Not delegated (sensitive) |
    115 | 0x400000 | 4194304 | Does not require Kerberos pre-auth |
    116 | 0x0010 | 16 | Account locked out (transient) |
    117 | 0x0040 | 64 | Password can't change |
    118 | 0x0080 | 128 | Reversible (cleartext) password storage allowed |
    119 | 0x2000 | 8192 | Server trust account (domain controller) |
    120 | 0x40000 | 262144 | Smart-card required for interactive logon |
    121 | 0x200000 | 2097152 | Use DES keys only (weak Kerberos / downgrade) |
    122 | 0x800000 | 8388608 | Password expired |
    123 | 0x1000000 | 16777216 | Trusted to auth for delegation (constrained + protocol transition / S4U2Self) |
    124 | 0x4000000 | 67108864 | Partial secrets account (RODC) |
    125 
    126 > [!tip] AND-rule vs OR-rule for UAC bits
    127 > `…1.2.840.113556.1.4.803:=X` (BIT_AND) matches when `(uac & X) == X` — **every** bit in `X` must be set, so require two flags either by summing them (`32 + 65536 = 65568`) or by AND-ing two clauses. `…1.2.840.113556.1.4.804:=X` (BIT_OR) matches when `(uac & X) != 0` — **any** of the bits. Use `803` for "has all of", `804` for "has any of".
    128 
    129 ## High-value `-LDAPFilter` recipes
    130 
    131 Targeted one-liners for the objects worth finding first. Each filters on exactly the attribute it needs — add `-Properties` to *display* it.
    132 
    133 ```powershell
    134 # Cleartext creds parked in description / info (Notes) fields
    135 Get-ADUser -LDAPFilter "(|(description=*pass*)(description=*pwd*)(info=*pass*)(info=*pwd*))" `
    136   -Properties description,info | ft SamAccountName,description,info
    137 
    138 # Privileged AND kerberoastable — an SPN on an adminCount=1 principal
    139 Get-ADUser -LDAPFilter "(&(servicePrincipalName=*)(adminCount=1))" `
    140   -Properties servicePrincipalName,adminCount,memberOf | ft SamAccountName,servicePrincipalName
    141 
    142 # PASSWD_NOTREQD (blank password may be allowed) — bit 0x20 = 32
    143 Get-ADUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=32)" -Properties userAccountControl
    144 
    145 # PASSWD_NOTREQD *and* DONT_EXPIRE_PASSWORD (32 + 65536 = 65568) — two equivalent forms
    146 Get-ADUser -LDAPFilter "(&(userAccountControl:1.2.840.113556.1.4.803:=32)(userAccountControl:1.2.840.113556.1.4.803:=65536))"
    147 Get-ADUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=65568)"
    148 
    149 # Reversible encryption enabled (0x80 = 128) — password recoverable from NTDS
    150 Get-ADUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=128)"
    151 
    152 # Constrained delegation w/ protocol transition (0x1000000 = 16777216) — S4U abuse targets
    153 Get-ADObject -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=16777216)" -Properties msDS-AllowedToDelegateTo
    154 
    155 # Domain controllers by UAC (SERVER_TRUST_ACCOUNT 0x2000 = 8192)
    156 Get-ADObject -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=8192)" -Properties dNSHostName
    157 
    158 # Computers by OS — patch-level / EOL targeting (operatingSystem + build number)
    159 Get-ADComputer -LDAPFilter "(operatingSystem=*Server*2012*)" -Properties operatingSystem,operatingSystemVersion |
    160   ft Name,operatingSystem,operatingSystemVersion
    161 Get-ADComputer -LDAPFilter "(|(operatingSystem=*2008*)(operatingSystem=*2003*))" -Properties operatingSystem
    162 ```
    163 
    164 | Want | `-LDAPFilter` |
    165 |---|---|
    166 | Creds in description/info | `(\|(description=*pass*)(info=*pass*))` |
    167 | SPN + adminCount=1 | `(&(servicePrincipalName=*)(adminCount=1))` |
    168 | Password not required | `(userAccountControl:1.2.840.113556.1.4.803:=32)` |
    169 | Not-required + never-expires | `(userAccountControl:1.2.840.113556.1.4.803:=65568)` |
    170 | Reversible encryption | `(userAccountControl:1.2.840.113556.1.4.803:=128)` |
    171 | Protocol-transition delegation | `(userAccountControl:1.2.840.113556.1.4.803:=16777216)` |
    172 | Domain controllers | `(userAccountControl:1.2.840.113556.1.4.803:=8192)` |
    173 | OS = Server 2012 | `(operatingSystem=*Server*2012*)` |
    174 
    175 ## Scope and attributes — pull only what you need
    176 
    177 ```powershell
    178 # only the columns you want, not the whole object
    179 Get-ADUser -Filter * -Properties SamAccountName,Description,LastLogonDate |
    180   Select SamAccountName,Description,LastLogonDate
    181 
    182 # restrict the search to one OU (and its children)
    183 Get-ADUser -SearchBase "OU=Servers,DC=htb,DC=local" -Filter *
    184 
    185 # scope: Base (this object) / OneLevel (direct children) / Subtree (default)
    186 Get-ADObject -SearchBase "DC=htb,DC=local" -SearchScope OneLevel -LDAPFilter "(objectClass=organizationalUnit)"
    187 ```
    188 
    189 > [!info] Read it back with the right tool
    190 > `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.
    191 
    192 ## Deleted / tombstoned objects & the AD Recycle Bin
    193 
    194 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**.
    195 
    196 List every deleted object with its old parent and SID:
    197 
    198 ```powershell
    199 Get-ADObject -Filter 'isDeleted -eq $true -and name -ne "Deleted Objects"' `
    200   -IncludeDeletedObjects -Properties objectSid,lastKnownParent,whenChanged,msDS-LastKnownRDN |
    201   ft name,objectSid,lastKnownParent
    202 ```
    203 
    204 Inspect one deleted object fully, then restore it (Recycle Bin must be enabled):
    205 
    206 ```powershell
    207 Get-ADObject -LDAPFilter "(msDS-LastKnownRDN=alfred)" -IncludeDeletedObjects -Properties *
    208 Restore-ADObject -Identity "<distinguishedName-with-\0ADEL:GUID>"
    209 
    210 # is the Recycle Bin feature on?
    211 Get-ADOptionalFeature -Filter "name -like 'Recycle Bin Feature'" | ft name,EnabledScopes
    212 ```
    213 
    214 > [!note] Why this matters on a box
    215 > 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.
    216 
    217 ## Service accounts, gMSA & delegation
    218 
    219 ```powershell
    220 Get-ADServiceAccount -Filter * -Properties PrincipalsAllowedToRetrieveManagedPassword
    221 # who can read a gMSA password -> if it's you/your group, you can pull it
    222 Get-ADServiceAccount -Identity gmsaSvc -Properties * |
    223   Select Name,PrincipalsAllowedToRetrieveManagedPassword
    224 
    225 # constrained delegation targets
    226 Get-ADObject -LDAPFilter "(msDS-AllowedToDelegateTo=*)" -Properties msDS-AllowedToDelegateTo
    227 # resource-based constrained delegation (who can act on this computer)
    228 Get-ADComputer -Filter * -Properties PrincipalsAllowedToDelegateToAccount |
    229   Where-Object {$_.PrincipalsAllowedToDelegateToAccount}
    230 ```
    231 
    232 ## LAPS & gMSA — who can read the secret
    233 
    234 LAPS and gMSA passwords are AD attributes; the win is finding the objects **and** confirming *you* can read them. The confidential attributes (`ms-Mcs-AdmPwd`, `msLAPS-Password`, `msDS-ManagedPassword`) are only returned to principals with the read right, so a presence filter on them doubles as an access check.
    235 
    236 ### LAPS readers
    237 
    238 ```powershell
    239 # LAPS-managed computers (the expiry attr is world-readable) — v1 and v2
    240 Get-ADComputer -LDAPFilter "(ms-Mcs-AdmPwdExpirationTime=*)" -Properties ms-Mcs-AdmPwdExpirationTime      # LAPS v1
    241 Get-ADComputer -LDAPFilter "(msLAPS-PasswordExpirationTime=*)" -Properties msLAPS-PasswordExpirationTime  # LAPS v2
    242 
    243 # Computers whose ms-Mcs-AdmPwd you can actually READ (confidential attr only returns to authorised readers)
    244 Get-ADComputer -LDAPFilter "(ms-Mcs-AdmPwd=*)" -Properties ms-Mcs-AdmPwd | ft Name,ms-Mcs-AdmPwd
    245 
    246 # Who is delegated LAPS read on an OU
    247 Find-AdmPwdExtendedRights -Identity "OU=Servers,DC=htb,DC=local"   # LAPS v1 (AdmPwd.PS module)
    248 Find-LapsADExtendedRights -Identity "OU=Servers,DC=htb,DC=local"   # LAPS v2 (Windows LAPS module)
    249 ```
    250 
    251 ### gMSA readers
    252 
    253 ```powershell
    254 # All gMSAs by class, with the password-refresh interval
    255 Get-ADObject -LDAPFilter "(objectClass=msDS-GroupManagedServiceAccount)" `
    256   -Properties sAMAccountName,msDS-ManagedPasswordInterval | ft sAMAccountName,msDS-ManagedPasswordInterval
    257 
    258 # Who may retrieve each gMSA password (msDS-GroupMSAMembership, surfaced friendly)
    259 Get-ADServiceAccount -Filter * -Properties PrincipalsAllowedToRetrieveManagedPassword |
    260   Select Name,PrincipalsAllowedToRetrieveManagedPassword
    261 ```
    262 
    263 ```bash
    264 # From Linux — same objects
    265 ldapsearch -x -H ldap://$DC -D 'HTB\alfred' -w 'Pass' -b 'DC=htb,DC=local' \
    266   "(objectClass=msDS-GroupManagedServiceAccount)" sAMAccountName msDS-GroupMSAMembership
    267 ```
    268 
    269 > [!info] Extract, don't just find
    270 > Enumeration stops at *who can read it*. To pull the actual secret see [Attack #72 — LAPS Password Extraction](/sheets/active-directory/attack-72-laps-password-extraction) and [Attack #73 — gMSA Password Extraction](/sheets/active-directory/attack-73-gmsa-password-extraction).
    271 
    272 ## DCSync rights — read the domain-head ACL
    273 
    274 DCSync needs two extended rights on the **domain object** itself: `DS-Replication-Get-Changes` (`1131f6aa-…`) and `DS-Replication-Get-Changes-All` (`1131f6ad-…`). Read the domain head's ACL and list every principal that holds them — anyone beyond DCs / Domain Admins / Enterprise Admins is a finding.
    275 
    276 ```powershell
    277 $dcsync = '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2','1131f6ad-9c07-11d1-f79f-00c04fc2dcd2'
    278 $dn = (Get-ADDomain).DistinguishedName
    279 (Get-Acl "AD:\$dn").Access |
    280   Where-Object { $dcsync -contains $_.ObjectType.ToString() } |
    281   Select-Object IdentityReference,ActiveDirectoryRights,ObjectType,AccessControlType
    282 ```
    283 
    284 An entry with `ObjectType = 00000000-0000-0000-0000-000000000000` and `ActiveDirectoryRights` including `GenericAll` / `WriteDacl` / `ExtendedRight` grants DCSync implicitly (all extended rights). A third right, `DS-Replication-Get-Changes-In-Filtered-Set` (`89e95b76-444d-4c62-991a-0facbeda640c`), covers RODC-filtered attributes.
    285 
    286 > [!info] Turn the finding into the attack
    287 > A non-default principal holding both replication rights can replicate secrets. Execute it from [Attack #37 — DCSync](/sheets/active-directory/attack-37-dcsync-attack) — `impacket-secretsdump -just-dc <domain>/<user>@<dc>` or `nxc smb <dc> -u u -p p --ntds`.
    288 
    289 ## Trusts — direction & SID filtering
    290 
    291 `Get-ADTrust` resolves the trust's direction and type and — critical for cross-domain escalation — whether **SID filtering** is enforced.
    292 
    293 ```powershell
    294 Get-ADTrust -Filter * -Properties * |
    295   Select Name,Direction,TrustType,IntraForest,ForestTransitive,SIDFilteringQuarantined,SIDFilteringForestAware,SelectiveAuthentication,TrustAttributes | fl
    296 ```
    297 
    298 | Property | Reading it |
    299 |---|---|
    300 | `Direction` | `Inbound` (they trust us), `Outbound` (we trust them), `BiDirectional` |
    301 | `IntraForest` | `True` = parent/child inside one forest (SID filtering off by default → SID-history viable) |
    302 | `ForestTransitive` | `True` = trust to a separate forest |
    303 | `SIDFilteringQuarantined` | `True` = SID filtering ON, injected SID-history dropped; `False` = injection viable |
    304 | `SIDFilteringForestAware` | forest-boundary SID-filtering state (nuanced — read alongside `TrustAttributes`) |
    305 | `SelectiveAuthentication` | `True` = principals need an explicit *Allowed-to-authenticate* per resource |
    306 
    307 Raw and module-free — the `trustedDomain` object carries the numeric bitmask:
    308 
    309 ```powershell
    310 Get-ADObject -LDAPFilter "(objectClass=trustedDomain)" `
    311   -Properties trustPartner,trustDirection,trustType,trustAttributes,securityIdentifier
    312 ```
    313 
    314 ```bash
    315 nltest /domain_trusts /all_trusts /v      # direction + type from any domain-joined host
    316 ```
    317 
    318 `trustDirection`: `1` inbound, `2` outbound, `3` bidirectional. `trustAttributes` bits: `0x1` non-transitive, `0x4` quarantined (SID filtering **on**), `0x8` forest-transitive, `0x20` within-forest, `0x40` treat-as-external.
    319 
    320 > [!info] Where an unfiltered trust goes
    321 > Intra-forest (parent/child) trusts don't SID-filter by default, so a child-domain compromise reaches the forest root via SID-history. See [Attack #68 — Cross-Domain Trust Abuse (SID History)](/sheets/active-directory/attack-68-cross-domain-trust-abuse-sid-history) and [Attack #69 — Forest Trust Abuse](/sheets/active-directory/attack-69-forest-trust-abuse-cross-forest-ticket-forging).
    322 
    323 ## No RSAT? ADSI / LDAP without the module
    324 
    325 `[adsisearcher]` and `System.DirectoryServices` ship on stock Windows — no module, no dropped binary.
    326 
    327 ```powershell
    328 ([adsisearcher]"(servicePrincipalName=*)").FindAll()                 # SPNs
    329 $s = [adsisearcher]"(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))"
    330 $s.PropertiesToLoad.AddRange(@("samaccountname")); $s.FindAll().Properties.samaccountname
    331 ```
    332 
    333 Built-in binaries when PowerShell is awkward:
    334 
    335 ```bash
    336 dsquery user -limit 0                                   # all users (if dsquery present)
    337 net user /domain ; net group "Domain Admins" /domain    # quick membership
    338 nltest /dclist:htb.local ; nltest /domain_trusts        # DCs and trusts
    339 setspn -T htb.local -Q */*                               # SPNs (kerberoast targets)
    340 ```
    341 
    342 ## From Linux — same filters, different client
    343 
    344 `ldapsearch` speaks the exact same LDAP filters as `-LDAPFilter`:
    345 
    346 ```bash
    347 ldapsearch -x -H ldap://$DC -D 'HTB\alfred' -w 'Pass' -b 'DC=htb,DC=local' \
    348   '(servicePrincipalName=*)' sAMAccountName servicePrincipalName
    349 # AS-REP roastable
    350 ldapsearch -x -H ldap://$DC -D 'HTB\alfred' -w 'Pass' -b 'DC=htb,DC=local' \
    351   '(userAccountControl:1.2.840.113556.1.4.803:=4194304)' sAMAccountName
    352 # deleted objects need the Show Deleted Objects control:
    353 ldapsearch -x -H ldap://$DC -D 'HTB\alfred' -w 'Pass' -b 'DC=htb,DC=local' \
    354   -E '!1.2.840.113556.1.4.417' '(isDeleted=TRUE)' msDS-LastKnownRDN lastKnownParent
    355 ```
    356 
    357 > [!info] Collectors
    358 > `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.
    359 
    360 ## Quiet queries — LDAP volume & signing (OPSEC)
    361 
    362 Native LDAP recon is low-signal, not no-signal. Keep it that way.
    363 
    364 - **Don't pull the directory.** `-Filter *` / `(objectClass=*)` over a `Subtree` scope walks every object and every default attribute — big, slow, loud. Scope with `-SearchBase`, predicate with a real filter, and list only the attributes you need. Reserve `-Properties *` for a single already-identified object.
    365 - **Watch paging.** DCs cap responses at the `MaxPageSize` policy (default **1000**). `Get-AD*` pages automatically; cap the pull with `-ResultSetSize` and tune the page with `-ResultPageSize`. `ldapsearch` needs `-E pr=1000/noprompt`; without it a large search trips the size limit at 1000.
    366 - **Bind securely / expect signing.** Hardened DCs enforce **LDAP signing** and **channel binding** and refuse simple binds. `Get-AD*` and `[adsisearcher]` negotiate sign-and-seal already. `ldapsearch` simple bind on `389` sends the password in cleartext — use `-Z` (StartTLS) or `ldaps://…:636`. `nxc ldap` supports `-k` and LDAPS for the same reason.
    367 - **Prefer one DC you already talk to.** Spraying queries across every DC multiplies the trail; pin `-Server <dc>` to the one you hold a session with.
    368 
    369 ```powershell
    370 Get-ADUser -LDAPFilter "(servicePrincipalName=*)" -ResultPageSize 200 -ResultSetSize 500 `
    371   -SearchBase "OU=Servers,DC=htb,DC=local" -Properties servicePrincipalName -Server dc01.htb.local
    372 ```
    373 
    374 > [!warning] What lights up
    375 > - **4662** (object access) fires when a **SACL** audits an attribute — reading confidential `ms-Mcs-AdmPwd` / `msDS-ManagedPassword` is the classic trigger.
    376 > - **1644** logs expensive/inefficient LDAP searches — only when *15 Field Engineering* diagnostics are raised, but a `(objectClass=*)` subtree dump is exactly what it flags.
    377 > - **2889** logs clients doing unsigned/cleartext simple binds when LDAP-interface-events diagnostics are on — your `ldapsearch -x` on `389` shows up here.
    378 
    379 ## Native vs PowerView — quick map
    380 
    381 | Task | Native | PowerView |
    382 |---|---|---|
    383 | Find users/computers | `Get-ADUser` / `Get-ADComputer` `-Filter`/`-LDAPFilter` | `Get-DomainUser` / `Get-DomainComputer` |
    384 | Group membership | `Get-ADGroupMember -Recursive` | `Get-DomainGroupMember -Recurse` |
    385 | SPNs (kerberoast) | `(servicePrincipalName=*)` | `Get-DomainUser -SPN` |
    386 | AS-REP roastable | UAC bit `4194304` filter | `Get-DomainUser -PreauthNotRequired` |
    387 | Deleted objects | `Get-ADObject -IncludeDeletedObjects` | *(n/a — use native)* |
    388 | Trusts | `Get-ADTrust -Filter *` | `Get-DomainTrust` |
    389 | Delegation | `msDS-AllowedToDelegateTo` / `TrustedForDelegation` | `Get-DomainComputer -Unconstrained` |
    390 | Object ACLs | `Get-Acl "AD:\<DN>"` | `Get-DomainObjectAcl` *(easier)* |
    391 | GPO to OU mapping | `Get-GPO` / `Get-GPInheritance` (GroupPolicy module) | `Get-DomainGPO` / `Get-DomainOU` |
    392 | LAPS read | `(ms-Mcs-AdmPwd=*)` / `Get-LapsADPassword` | `Get-DomainComputer -Properties ms-Mcs-AdmPwd` |
    393 | gMSA read | `Get-ADServiceAccount -Properties PrincipalsAllowedToRetrieveManagedPassword` | `Get-DomainObject -LDAPFilter "(objectClass=msDS-GroupManagedServiceAccount)"` |
    394 | DCSync rights | `Get-Acl "AD:\<domainDN>"` (replication GUIDs) | `Get-DomainObjectAcl -SearchBase <domainDN> -ResolveGUIDs` |
    395 | Password not required | UAC bit `32` filter | `Get-DomainUser -UACFilter PASSWD_NOTREQD` |
    396 | Computers by OS | `(operatingSystem=*2012*)` | `Get-DomainComputer -OperatingSystem "*2012*"` |
    397 
    398 > [!tip] The habit to build
    399 > 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**.
    400 
    401 
    402 ## Troubleshooting
    403 
    404 | Problem | Cause & fix |
    405 |---------|-------------|
    406 | `Get-ADUser : term not recognized` | The ActiveDirectory module is not loaded / RSAT absent. Import it, or drop to the ADSI / `System.DirectoryServices` path below — no install needed |
    407 | Cmdlets work but return nothing | You are likely bound to a read-only GC or the wrong `-SearchBase`. Confirm the domain DN with `Get-ADDomain` and widen the base |
    408 | `-LDAPFilter` returns fewer objects than expected | The default page size caps results. The AD cmdlets page automatically; raw ADSI/`DirectorySearcher` does not — set `.PageSize = 1000` |
    409 | `A referral was returned from the server` | The query crossed a domain/OU boundary the current server cannot answer. Target a DC for that domain with `-Server`, or query the GC (`:3268`) for a forest-wide read |
    410 | Attribute you asked for is blank | It is not in the default property set. Add it explicitly: `-Properties memberof,servicePrincipalName,lastLogonTimestamp` |
    411 | `lastLogonTimestamp` looks wrong | It is replicated only every ~14 days by design. For precise timing you need per-DC `lastLogon`, which does not replicate |
    412 | ADSI works locally but fails from a foothold shell | No Kerberos/NTLM context. Pass explicit creds to the `DirectoryEntry`, or run under `runas /netonly` |
    413 
    414 ## See Also
    415 
    416 - **[PowerView & PowerUp](/sheets/active-directory/powerview-powerup)** — the offensive toolkit whose queries this sheet maps to native equivalents of.
    417 - **[BloodHound](/sheets/active-directory/bloodhound)** — graph the relationships once you have collected the objects.
    418 - **[AD Recycle Bin Enumeration](/sheets/active-directory/ad-recycle-bin-enumeration)** — the deleted-object corner of the same directory.
    419 - **[Kerberoasting](/sheets/active-directory/kerberoasting-local-on-host)** — act on the SPN-bearing accounts these filters surface.
    420