
In a true scenario, we got from a pentest report, that to many of our servers had local active accounts that were local administrators. To mitigate this, we planned to do the following:
- Delete all accounts except the default administrator (Disable default on 2016)
- Rename the default to a different name
- Set a new ‘impossible’ password that nobody knows (45chrs)
- Leave as-is domain users in the local administrators group
Simple enough if done on one server, via the Windows GUI…but given the circumstanses, having about 100 Windows servers, we decided to do it using PowerShell and to run the script from the Azure portals ‘Run command’ feature (Recommended). Both can however also be used locally on the servers.
What differs in the two versions are, if the servers are running 2016 or later, or 2012R2 or earlier. We had both so we needed two scripts.
(Apologies for the bad formatting in this blog-template)
Windows Server 2016 and later:
# Delete all local admin but default, rename it to Osadmin and reset pwd to 45 chrs random string
$NewAdminName = "OSAdmin"
$Admins = Get-LocalGroupMember -Group 'Administrators' | Select-Object ObjectClass, Name, PrincipalSource | Where-Object {$_.PrincipalSource -eq "Local"} | Select-Object Name
$DefaultAdmin = (Get-WmiObject Win32_UserAccount -filter "LocalAccount=True" | ? {$_.SID -like "S-1-5-21-*-500"}).Name
foreach($Admin in $Admins) {
$UserName = ($Admin.Name).ToString().split("\")[1]
Disable-LocalUser $UserName
If ($UserName -ne $DefaultAdmin) {
Remove-LocalUser $UserName
Write-Host "Removed Admin: $UserName"
}
else {
$Random = ConvertTo-SecureString ((1..45 | ForEach-Object {('abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!"§$%&/()=?}][{@#*+')[(Get-Random -Maximum ('abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!"§$%&/()=?}][{@#*+').length)]}) -join "") -AsPlainText -Force
Set-LocalUser -Name $DefaultAdmin -Password $Random
Write-Host "Default Admin password reset to 45 chrs long random string"
if ($DefaultAdmin -ne $NewAdminName){
Rename-LocalUser -Name $DefaultAdmin -NewName $NewAdminName
Write-Host "Renamed default Admin: $UserName to $NewAdminName"
}
}
}
Write-host "Done - Server secured :-)"
– – – – – – – – – – – – – – – – – – – –
Pre Windows Server 2016:
# Delete all local admin but default, rename it to Osadmin and reset pwd to 45 chrs random string
$NewAdminName = "OSAdmin"
$LocalAdmins = (get-wmiobject -ComputerName $Env:Computername win32_group -filter "name='Administrators' AND LocalAccount='True'").GetRelated("win32_useraccount")
foreach ($LocalUser in $LocalAdmins){
$UserName = $LocalUser.Name
$UserSID = $LocalUser.SID
$userDomain = $LocalUser.Domain
if ($LocalUser.Domain -eq $Env:Computername)
{
If ($userSID -like "S-1-5-21-*-500"){
Write-Host "OK Default: $userName $userDomain" -ForegroundColor Green
If ($UserName -ne $NewAdminName){
$LocalUser.Rename($NewAdminName)
}
$Random = (1..45 | ForEach-Object {('abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!"§$%&/()=?}][{@#*+')[(Get-Random -Maximum ('abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!"§$%&/()=?}][{@#*+').length)]}) -join ""
[adsi]$User = "WinNT://./$NewAdminName,user"
$User.SetPassword($Random)
$User.SetInfo()
}
else {Write-Host "DELETE: $userName $userDomain" -ForegroundColor Red
[ADSI]$server = "WinNT://$Env:computername"
$server.delete("user",$UserName)
}
}
else {Write-Host "OK Domain: $userName $userDomain" -ForegroundColor Green}
}
Happy PowerShell scripting!
References
Not this one

___________________________________________________________________________________________________
Enjoy!
Regards

Thomas Odell Balkeståhl on LinkedIn