Identify local admin accounts with null passwords using Powershell in a Win XP domain

I manage about 400 machines in a Win XP networked environment and would like to determine which ones have a null password on the local admin account. It's a simple enough matter to use a tool to reset all local admin passwords, but at this point I would like to build a case for changing our policies and would like to assess the extent of the problem first.

There is a similar bit of code posted here on experts exchange which verifies whether or not a certain password has been used, but I'm not sure how to customize for Powershell. The title is Script to Verify Local Admin Password.

The below code gives me a list of machines which I need to assess for null password. Once the list is built, I would like to use Export-CSV to build the report to send my boss, which should include machine name. If there is a password, I don't need to know or change it, I'm just after the nulls/zero length strings or whatever the blank password data type is, and I would prefer to use Powershell.
$strComputer = Read-Host "Enter the first 3 letters of the terminal. For all computers, just press enter."
$strFilter = "(&(objectCategory=Computer)(Name=$strComputer*))"
 
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
 
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Subtree"
 
$colProplist = "name"
foreach ($i in $colPropList){[void]$objSearcher.PropertiesToLoad.Add($i)}
 
$colResults = $objSearcher.FindAll()
 
$colResults | %{ $a = New-Object PSObject ; $a | Add-Member -MemberType NoteProperty -Name Name -Value $_.Properties.Item("Name")[0] ; $a } | Export-Csv -NoTypeInformation "C:\MachineList.csv"

Open in new window

LVL 1
gonealAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Chris DentPowerShell DeveloperCommented:

It is possible to test for and capture an error condition. You want those where no error condition exists, which the snippet below does.

I've got it echoing the results immediately, and storing them in a $Results object so they can be reviewed on completion (and any other error messages can be checked, account is locked out, for example).

HTH

Chris
$LocalAdministrator = "Administrator"
$PasswordToTest = ""
$Computer = "PartialComputerName
 
$LdapFilter = "(&(objectCategory=Computer)(name=$Computer*))"
 
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($Null, $LdapFilter)
$Searcher.PageSize = 1000
 
$PropertiesToLoad = @("name", "dNSHostName")
$Searcher.PropertiesToLoad.AddRange($PropertiesToLoad)
 
$Results = @()
$Searcher.FindAll() | %{
  # Variable that can be accessed within the Trap
  $Script:Exception = $Null
  # Capture any error
  Trap [Exception] {
    $Script:Exception = ($_.Exception.Message).Trim()
    Continue;
  }
 
  # Test binding to the Administrators group with the specified username and password
  [Void](New-Object System.DirectoryServices.DirectoryEntry(`
    "WinNT://$($_.Properties['dnshostname'])/Administrators, group", `
    "$($_.Properties['dnshostname'])\$LocalAdministrator", `
    $PasswordToTest, 0)).PsBase.Get_Name()
 
  # If no error is returned the the bind suceeded with this password
  If (!$Script:Exception) {
    Write-Host "$($_.Properties['dnshostname']): Successful bind using specified credentials"
    $Results += $_ | Select-Object @{n='HostName';e={ $_.Properties["dnshostname"]}}, `
      @{n='TestResult';e={ "Successful Bind" }}
  } Else {
    $Results += $_ | Select-Object @{n='HostName';e={ $_.Properties["dnshostname"]}}, `
      @{n='TestResult';e={ $Script:Exception }}
  }
}

Open in new window

0
Chris DentPowerShell DeveloperCommented:

Missed a " here:

$Computer = "PartialComputerName"

Sorry about that.

Chris
0
gonealAuthor Commented:
Thanks for all your work on this!
This looks to be pretty close...When I pass the first few letters of the machine names I am intersted in checking, in this case TAC, it writes a handful of them to the host with the expected "Successful bind using specified credentials".

I have approximately 80-100 machine names which begin with the digits TAC, and I know that there are more than just 4 or 5 with good credentials. When the script finishes executing, I can't echo to the host by entering $Results (it returns nothing) so I'm guessing the variable is empty. Would this mean that all machines pass the credentials test? If so, why were only a handful written to the host for viewing?

How should I modify the code so that each machine is reported with account status (whether binding with the credentials passed of failed) and then write the results to a CSV?
0
Top Threats of Q1 & How to Defend Against Them

WEBINAR: Join WatchGuard CTO and our Threat Research Team on Aug. 2nd to hear the findings from our Q1 Internet Security Report! Learn more about the top threats detected in the first quarter and how you can defend your business against them!

Chris DentPowerShell DeveloperCommented:

Hmm it should be writing all of them to the $Results array. I had it write both success and failure.

If you run this one it's own, does it return all of the machines we're interested in?

$Searcher.FindAll()

It's entirely possible the Trap is capturing too much, it would be worth running it without the Trap section to see if it errors for a large number of hosts.

Chris
0
gonealAuthor Commented:
When I run the code up to $Searcher.FindAll(), it returns 126 machines, when I run the entire routine, it returns 11 machines, all of which report success. The problem has got to be the trap, but I'm not sure how to rework it.
0
Chris DentPowerShell DeveloperCommented:

Lets run it without the Trap for now and see what it's returning to break it.

I'm sure it'll be that it's trapping too much. Ideally the trap should be more specific, I knew quick and easy would come back to bite ;)

Chris
$LocalAdministrator = "Administrator"
$PasswordToTest = ""
$Computer = "PartialComputerName
 
$LdapFilter = "(&(objectCategory=Computer)(name=$Computer*))"
 
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($Null, $LdapFilter)
$Searcher.PageSize = 1000
 
$PropertiesToLoad = @("name", "dNSHostName")
$Searcher.PropertiesToLoad.AddRange($PropertiesToLoad)
 
$Results = @()
$Searcher.FindAll() | %{
  # Test binding to the Administrators group with the specified username and password
  (New-Object System.DirectoryServices.DirectoryEntry(`
    "WinNT://$($_.Properties['dnshostname'])/Administrators, group", `
    "$($_.Properties['dnshostname'])\$LocalAdministrator", `
    $PasswordToTest, 0)).PsBase.Get_Name()
}

Open in new window

0
gonealAuthor Commented:
The code errors on nearly everything now - the only successful value it appears to return is the text Administrators, which I assume to be a reference to the Administrators account group. Below is the record of errors:

*****************************************************************************************************************
Exception calling "get_Name" with "0" argument(s): "Logon failure: unknown username or bad password.

Exception calling "get_Name" with "0" argument(s): "The netowrk path was not found.

Exception calling "get_Name" with "0" argument(s): "Access id denied.

Exception calling "get_Name" with "0" argument(s): "There are currently no logon servers available to service the request.
*****************************************************************************************************************

No machine names were returned.

When I run the code to line 14: $Searcher.FindAll()

I notice that a few of the results return {name, adspath} instead of {dnshostname, name, adspath}

Incidentally, I've been asked to test the reverse of the original scenario - accounts with the proper password instead of a blank/null password, so I've modified line 2: $PasswordToTest = ""

so that it includes the actual expected admin password, which I don't think effects your code, it just reverses the logic (sort of).

Thanks again for your help!
0
Chris DentPowerShell DeveloperCommented:

If the DNS Name is always the same we can just use the machine name. And this one will echo the computer name.

Still no trapping, want that back in?

Chris
$LocalAdministrator = "Administrator"
$PasswordToTest = ""
$Computer = "PartialComputerName
 
$LdapFilter = "(&(objectCategory=Computer)(name=$Computer*))"
 
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($Null, $LdapFilter)
$Searcher.PageSize = 1000
 
$PropertiesToLoad = @("name", "dNSHostName")
$Searcher.PropertiesToLoad.AddRange($PropertiesToLoad)
 
$Results = @()
$Searcher.FindAll() | %{
  # Echo the computer name
  Write-Host $_.Properties['name']
 
  # Test binding to the Administrators group with the specified username and password
  (New-Object System.DirectoryServices.DirectoryEntry(`
    "WinNT://$($_.Properties['name'])/Administrators, group", `
    "$($_.Properties['dnshostname'])\$LocalAdministrator", `
    $PasswordToTest, 0)).PsBase.Get_Name()
}

Open in new window

0
gonealAuthor Commented:
Still errors with same as above. Let's try the trap again.
0
Chris DentPowerShell DeveloperCommented:

Okie dokie.

This version works for me and generates the report properly (both when using invalid and valid credentials).  The result of each test is echoed on the fly as well.

Contemplating adding a Ping first to see if the host is up, otherwise "Network Path Not Found" takes a while to return.

Chris
$LocalAdministrator = "Administrator"
$PasswordToTest = ""
$Computer = "PartialComputerName"
 
$LdapFilter = "(&(objectCategory=Computer)(name=$Computer*))"
 
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($Null, $LdapFilter)
$Searcher.PageSize = 1000
 
$PropertiesToLoad = @("name")
$Searcher.PropertiesToLoad.AddRange($PropertiesToLoad)
 
$Results = @()
$Searcher.FindAll() | %{
  # Variable that can be accessed within the Trap
  $Script:Exception = $Null
  # Capture any error
  Trap [Exception] {
    $Script:Exception = ($_.Exception.Message).Trim()
    Continue;
  }
 
  # Test binding to the Administrators group with the specified username and password
  [Void](New-Object System.DirectoryServices.DirectoryEntry(`
    "WinNT://$($_.Properties['name'])/Administrators, group", `
    "$($_.Properties['name'])\$LocalAdministrator", `
    $PasswordToTest, 0)).PsBase.Get_Name()
 
  # If no error is returned the the bind suceeded with this password
  If (!$Script:Exception) {
    Write-Host "$($_.Properties['name']): Successful bind"
    $Results += $_ | Select-Object @{n='Name';e={ $_.Properties["name"]}}, `
      @{n='TestResult';e={ "Successful Bind" }}
  } Else {
    Write-Host "$($_.Properties['name']): Error binding - $($Script:Exception)"
    $Results += $_ | Select-Object @{n='Name';e={ $_.Properties["name"]}}, `
      @{n='TestResult';e={ $Script:Exception }}
  }
}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
gonealAuthor Commented:
That did the trick for me! The last piece I need is to be able to figure out how to position a pipeline for this so I can export the results to a CSV file for reporting.

Thanks again for all your help!
0
Chris DentPowerShell DeveloperCommented:

This should do, right at the end :)

$Results | Export-CSV "YourFile.csv"

Chris
0
gonealAuthor Commented:
Bingo! Again, thanks a million.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Powershell

From novice to tech pro — start learning today.