How To Find Failed Logon Attempts in PowerShell
December 5, 2022
Anyone who has ever sifted through the Windows Event Viewer knows just how many log entries Windows tends to generate. It’s easy to overlook something important.
While there are certainly good third-party tools that can help with this, you can also use PowerShell to track down event log entries. The advantage to using PowerShell for such tasks is that not only does it make it easier to find exactly what you are looking for, but you can even script actions that will run if certain events have occurred.
In this article, I want to show you how to use PowerShell to find failed logon attempts.
Before I Get Started
There are two things to note. First, even though I will focus on failed logons, the technique that I discuss can be adapted to work with any event log entry. Second, this technique is not designed for real-time detection of failed logons. Instead, it’s primarily designed to keep you aware of what is going on within your network. Again, there are some good third-party security tools that can alert you to failed logons in real time.
How To Search for Failed Logon Attempts
The Windows event logs assign an Event ID to each type of event that is logged. The Event ID used for a failed logon attempt is 4625. Similarly, Event ID 4624 reflects a successful logon. That being the case, the way to go about tracking down failed logons is to build a simple PowerShell script that searches the event logs for instances of Event ID 4625.
At its simplest, such a script might look like this:
Get-WinEvent -FilterHashTable @{LogName="Security"; ID=4625} | Select-Object TimeCreated, Message | Format-Table -Wrap
Get-WinEvent is the PowerShell cmdlet used to retrieve the Windows event logs. The trick to extracting information from the logs is to treat them as a hash table. Doing so gives you the ability to filter log entries on an as-needed basis. In this case, we are using the LogName=”Security” filter to make it so that only events from the Windows Security logs are returned. We are also using the ID=4625 filter to ensure that only instances of Event ID 4625 are returned.
As you can imagine, there can be a lot of information associated with an event log entry. For a 4625 event, the logs contain information such as the ID associated with the thread and process that triggered the event. You can also see the name of the computer, the user ID, the time when the event was created, and more.
If you look back at the command that I used, you can see that I used the Select-Object cmdlet to display the time when the event was created and the event message. While it might seem that limiting the output in this way would diminish the value of the results, the message that is associated with Event 4625 contains a lot of information. In fact, the sheer volume of information that is being produced is the reason why the command ends in Format-Table -Wrap. Without this command, the output would be truncated. You would see that a logon failure had occurred, but the message wouldn’t tell you anything useful. You can see an example of this in Figure 1.
Failed logon 1
Figure 1. This is what happens if you do not wrap the output.
In contrast, appending Format-Table -Wrap causes PowerShell to produce very verbose information that may be highly useful. You can see a portion of that output in Figure 2.
Failed logon 2
Figure 2. PowerShell can provide a wealth of information about failed logon attempts.
In a production environment, it is entirely possible that you will have a large number of 4625 events, so it may be useful to adjust your filter to reduce the number of results and zero in on what you are truly interested in. Figure 3 shows a list of the properties that you can filter by (excluding the message). In case you are wondering how I got this list, I changed the Select-Object portion of the command to say Select-Object *, thereby revealing all the log properties.
Failed logon 3
Figure 3. These are some of the properties that you can use in your filter.
You can use any of these attributes as filters. For example, if you wanted to filter by machine name, you would modify the command to look like this:
Get-WinEvent -FilterHashTable @{LogName="Security"; ID=4625; MachineName=”Win-CFVP7ABN07K”} | Select-Object TimeCreated, Message | Format-Table -Wrap
Keep in mind that I am referencing a specific machine name, so you should substitute the machine name that you want to query. Similarly, you can filter the output by date. If you want to see all the events that have occurred in the last day, you could use this command:
$Start=(Get-Date).AddDays(-1)Get-WinEvent -FilterHashTable @{LogName="Security"; ID=4625;StartTime=$Start} | Select-Object TimeCreated, Message | Format-Table -Wrap
About the Author
You May Also Like