The Powershell Diaries - Finding Problem User Accounts in AD
Powershell has gotten a lot of attention lately as a pentester's tool of choice, since it has access to pretty much every low-level system function in the Microsoft ecosystem, and the AV industry isn't dealing well with that yet (aside from ignoring powershell completely that is).
But what about day-to-day system administration? Really, the possibilities for admins are just as limitless as for pentesters - that's what Powershell was invented for after all !
A simple call like "get-aduser -filter * -properties * " can get you everything you want on domain user accounts. However, most sysadmins will look at this and give me the TLDR response - it's just too much information to process effectively.
But how about filtering that- let's find all users who aren't required to reset their passwords?
Or who don't have passwords at all?
How about have never reset their passwords (ie - haven't changed the initial password set at creation):
get-aduser -filter * -properties * | select samaccountname,passwordlastset
Operationally - let's add to the list - say folks who've had their accounts locked. This might be a "reset password on Friday, can't remember on Monday" symptom, but might also be someone brute forcing that account on the corporate website or VPN (hint - 2-factor authentication does wonders for those!)
get-aduser -filter * -properties * | select samaccountname,passwordlastset,lockedout
You can use the above to also find out who's left the organization. If you're like lots of IT groups, maybe HR isn't so timely in letting you know about departures! Let's dig to see who hasn't logged in in 4 weeks. 8 weeks? 12 weeks? Best call HR with this list in-hand to see if these folks are on longer term leave, or if they've moved on or maybe just stopped showing up for work?
get-aduser -filter * -properties * | select samaccountname,lastlogondate
At this point it becomes obvious that you want to sort these lists. You can go directly to a GUI view, where you can sort an play with the data as needed:
get-aduser -filter * -properties * | select samaccountname,passwordlastset | out-gridview
I find the CSV output, which can then be imported to excel - to be the most useful. If for regulatory (or other) reasons, you then need to save those files to demonstrate that you do audit yourself, and that you compare your audits to previous data, this can be a real help
The list that I use most often is below (change the field order as needed):
get-aduser -filter * -properties * | select samaccountname, name, enabled, scriptpath, passwordlastset, passwordexpired, passwordneverexpires, passwordnotrequired, lockedout, lastlogondate, cannotchangepassword, accountexiprationdate | export-csv "c:\pathspec\account-problems-yy-mm-dd.csv"
This imports directly into Excel (or any other spreadsheet), where you can slice and dice to your heart's content.
In closing, let me acknowledge Jason Fossen and SANS SEC 505 for re-kindling my enthusiasm for Powershell ! If you want to dig deeper into Powershell with a security slant, I'll be posting on this topic for a while, stay tuned. But if you want 6 days solid of concentrated powershell+windows goodness, take a look at SEC 505!
===============
Rob VandenBrink
Metafore
Comments
Anonymous
Jun 24th 2015
9 years ago
https://technet.microsoft.com/en-us/library/finding_users_whose_password_never_expires%28v=ws.10%29.aspx
I often use ADSI queries in a linked server through SQL Server instead of PS, mainly because we have some apps that use them, too, and it is quick and easy. There are some limitations with that approach (can't query multi-value attributes), but overall it works great. To find users with passwords that never expire in A/D using ADSI in SQL, I use:
SELECT
displayName AS DisplayName,
sAMAccountName AS Account
FROM OPENQUERY(ADSI,
'<LDAP://DC=YOUR,DC=FARM>;(&(objectCategory=Person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=65536)(!(userAccountControl:1.2.840.113556.1.4.803:=2)));
displayName,
sAMAccountName;subtree')
ORDER BY 1
The first ASN.1 "magic string" specifies accounts whose password doesn't expire, and the second one only brings back non-disabled accounts.
Anonymous
Jun 24th 2015
9 years ago
Anonymous
Jun 24th 2015
9 years ago
Maybe print all the reports to PDF for a non-technical auditor??
Anonymous
Jun 24th 2015
9 years ago
In any environment with multiple domain controllers you need to make sure that you query all domain controllers and use the most recent time stamps. Windows does not sync the times across controllers. Simply using "get-addomaincontroller -Filter *" will return your domain controllers. You will then want to write a script to find the most recent time stamps.
@bsdwiz
Anonymous
Jun 24th 2015
9 years ago
For instance, maybe you only care about enabled accounts so -filter "enabled -eq 'True'" would only find enabled accounts. The same could be done to only pull out accounts that the password never expires.
If the admin naming scheme is known
-filter "samaccountname -like '*admin*'"
or
-filter "samaccountname -like '*adm*'"
There is so much more that can be done. I have scripts used to enumerate all local admins on workstations across the enterprise as just one example.
Anonymous
Jun 24th 2015
9 years ago
Anonymous
Jun 25th 2015
9 years ago
Instead of -properties * you would use -properties passwordlastset,otherproperty1,otherproperty2
Anonymous
Jun 26th 2015
9 years ago
Get-ADUser -filter * -properties * -resultpagesize 0 -SearchBase "ou=users,ou=USOffice,dc=example,dc=com" | select samAccountName, Name, Enabled, ScriptPath, PasswordNeverExpires, PasswordNotRequired, LockedOut, CannotChangePassword, @{Name="PasswordLastSet"; Expression={$_.PasswordLastSet.ToString("u")}}, @{Name="PasswordExpired"; Expression={$_.PasswordExpired.ToString("u")}}, @{Name="AccountExpirationDate"; Expression={$_.AccountExpirationDate.ToString("u")}}, @{Name="lastLogonDate"; Expression={$_.lastLogonDate.ToString("u")}} | export-csv -notype c:\temp\yyyy-MM-dd_AccountData.csv
HTH,
Kurt
Anonymous
Jun 26th 2015
9 years ago
Specifying the prperties to be retrieved helps reduce the load:
$Properties = @(samaccountname, name, enabled, scriptpath, PasswordLastSet, PasswordExpired, passwordneverexpires,
passwordnotrequired, lockedout, lastlogondate, cannotchangepassword, accountexiprationdate)
get-aduser -filter * -properties $Properties |
select $Properties |
export-csv "c:\pathspec\account-problems-yy-mm-dd.csv"
You can create an array with the required property names and then reference that in the Get and in the Select.
Also, look at Search-ADAccount for a quick way to perform certain filtering.
https://technet.microsoft.com/en-us/library/ee617247.aspx
Anonymous
Jun 21st 2017
7 years ago