Local Administrator Audit Script

3

In a large environment, correctly managing Privileged Access can be complicated and complex. Especially in a Windows Server environment, administrators can easily add other users and groups to the server’s Local Administrator group. This can lead to privilege escalation, privilege abuse, and data loss. So it’s important to monitor local administrators on servers. I put together this script to audit local administrator members across all the servers.

Goals

The goals I had for this script were as follows:

  • The script can be run from one central location and audit all servers in the domain.
  • One output that could be parsed at any time for rogue accounts.
  • As dynamic as possible.

Powershell Script

This audit script is fairly simple. There’s an array of users and group names that are whitelisted because they are valid administrator accounts and groups.

We pull the server machine names from Active Directory and then pull each server’s Local Administrator members using Get-NetLocalGroup. We take the output from that and parse it in Check-Members. Any unexpected accounts that are members of the group are logged with a warning.

1### Configuration ###
2$Logdir = "C:\scripts\logs\"
3$Logfile = "LocalAdmin-Audit-$(get-date -Format FileDateTimeUniversal).log"
4$Logfile = Join-Path $Logdir $Logfile
5$Tab = [char]9
6 
7# Whitelist Users and Groups
8$validadmins = "LOCALUS\Domain Admins", "Domain Admins", "Enterprise Admins", "LOCALUS\Server-Local-Admins", "LOCALUS\SVC_DEPLOY", "LOCALUS\SVC_CS_Admin", "LOCALUS\SVC_SQL", "LOCALUS\SVC_Manager", "LOCALUS\Web_Admin", "LOCALUS\SVC_SQL_AGENT"
9 
10### Functions ###
11Function LogWrite
12{
13   Param ([string]$logstring)
14 
15   if (!(Test-Path $Logdir)) { New-Item -ItemType Directory -Force -Path $Logdir}
16 
17   $d = Get-Date -Format “dd/MM/yyyy HH:mm:ss”
18   $logline = "$d $Tab $logstring"
19 
20   Add-content $Logfile -value $logline
21}
22 
23Function Get-NetLocalGroup {
24[cmdletbinding()]
25 
26Param(
27[Parameter(Position=0)]
28[ValidateNotNullorEmpty()]
29[object[]]$Computername=$env:computername,
30[ValidateNotNullorEmpty()]
31[string]$Group = "Administrators",
32[switch]$Asjob
33)
34 
35Write-Verbose "Getting members of local group $Group"
36 
37#define the scriptblock
38$sb = {
39 Param([string]$Name = "Administrators")
40$members = net localgroup $Name |
41 where {$_ -AND $_ -notmatch "command completed successfully"} |
42 select -skip 4
43New-Object PSObject -Property @{
44 Computername = $env:COMPUTERNAME
45 Group = $Name
46 Members=$members
47 }
48} #end scriptblock
49 
50#define a parameter hash table for splatting
51$paramhash = @{
52 Scriptblock = $sb
53 HideComputername=$True
54 ArgumentList=$Group
55 }
56 
57if ($Computername[0] -is [management.automation.runspaces.pssession]) {
58    $paramhash.Add("Session",$Computername)
59}
60else {
61    $paramhash.Add("Computername",$Computername)
62}
63 
64if ($asjob) {
65    Write-Verbose "Running as job"
66    $paramhash.Add("AsJob",$True)
67}
68 
69#run the command
70Invoke-Command @paramhash | Select * -ExcludeProperty RunspaceID
71 
72} #end Get-NetLocalGroup
73 
74function Check-Members($admingroup){
75 
76$h = $admingroup.Computername
77$m = $admingroup.Members
78 
79Write-Host "Admins for $h"
80foreach ($m1 in $m)
81{   
82     if($validadmins -contains $m1){
83        Write-Host $m1 -ForegroundColor Green
84     }else{
85        if ($m1 -match "-Admin") { Write-Host $m1 -ForegroundColor Green } else {
86        Write-Host $m1 -ForegroundColor Yellow
87        LogWrite "$h $Tab WARNING $Tab Unexpected Admin Account found - $m1"
88        }
89     }
90}
91}
92 
93### Main ###
94$servers = Get-ADComputer -Filter *
95 
96foreach ($s in $servers)
97{
98    $server = $s.DNSHostName
99 
100    $ping = Test-Connection $server -Count 1 -Quiet
101 
102    LogWrite "$server $Tab Checking $server"
103 
104    if ($ping){
105        Write-Host "         $server       " -BackgroundColor White -ForegroundColor DarkBlue
106 
107        $admins = Get-NetLocalGroup $server
108        Check-Members $admins
109    }else{
110        Write-Warning "Could not connect to $server"
111        LogWrite "$server $Tab Could not connect to $server"
112    }
113    Write-Host "                                    " -BackgroundColor White -ForegroundColor DarkBlue
114}
115 
116### End Script ###

The script will generate a log output for every run. Servers that can not be contacted will be logged. Unexpected Admin accounts are logged as a warning.

09/11/2019 02:58:20      SERVERSSI01     Checking  SERVERSSI01
09/11/2019 02:58:20      SERVERAVS02     Checking SERVERAVS02
09/11/2019 02:58:21      SERVERAVS02     WARNING     Unexpected Admin Account found - LOCALUS\joel.robinson
09/11/2019 02:58:21      SERVERANL01     Checking SERVERANL01
09/11/2019 02:58:25      SERVERAPP02     Checking SERVERAPP02
09/11/2019 02:58:25      SERVERAPP02     Could not connect to SERVERAPP02
09/11/2019 02:58:29      SERVERSQL01     Checking SERVERSQL01

You could load this output into your SIEM, or configure the script to log directly to a SIEM or other data lake.

References

The Get-NetLocalGroup function was originally published on powershell.org in 2013.

My New Stories

March 2016 Web Hosting Deals
Powershell AD Group Management
Troubleshooting 403