SEC504: Hacker Tools, Techniques, and Incident Handling

Experience SANS training through course previews.
Learn MoreLet us help.
Contact usConnect, learn, and share with other cybersecurity professionals
Engage, challenge, and network with fellow CISOs in this exclusive community of security leaders
Become a member for instant access to our free resources.
Sign UpMission-focused cybersecurity training for government, defense, and education
Explore industry-specific programming and customized training solutions
Sponsor a SANS event or research paper
We're here to help.
Contact UsJoin me as we answer the question: Where are all of the user properties for Active Directory users for Get-ADUSer?
In my first Month of PowerShell getting started article I talked about a common PowerShell process: running a cmdlet and piping the results to [code]Get-Member[/code] or [code]Select-Object -Property *[/code] to get a list of the object properties.
It's standard PowerShell practice, and I've done it hundreds of times throughout the Month of PowerShell. That is, until I started working with Active Directory user properties:
PS C:\Users\jwright> Get-ADUser -Identity jwright | Select-Object -Property *
DistinguishedName : CN=jwright,CN=Users,DC=falsimentis,DC=local
Enabled : True
GivenName :
Name : jwright
ObjectClass : user
ObjectGUID : f69b19e6-61ac-4cae-a7ac-ec88ec3bb564
SamAccountName : jwright
SID : S-1-5-21-1850091285-130397106-3068105436-500
Surname :
UserPrincipalName :
PropertyNames : {DistinguishedName, Enabled, GivenName, Name...}
AddedProperties : {}
RemovedProperties : {}
ModifiedProperties : {}
PropertyCount : 10
PS C:\Users\jwright>
Normally, I'd expect to see all of the properties for the AD User object, but lots of properties are missing. This is not consistent with other PowerShell cmdlet behavior.
Looking at the [code]Get-Help Get-ADUser[/code] output, there is an option [code]-Properties[/code] that accepts a string or list or strings, similar to what we see in the help for [code]Select-Object[/code]. When we use that option with a wildcard, we get all the AD user properties:
PS C:\Users\jwright> Get-ADUser -Identity jwright -Properties * | Select-Object -Property *
AccountExpirationDate :
accountExpires : 0
AccountLockoutTime :
AccountNotDelegated : False
AllowReversiblePasswordEncryption : False
AuthenticationPolicy : {}
AuthenticationPolicySilo : {}
BadLogonCount : 0
badPasswordTime : 0
badPwdCount : 0
CannotChangePassword : False
CanonicalName : falsimentis.local/Users/jwright
Certificates : {}
City :
CN : jwright
codePage : 0
Company :
CompoundIdentitySupported : {}
Country :
countryCode : 0
Created : 7/16/2022 7:08:49 PM
createTimeStamp : 7/16/2022 7:08:49 PM
Deleted :
Department :
Description : Built-in account for administering the computer/domain
DisplayName :
DistinguishedName : CN=jwright,CN=Users,DC=falsimentis,DC=local
Division :
DoesNotRequirePreAuth : False
dSCorePropagationData : {7/16/2022 7:09:34 PM, 1/1/1601 12:00:01 AM}
EmailAddress :
EmployeeID :
EmployeeNumber :
Enabled : True
Fax :
GivenName :
HomeDirectory :
HomedirRequired : False
HomeDrive :
HomePage :
HomePhone :
Initials :
instanceType : 4
isCriticalSystemObject : True
isDeleted :
KerberosEncryptionType : {}
LastBadPasswordAttempt :
LastKnownParent :
lastLogoff : 0
lastLogon : 133024722383629408
LastLogonDate : 7/16/2022 7:10:23 PM
lastLogonTimestamp : 133024722231721433
LockedOut : False
logonCount : 25
logonHours : {255, 255, 255, 255...}
LogonWorkstations :
Manager :
MemberOf : {CN=Group Policy Creator Owners,CN=Users,DC=falsimentis,DC=local, CN=Domain
Admins,CN=Users,DC=falsimentis,DC=local, CN=Enterprise
Admins,CN=Users,DC=falsimentis,DC=local, CN=Schema
Admins,CN=Users,DC=falsimentis,DC=local...}
MNSLogonAccount : False
MobilePhone :
Modified : 7/16/2022 7:10:23 PM
modifyTimeStamp : 7/16/2022 7:10:23 PM
msDS-User-Account-Control-Computed : 0
Name : jwright
nTSecurityDescriptor : System.DirectoryServices.ActiveDirectorySecurity
ObjectCategory : CN=Person,CN=Schema,CN=Configuration,DC=falsimentis,DC=local
ObjectClass : user
ObjectGUID : f69b19e6-61ac-4cae-a7ac-ec88ec3bb564
objectSid : S-1-5-21-1850091285-130397106-3068105436-500
Office :
OfficePhone :
Organization :
OtherName :
PasswordExpired : False
PasswordLastSet : 7/16/2022 6:42:54 PM
PasswordNeverExpires : False
PasswordNotRequired : False
POBox :
PostalCode :
PrimaryGroup : CN=Domain Users,CN=Users,DC=falsimentis,DC=local
primaryGroupID : 513
PrincipalsAllowedToDelegateToAccount : {}
ProfilePath :
ProtectedFromAccidentalDeletion : False
pwdLastSet : 133024705746844315
SamAccountName : jwright
sAMAccountType : 805306368
ScriptPath :
sDRightsEffective : 15
ServicePrincipalNames : {}
SID : S-1-5-21-1850091285-130397106-3068105436-500
SIDHistory : {}
SmartcardLogonRequired : False
State :
StreetAddress :
Surname :
Title :
TrustedForDelegation : False
TrustedToAuthForDelegation : False
UseDESKeyOnly : False
userAccountControl : 512
userCertificate : {}
UserPrincipalName :
uSNChanged : 12579
uSNCreated : 8196
whenChanged : 7/16/2022 7:10:23 PM
whenCreated : 7/16/2022 7:08:49 PM
PropertyNames : {AccountExpirationDate, accountExpires, AccountLockoutTime,
AccountNotDelegated...}
AddedProperties : {}
RemovedProperties : {}
ModifiedProperties : {}
PropertyCount : 107
The question is, why? Why does [code]Get-ADUser[/code] break from convention like other PowerShell cmdlets and not send all of the available properties to the next command in the pipeline? I think I found the answer in Mike Robbins's PowerShell 101:
This seems like a reasonable assertion for why, and it's an important precedent for PowerShell users to keep in mind: PowerShell cmdlets may not include all available properties by default.
How do you know if the absence of a property you want to access (e.g., the parent process ID in [code]Get-Process[/code]) is available but omitted by default, or if it's not available at all? The only reasonable way to know is to look at the [code]Get-Help[/code] output for a given cmdlet, and see if there is an option for [code]-Properties[/code], and experiment with [code]CMDLETNAME -Properties * | Select-Object -Property *[/code].
In practice, you will rarely need all of the properties from any cmdlet in the pipeline. This brings us to the lesson Blake Regan @crash0ver1d3 tried to tell me a few weeks ago but I wasn't ready to listen yet:
For example, if I want to build a CSV file of all domain users that includes the name, SID, password expired (Boolean), and password last set (date) elements, I can run this command:
PS C:\Users\jwright> Get-ADUser -Filter * -Properties * | Select-Object -Property Name, SID, PasswordExpired, PasswordLastSet | ConvertTo-Csv | Out-File userpassset.csv
PS C:\Users\jwright>
However, this creates an unnecessarily large collection of user objects. It might not be noticeable with tens or hundreds of users, but on a domain with hundreds of thousands of users this command will create a lot of unnecessary overhead. Here's a better solution:
PS C:\Users\jwright> Get-ADUser -Filter * -Properties Name, SID, PasswordExpired, PasswordLastSet | Select-Object -Property Name, SID, PasswordExpired, PasswordLastSet | ConvertTo-Csv | Out-File userpassset.csv
PS C:\Users\jwright> Get-Content -first 3 .\userpassset.csv
#TYPE Selected.Microsoft.ActiveDirectory.Management.ADUser
"Name","SID","PasswordExpired","PasswordLastSet"
"jwright","S-1-5-21-1850091285-130397106-3068105436-500","False","7/16/2022 6:42:54 PM"
PS C:\Users\jwright>
It's a little redundant to specify the object properties twice (and don't get me started on the inconsistent use of [code]Get-ADUSer -Properties[/code] and [code]Select-Object -Property[/code]), but it improves performance without a lot of added effort.
If you have other questions about PowerShell things, let me know! Reach out on Twitter (my DMs are open), or email josh@willhackforsushi.com.
Thank you for reading!
-Joshua Wright
Return to Getting Started With PowerShell
Joshua Wright is the author of SANS SEC504: Hacker Tools, Techniques, and Incident Handling, a faculty fellow for the SANS Institute, and a senior technical director at Counter Hack.
As Senior Technical Director at Counter Hack and SANS Faculty Fellow, Joshua has advanced cybersecurity through ethical penetration testing, uncovering critical vulnerabilities across Fortune 500 companies and national infrastructure providers.
Read more about Joshua Wright