Finding remote or local login events and types using PowerShell
Does anyone actually like scrolling through the Event Viewer? Well the filtering is nice, but why make yourself do all that work? Besides, what is login type 7
or how do you filter for specific users? Let me show you in PowerShell (no idea if that wink will actually show up or just be :wink:
)
Pop quiz
Ok, quick, riddle me these:
- What is the event ID for logins?
- What event log do login events land in?
- What types of logins are there and what are their types?
If you’re answer was, let me look it up, then great! You’ve got too many other to worry about to try to memorize this stuff.
But, real quick, since we are going to cover this, here are the answers that we’ll need in pulling events from the event log:
- Event ID for logins: 4624 (Since Vista)
- Event log: Security
- Logon types:
Logon Type | Logon Title | Description |
---|---|---|
2 | Interactive | A user logged on to this computer. |
3 | Network | A user or computer logged on to this computer from the network. |
4 | Batch | Batch logon type is used by batch servers, where processes may be executing on behalf of a user without their direct intervention. |
5 | Service | A service was started by the Service Control Manager. |
7 | Unlock | This workstation was unlocked. |
8 | NetworkCleartext | A user logged on to this computer from the network. The user’s password was passed to the authentication package in its unhashed form. The built-in authentication packages all hash credentials before sending them across the network. The credentials do not traverse the network in plaintext (also called cleartext). |
9 | NewCredentials | A caller cloned its current token and specified new credentials for outbound connections. The new logon session has the same local identity, but uses different credentials for other network connections. |
10 | RemoteInteractive | A user logged on to this computer remotely using Terminal Services or Remote Desktop. |
11 | CachedInteractive | A user logged on to this computer with network credentials that were stored locally on the computer. The domain controller was not contacted to verify the credentials. |
To build a tool or not to build a tool…
That is a dumb question!
Get-WinEvent refresher
If you remember from tracking down lockouts or even tracking down bad password attempts, then you should know about Get-WinEvent
. You’ve hopefully also picked up that we can use this cmdlet to write TONS of useful functions for pulling information about a computer!
So just a quick refresher on the base command we’ll be using:
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624
}
This will return all events from the Security event log that have an ID of 4624. And, just as I was reminded of when I tested that command, you need to be running as an administrator to access the Security logs.
Dealing with the data
When you run that command, you’ll notice that you get a large number of entries. I got 55 in the last hour, one of which was me logging into my computer and another one was me unlocking my computer after going to the bathroom. But that doesn’t account for the 53 other events, so lets turn that into some useful data to find out.
Properties
So like my previous posts on getting specific data from the event logs, we are going to take a look at the properties of the event object that is returned. This is so similar to before, I’m not going to go in depth again, I’ll just show you what it looks like:
Here is the message
An account was successfully logged on.
Subject:
Security ID: S-1-5-18
Account Name: ALPHAWOLF$
Account Domain: HOWELLIT
Logon ID: 0x3E7
Logon Information:
Logon Type: 2
Restricted Admin Mode: -
Virtual Account: No
Elevated Token: No
Impersonation Level: Impersonation
New Logon:
Security ID: S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXX-XXXX
Account Name: Anthony
Account Domain: ALPHAWOLF
Logon ID: 0xBCB48FD
Linked Logon ID: 0xBCB48E0
Network Account Name: -
Network Account Domain: -
Logon GUID: {00000000-0000-0000-0000-000000000000}
Process Information:
Process ID: 0xb58
Process Name: C:\Windows\System32\svchost.exe
Network Information:
Workstation Name: ALPHAWOLF
Source Network Address: 127.0.0.1
Source Port: 0
Detailed Authentication Information:
Logon Process: User32
Authentication Package: Negotiate
Transited Services: -
Package Name (NTLM only): -
Key Length: 0
This event is generated when a logon session is created. It is generated on the computer that was accessed.
The subject fields indicate the account on the local system which requested the logon. This is most commonly a service such as the Server service, or a local process such as Winlogon.exe or Services.exe.
The logon type field indicates the kind of logon that occurred. The most common types are 2 (interactive) and 3 (network).
The New Logon fields indicate the account for whom the new logon was created, i.e. the account that was logged on.
The network fields indicate where a remote logon request originated. Workstation name is not always available and may be left blank in some cases.
The impersonation level field indicates the extent to which a process in the logon session can impersonate.
The authentication information fields provide detailed information about this specific logon request.
- Logon GUID is a unique identifier that can be used to correlate this event with a KDC event.
- Transited services indicate which intermediate services have participated in this logon request.
- Package name indicates which sub-protocol was used among the NTLM protocols.
- Key length indicates the length of the generated session key. This will be 0 if no session key was requested.
You’ll notice a lot of good information there, but, spoiler alert, the easiest to access is in the Properties
property (unless you want to get into XPath, check out Mathias Jessen’s post):
Value
-----
S-1-5-18
ALPHAWOLF$
HOWELLIT
999
S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXX-XXXX
Anthony
ALPHAWOLF
197871869
2
User32
Negotiate
ALPHAWOLF
00000000-0000-0000-0000-000000000000
-
-
0
2904
C:\Windows\System32\svchost.exe
127.0.0.1
0
%%1833
-
-
-
%%1843
197871840
%%1843
So we can do the same thing as before and create a custom object from that data. And we can even convert the logon type from a number to the actual type so you don’t have to reference the table every time!
Logon types
If you remember from by Get-ADUserBadPasswords
post, I used a hashtable to match logon types to definitions. That works, but an enum
is easier to read, so we’ll use that this time:
enum LogonTypes {
Interactive = 2
Network = 3
Batch = 4
Service = 5
Unlock = 7
NetworkClearText = 8
NewCredentials = 9
RemoteInteractive = 10
CachedInteractive = 11
}
And once we have that enum, we can use it to determine the logon types (Notice how clean this is compared to using a hashtable?):
PS> [LogonTypes]2
Interactive
Objectifying the event
So with all of that information, we can grab all of the events:
$events = Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624
}
Loop through them and create an output object:
foreach ($event in $events) {
[pscustomobject]@{
UserAccount = $event.Properties.Value[5]
UserDomain = $event.Properties.Value[6]
LogonType = [LogonType]$event.Properties.Value[8]
WorkstationName = $event.Properties.Value[11]
SourceNetworkAddress = $event.Properties.Value[19]
}
}
Writing the function
Awesome! We made it. Lets make this into a function!
Function Get-LoginEvents {
Param (
[Parameter(
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias('Name')]
[string]$ComputerName = $env:ComputerName
,
[datetime]$StartTime
,
[datetime]$EndTime
)
Begin {
enum LogonTypes {
Interactive = 2
Network = 3
Batch = 4
Service = 5
Unlock = 7
NetworkClearText = 8
NewCredentials = 9
RemoteInteractive = 10
CachedInteractive = 11
}
$filterHt = @{
LogName = 'Security'
ID = 4624
}
if ($PSBoundParameters.ContainsKey('StartTime')){
$filterHt['StartTime'] = $StartTime
}
if ($PSBoundParameters.ContainsKey('EndTime')){
$filterHt['EndTime'] = $EndTime
}
}
Process {
Get-WinEvent -ComputerName $ComputerName -FilterHashtable $filterHt | foreach-Object {
[pscustomobject]@{
ComputerName = $ComputerName
UserAccount = $_.Properties.Value[5]
UserDomain = $_.Properties.Value[6]
LogonType = [LogonTypes]$_.Properties.Value[8]
WorkstationName = $_.Properties.Value[11]
SourceNetworkAddress = $_.Properties.Value[19]
TimeStamp = $_.TimeCreated
}
}
}
End{}
}
Something you should take note of is that in this function I went from:
$events = Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624
}
foreach ($event in $events) {
[pscustomobject]@{
ComputerName = $ComputerName
UserAccount = $event.Properties.Value[5]
UserDomain = $event.Properties.Value[6]
LogonType = [LogonType]$event.Properties.Value[8]
WorkstationName = $event.Properties.Value[11]
SourceNetworkAddress = $event.Properties.Value[19]
}
}
To using:
Get-WinEvent -ComputerName $ComputerName -FilterHashtable $filterHt | foreach-Object {
[pscustomobject]@{
ComputerName = $ComputerName
UserAccount = $_.Properties.Value[5]
UserDomain = $_.Properties.Value[6]
LogonType = [LogonTypes]$_.Properties.Value[8]
WorkstationName = $_.Properties.Value[11]
SourceNetworkAddress = $_.Properties.Value[19]
}
}
This is because the second method leverages the pipeline to immediately start receiving output instead of enumerating all the objects and then returning them one by one. So the second method is faster if you are running the command from an interactive prompt. Be sure to test it out yourself if you don’t believe me!
Usage
The simplest usage is just running the cmdlet naked:
Get-LoginEvents
This will return and parse all login security events on your local system. This could be quite a few! So I encourage you to use the date filtering:
Get-LoginEvents -StartTime (Get-Date).AddHours(-1)
Or even pipe in to Where-Object
to filter for certain types of logons:
Get-LoginEvents -StartTime (Get-Date).AddDays(-1) | ? LogonType -eq 'Interactive'
And since we did enable pipeline input, you can pass data from a string array:
Get-Content C:\path\to\computers.txt | Get-LoginEvents -StartTime (Get-Date).AddDays(-1) -EndTime (Get-Date).AddDays(-1).AddHours(1)
Or even pipe Get-ADComputer
to the cmdlet:
Get-ADComputer -Filter {Name -like 'IT*'} | Get-LoginEvents -StartTime (Get-Date).AddDays(-7) | Where-Object LogonType -eq 'Interactive'
Conclusion
I hope you find this useful and that it saves you some time! I’ve used this cmdlet to remind myself when I logged into my computer to accurately track my time, to track malware logins across networks, to troubleshoot service account login issues, etc. There is so much you can do with it.
I’ve also uploaded this to my Utilities repo as well as the last post since I discovered that I never pushed my changes. But I have now!
If you’ve got any feedback, let me know with a comment, tweet, email, or whatever. I’m happy to assist if you have trouble using any of the tools I write.
Leave a Comment