Auditing Logon Events in Windows
Posted on February 16, 2019
Every so often we’re asked to look into logon events to determine who logged into a device and when, be it a server or a PC. While there are commercial tools which can provide this information, they tend to be out of reach for smaller organisations leaving us with parsing the Security Event Log.
This post is designed to provide a crash course in what events are relevant, and a quick guide on what to look for.
The content below is specific to Server 2012 R2, and there are large differences in logging between 2008, 2012, and 2016 when it comes to RDP. There is a fantastic in depth write up at PureRDS.org on the topic.
Where do we look?
In both AD and non-domain environments, we need to look at the logs of the device in question - not the domain controllers. Specifically, we’re looking at the Security log,
Successful logon events
Here we’re looking for Event ID 4624
. However, this ID is also logged when other authentication events occur, such as a user connecting to a share on a file server - event 4624
would be logged on the file server.
The different types of logon events are identified with the Logon Type
field. There is a great article on the different logon types at Techgenix, but here is a quick reference for the types we’re interested in.
Type | Name | Description |
---|---|---|
2 | Interactive | These are the normal logon events you would expect to see when a user logs onto their PC, or onto the console session of a server |
3 | Network | These are logons such as accessing a share, IIS, etc. In specific circumstances type 3 will also be logged for RDP connections - more on this later |
7 | Unlock | The device was unlocked (i.e., screensaver with a password) |
10 | RemoteInteractive | This typically indicates a login via RDP, though with some exceptions described below |
There is a also official documentation at Microsoft.
Based on the above, if we’re looking for a user that logged onto a PC, or a server via the console session, we’re looking for Event ID 4624
, with a Logon Type of 2
.
If we’re also looking for RDP logons (typically to servers), we would be looking for Event ID 4624
, with a Logon Type of 10
.
It’s worth noting that this also gives us the IP address the user was connecting from, under the Network Information
heading.
If we only care about RDP logons, another log to check is Microsoft-Windows-TerminalServices-LocalSessionManager/Operational
. Here are RDP logons are logged, and in a much cleaner way than what is provided by the Security log.
We would be looking for Event ID 21
, logged by TerminalServices-LocalSessionManager
.
Incorrect username or password
When a user attempts to log on and gets the username or password wrong, this will be logged as an Audit Failure
with Event ID 4625
in the Logon
Task Category.
There is one caveat to this with RDP logons that use NLA (Network Layer Authentication). Failed logon attempts here are logged as Event ID 4624
, with a Logon Type of 3
.
Extracting relevant logon data
So now we know what we’re looking for, but short of clickity clacking through 100,000 Security event log entries all way to the RSI clinic, how do we extract the data we want?
Here are two examples, one using the Event Viewer, and one using PowerShell. We’ll be looking for Security Log Event Id 4624
with a Logon Type of 10
. Modifying the examples to meet your specific needs should be easy.
I won’t go into too much detail here as both of these could be their own standalone posts.
Using Event Viewer
Event logs can be represented as XML documents, if you view any log entry and select the Details tab, there will be an option to display the log in XML View. This is fantastic because it brings structure to our data and makes querying easy.
To create a filter using XML, select Filter Current Log, select the XML tab, tick the Edit Query Manually checkbox, and enter your query.
Here is a sample query for our current example:
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[(EventID=4624)] and EventData[Data[@Name='LogonType'] and Data=10]] </Select> </Query>
</QueryList>
The highlighted lines are the key elements. If you look at the XML representation of the event log, you’ll notice that the EventID
property is a child of System
, and that the LogonType
value is a Data
property under the EventData
root.
Some more information on XPath / XML queries can be found here.
Using PowerShell
Firstly, what not to do. Do not pull the entire event log and then use Where-Object
to filter the logs you want. This is the least performant way to achieve our goal.
What we’re going to do instead is write a hashtable filter that will return only what we need (as usual, with a caveat or two).
PowerSell 6+
PowerShell 6+ introduced <named-data>
keys which, if you’re fortunate enough to be running PowerShell 6+, makes you life much easier.
There is great example already on the linked page, but here would be our version of that.
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4624; 'LogonType'=10}
PowerShell 5.x and earlier
Using a Hashtable
Unfortunately <named-data>
is not available to us and there is no structured way to query exactly what we want using a hashtable. What I’ve found tends to work fairly well is to use the Data
key and, if we return more than what were after, at that point use Where-Object
to do our filtering.
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4624; Data=10}
Using XPath
I tend to prefer the above method as it usually gets me very close to what I want, and hashtables are easy compared to XML queries. But if we want accuracy with no further filtering required, we can go down the XPath route.
This is the same XML query format we used in the Event Viewer example.
Get-WinEvent -LogName Security -FilterXPath "*[System[(EventID=4624)] and EventData[Data[@Name='LogonType'] and Data=10]]"
That’s it for now, happy hunting!