This post is just a cheatsheet for situations where we have access to an Active Directory domain. It provides a checklist of enumeration steps, useful commands, and key information to gather during the assessment.
Windows basic enumeration
we need to know the groups and if there is possibility to escalation.
whoami
whoami /all
whoami /groups
whoami /priv
For domain information:
echo %USERDOMAIN%
echo %LOGONSERVER%
set
systeminfo
For users and groups:
net user /domain
net user username /domain
net group /domain
net group "Domain Admins" /domain
net group "Enterprise Admins" /domain
For domain/workstation/DC discovery:
nltest /domain_trusts
nltest /dclist:domain.local
nltest /dsgetdc:domain.local
nltest /sc_query:domain.local
For Kerberos and SPNs:
klist
klist sessions
setspn -L username
setspn -Q */*
For shares and sessions:
net view /domain
net view \\HOSTNAME
net use
For GPO information:
gpresult /r
gpresult /scope computer /v
gpresult /scope user /v
If dsquery happens to already be installed on the host, you can use:
dsquery user
dsquery computer
dsquery group
dsquery server
You can also use built-in PowerShell/.NET without importing a module. For example, query LDAP directly:
$root = [ADSI]"LDAP://RootDSE"
$root.defaultNamingContext
Enumerate users:
$root = [ADSI]"LDAP://RootDSE"
$searcher = New-Object DirectoryServices.DirectorySearcher
$searcher.SearchRoot = [ADSI]("LDAP://" + $root.defaultNamingContext)
$searcher.Filter = "(&(objectCategory=person)(objectClass=user))"
$searcher.FindAll() | ForEach-Object {
$_.Properties.samaccountname
}
Computers:
$searcher.Filter = "(objectCategory=computer)"
$searcher.FindAll() | ForEach-Object {
$_.Properties.name
}
Groups:
$searcher.Filter = "(objectCategory=group)"
$searcher.FindAll() | ForEach-Object {
$_.Properties.samaccountname
}
SPN-enabled accounts:
$searcher.Filter = "(&(objectCategory=person)(objectClass=user)(servicePrincipalName=*))"
$searcher.FindAll() | ForEach-Object {
$_.Properties.samaccountname
$_.Properties.serviceprincipalname
}
So for a zero-download / living-off-the-land AD enumeration workflow, I’d primarily rely on:
whoami
net
nltest
klist
setspn
gpresult
PowerShell + System.DirectoryServices / ADSI
The most powerful option there is usually PowerShell with **System.DirectoryServices.DirectorySearcher**, because it lets you query LDAP directly without importing any external tool or AD module.
adsisearcher
Yes. Think of PswReaders as a capability clue, not something you “run.”
You already established:
INFINITY\PswReaders
└── ted
So the red-team question is:
What object/attribute/resource has an ACL that grants
INFINITY\PswReaders some special read permission?
Given the name PswReaders, the first hypothesis I would test is LAPS / managed local-admin password read access.
1. Check whether LAPS exists in the domain
From the logged-in ted Windows session, you can query LDAP using the existing domain authentication — you do not need to know Ted’s password again.
Try:
([adsisearcher]"(&(objectCategory=computer)(ms-Mcs-AdmPwdExpirationTime=*))").FindAll().Count
If that returns computers, legacy Microsoft LAPS is probably deployed.
For newer Windows LAPS:
([adsisearcher]"(&(objectCategory=computer)(msLAPS-PasswordExpirationTime=*))").FindAll().Count
2. Identify machines with LAPS-related attributes
Legacy LAPS:
$s = [adsisearcher]"(&(objectCategory=computer)(ms-Mcs-AdmPwdExpirationTime=*))"
$s.PropertiesToLoad.Add("name") | Out-Null
$s.PropertiesToLoad.Add("dnshostname") | Out-Null
$s.FindAll() | ForEach-Object {
$_.Properties
}
New LAPS:
$s = [adsisearcher]"(&(objectCategory=computer)(msLAPS-PasswordExpirationTime=*))"
$s.PropertiesToLoad.Add("name") | Out-Null
$s.PropertiesToLoad.Add("dnshostname") | Out-Null
$s.FindAll() | ForEach-Object {
$_.Properties
}
3. Check whether Ted can read the protected password attribute
For legacy LAPS, test whether the attribute is returned for a specific computer:
$s = [adsisearcher]"(&(objectCategory=computer)(name=COMPUTERNAME))"
$s.PropertiesToLoad.Add("ms-Mcs-AdmPwd") | Out-Null
$r = $s.FindOne()
$r.Properties.Keys
If you see:
ms-mcs-admpwd
then your authenticated ted context has permission to read that attribute.
That would make the group name make a lot of sense:
ted
↓
PswReaders
↓
read ms-Mcs-AdmPwd
↓
managed local administrator credential
For Windows LAPS, look for:
msLAPS-Password
msLAPS-EncryptedPassword
instead.
4. Don’t limit the hypothesis to LAPS
If LAPS isn’t present, investigate where the group’s SID is referenced.
You know the group SID:
S-1-5-21-3616753307-3538385277-467097740-1108
Potential intended uses include:
PswReaders
├── LAPS password readers ← first thing I'd check
├── password/configuration share
├── ACL on a specific AD object
├── service-account secret storage
├── GPO-controlled resource
├── registry ACL
└── application-specific credential store
5. Check SYSVOL/GPO clues
Since Ted is domain-authenticated:
dir \\infinity.com\SYSVOL
dir \\infinity.com\NETLOGON
Search for the group name:
findstr /S /I "PswReaders" \\infinity.com\SYSVOL\*.xml
findstr /S /I "PswReaders" \\infinity.com\SYSVOL\*.inf
findstr /S /I "PswReaders" \\infinity.com\SYSVOL\*.ini
And search for LAPS policy indicators:
findstr /S /I "AdmPwd LAPS" \\infinity.com\SYSVOL\*.*
6. Find the DC and enumerate there
nltest /dsgetdc:infinity.com
Suppose it gives:
DC01.infinity.com
Then:
net view \\DC01
and:
dir \\DC01\SYSVOL
dir \\DC01\NETLOGON
What I’d prioritize
Based specifically on the name PswReaders, I would investigate in this order:
1. Legacy LAPS
ms-Mcs-AdmPwd
2. Windows LAPS
msLAPS-Password / msLAPS-EncryptedPassword
3. SYSVOL / GPO references to PswReaders
4. Domain shares and application config
5. Other AD object ACLs
Your earlier SMB approach wasn’t very useful because you were looking locally. PswReaders is a domain group, and the interesting permission is very likely attached to an AD/domain resource rather than C:\Users\ted.
If the first LAPS count command returns something above 0, that’s the branch I would pursue first.
[adsisearcher] is usually available on standard Windows PowerShell 5.1 because it is a built-in PowerShell type accelerator for:
System.DirectoryServices.DirectorySearcher
So on a typical domain-joined Windows workstation/server, you normally don’t need to import anything:
$s = [adsisearcher]"(objectClass=user)"
$s.FindAll()
But it is not safe to assume it exists everywhere. The important cases are:
- Windows PowerShell 5.1: generally available by default.
- PowerShell 7 (
pwsh) on Windows: availability can depend on the .NET/System.DirectoryServices environment; don’t assume it blindly. - Linux/macOS PowerShell: ADSI/DirectoryServices functionality is not something you should expect to work like it does on Windows.
- Restricted PowerShell environments: Constrained Language Mode, AppLocker/WDAC, or other policy controls may limit what you can do.
- Even when
[adsisearcher]exists, it still needs network/DNS/LDAP access to a domain controller to enumerate AD.
You can check it locally without importing anything:
[adsisearcher]
or:
[adsisearcher]"(objectClass=*)"
For a more explicit check:
[System.Management.Automation.PSTypeName]'System.DirectoryServices.DirectorySearcher'
If you see a valid .Type, the underlying class is available.
You can also check the PowerShell version:
$PSVersionTable
For a pentest where you want maximum compatibility without dropping tools, I would prefer this pattern:
$searcher = New-Object System.DirectoryServices.DirectorySearcher
over relying solely on:
[adsisearcher]
because it’s clearer what underlying .NET class you’re using.
And [ADSI] is closely related but serves a slightly different purpose:
[ADSI]"LDAP://RootDSE"
A useful zero-import combination is therefore:
[ADSI]
[adsisearcher]
System.DirectoryServices.DirectorySearcher
With those alone, you can enumerate most LDAP-backed AD information such as users, groups, computers, SPNs, delegation attributes, GPO objects, and many ACL-related properties.