Those of you familiar with managing accounts in an Active Directory domain will know that if you set accounts to lockout, then you will regularly have helpdesk calls from users with locked out accounts. Some administrators have a policy not lock accounts and manage it slightly different, however this article is for those who do set account lockout thresholds.
The main places in my experience where account passwords are stored are listed below. Often a user has updated their password in the Directory and the password that has been stored will continue to authenticate with the previous password, eventually locking the user out.
-
Mapped network drives
-
RunAs shortcuts
-
Accounts that are used for service account logons
-
Processes on the client computers
-
Programs that may pass user credentials to a centralized network program or middle-tier application layer
-
Internet browser with stored credentials.
-
Terminal Services/Remote Desktop Sessions, including Citrix logons
-
Mobile phones or devices that authenticate such as ActiveSync devices or WIFI devices
There are tools provided by Microsoft to help resolve this problem, the tools are Account Lockout and Management Tools.
These tools are very useful for tracking down the source of the device that is causing the lockout and are required in order to run the script below, since it basically calls some of the tools in a more automated way. There is a full description on how to use these Account lockout tools. The first step here is to download these tools so that we can call them from our script.
The script below uses two of the tools in order to solve the problem, these are:
-
Lockoutstatus.exe
-
eventcombMT.exe
I also recommend the Quest Active Directory tools since a command like the following can easily provide you with a list of Locked out Accounts.
Get-QADUser -locked
One more thing prior to launching into the script is that the logfile report at the end contains the information we need to determine the where the lockout occurred. In my script I use some REGEX filters in order to parse the log files in order to find the information I need, you can alternatively view the whole log file by using the –FullLog switch.
#Requires -Version 2.0 [CmdletBinding()] Param ([Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [String]$Account, $HomeDir = "C:\PS\AccountLockout", $LogDir = "$HomeDir\Logs", $EventID = """529 644 675 676 681""", [Switch]$FullLog )#End Param # Call the LockoutStatus.exe with the Username, this presents the GUI and # it is a manual process to view and select the server with the first lockout $cmd = "cmd /c $Homedir\Tools\Lockoutstatus -u:$Account@NYUMC.org" Invoke-Expression "$cmd" $i = 0 $dom = [System.DirectoryServices.ActiveDirectory.Domain]::getcurrentdomain() $DCs = $dom.DomainControllers $DCs | ForEach-Object {$i++; write-Host "Select $i for -->" $_.Name} [Int]$Option = Read-Host "Please enter the server with lockout?" $Option -= 1 $Server = $DCs[$option].name $LogName = "$LogDir\$Server-Security_LOG.txt" # Call the eventcombMT.exe to parse the logs on the chosen server, # this also presents in the GUI, however does not need user input. $cmd = "cmd /c '$Homedir\Tools\eventcombMT.exe /s:$Server /et:safa /log:sec` /text:$Account /outdir:$LogDir /evt:$EventID /t:75 /start'" Invoke-Expression "$cmd" # After the process finished running the required information will be in # the log file We can now parse the logfile to view the offending device ID if (Test-Path $LogName) { if (!($FullLog)) { Get-Content $LogName | ForEach-Object {$_} | Where-Object {$_ -match "AUDIT FAILURE"} | Foreach {$_.Split(",")[5]} | ForEach-Object {[regex]::split($_, "\s\s\s\s\s")} | ForEach-Object {$_} | Where-Object {(($_ -match "\\") -or ($_ -match "Client Address:*") ` -or ($_ -match "User Name:"))} } else { get-content $LogName } } else {write-host "No event logs found matching selection" -BackgroundColor red}
Summary Info
Overall this script is very useful, however I believe it to be out dated. The main issue is that I am yet to come up with a more practical solution that I can automate end to end without any user input. This is however a task that I am interested in and will continue to look for a solution. I could simply parse ALL domain controller event logs, however this simply takes too long as compared to using the initial tool Lockoutstatus.exe to find the correct server straight out.
Besides the functional use of this script to check event logs for the source of locked out accounts, it is worthwhile mentioning that this script is actually just calling a COMMAND shell to do the work by running the EXE with the command line parameters.
As far as I am aware there are two main ways to do this (there is no doubt more ways):
Using Invoke-Expression PowerShell command, will run command from within PowerShell
$cmd = "cmd /c set" Invoke-Expression "$cmd"
Using .NET function to start a new Command Shell outside of PowerShell
[diagnostics.process]::start() $arguments = "/c set" [diagnostics.process]::start("cmd.exe", $arguments).waitforexit()
Next post I will most likely use this second method to call cmd.exe, since I am going to publish an article on using PowerShell to call the MBSA.exe, which is the Microsoft Baseline Security Analyser for running reports from Script.