Tech: Identifying privileged users within AD
Table of Contents
I regularly come across two pitfalls when trying to identify all privileged users within a Windows domain.
First, privileged users are not all Domain Admins. The list of “Privileged Accounts and Groups” is large, and the list of standard “Security Groups” is even longer, but reporting on the following already does a pretty good job – and gives a much better view than extracting Domain Admins alone:
- Administrators
- Domain Admins
- Enterprise Admins
- Schema Admins
- Backup Operators
- Account Operators
- Network Config Ops
Second, the Domain Admins group isn’t (always) called Domain Admins. Scripts like these ones are basically extracting Domain Admins through a command like:
Get-ADGroupMember -Identity "Domain Admins"
But that doesn’t work in a global context, where these display names are translated by Microsoft – and most scripts therefore crash on French or Spanish Active Directories.
It’s much more reliable to address them through their “Well-Known SID/RID”, for instance S-1-5-<domain>-512
for Domain Admins.
That’s how it looks #
The following function collects all privileged users (based on the abovementioned privileged groups), and extracts their access level. Just as most AD scripts, it works with any normal user account (yes, an intern’s account is perfectly sufficient to extract detailed information from AD).
And it works with StrictMode
!
function Get-Admins {
# Get all groups
# There's no way to search for a partial SID https://stackoverflow.com/questions/45244354/get-adgroup-filter-sid-like-512
# So we need to extract all groups
$AllADGroups = Get-ADGroup -filter *
# Get all SIDs/DNs of privileged groups
# https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/appendix-b--privileged-accounts-and-groups-in-active-directory
# http://techgenix.com/well-known-sids-windows-server-2008-r2-active-directory/
# At this time we're ignoring:
# DHCP Administrators
# DNSAdmins
# Event Log Readers
# Hyper-V Administrators (WS 2012)
# Pre-Windows 2000 Compatible Access
# Print Operators
# WinRMRemoteWMIUsers_ (WS 2012)
$list = [ordered]@{
"Administrators" = $(try { ($AllADGroups | Where-Object { $_.SID -eq "S-1-5-32-544"}).DistinguishedName} catch { "" })
"Domain Admins" = $(try { ($AllADGroups | Where-Object { $_.SID -like "*-512"}).DistinguishedName } catch { "" }) # https://msdn.microsoft.com/en-us/library/cc223718.aspx
"Enterprise Admins" = $(try { ($AllADGroups | Where-Object { $_.SID -like "*-519"}).DistinguishedName } catch { "" }) # https://msdn.microsoft.com/en-us/library/cc223710.aspx
"Schema Admins" = $(try { ($AllADGroups | Where-Object { $_.SID -like "*-518"}).DistinguishedName } catch { "" }) # https://msdn.microsoft.com/en-us/library/cc223714.aspx
"Backup Operators" = $(try { ($AllADGroups | Where-Object { $_.SID -like "*-551"}).DistinguishedName } catch { "" })
"Account Operators" = $(try { ($AllADGroups | Where-Object { $_.SID -like "*-548"}).DistinguishedName } catch { "" })
"Network Config Ops" = $(try { ($AllADGroups | Where-Object { $_.SID -like "*-556"}).DistinguishedName } catch { "" })
}
$PrivilegedSIDs = New-Object PSObject -property $list
# Get all admins in privileged groups (including recursive/n-th level members)
$PrivilegedGroupMembers = New-Object PSObject;
$PrivilegedSIDs.PSObject.Properties | Foreach-Object {
if ($_.Value -ne "") {
$listOfUsers = Get-ADGroupMember -recursive -identity $_.Value;
if (($listOfUsers | Measure-Object).count -ne 0) { $PrivilegedGroupMembers | Add-Member -MemberType NoteProperty -Name $_.Name -Value $listOfUsers }
}
}
# So the list of all admins is the list of all members of all privileged groups (-unique)
$PrivilegedUsers = @();
Foreach ($user in $PrivilegedGroupMembers.PSObject.Properties.value) { $PrivilegedUsers += $user }
$PrivilegedUsers = $PrivilegedUsers | Select-Object -Unique distinguishedname
# The following command just adds some information about each user account
# Feel free to adapt it to extract additional information (password last change, for instance)
$PrivilegedUsersWithDetails = Get-ADUser -filter {Enabled -eq $True} -properties description | Where-Object { $_.distinguishedname -in $PrivilegedUsers.distinguishedname }
$PrivilegedUsersWithDetails = $PrivilegedUsersWithDetails | Select-Object sAMAccountName,distinguishedname,Description, `
@{ Name = "Administrators"; Expression = {"" }}, `
@{ Name = "Domain Admins"; Expression = {"" }} , `
@{ Name = "Enterprise Admins"; Expression = {"" }}, `
@{ Name = "Schema Admins"; Expression = {"" }}, `
@{ Name = "Backup Operators"; Expression = {"" }}, `
@{ Name = "Account Operators"; Expression = {"" }}, `
@{ Name = "Network Config Ops"; Expression = {"" }}, `
@{ Name = "Privileged Groups"; Expression = {"" }}
# This populates the columns "Domain Admins" (etc.) with "Yes" when the user is member of this group.
$PrivilegedUsersWithDetails = $PrivilegedUsersWithDetails | Foreach-Object {
# an "Admin" is someone that's at least in one of the groups mentioned above
$currentuser = $_.distinguishedname;
Foreach ($groupname in $PrivilegedGroupMembers.PSObject.Properties.Name) {
if ($currentuser -in $PrivilegedGroupMembers.$groupname.distinguishedname) {
$_.$groupname = "Yes" ;
if ($_."Privileged Groups" -ne "") { $_."Privileged Groups" += ", " }
$_."Privileged Groups" += $groupname;
}
}
return $_
}
return $PrivilegedUsersWithDetails
}