Windows Log Hunting with PowerShell

A quick post for the new year with some useful one-liners to extract info from Windows logs with PowerShell. These commands are extremely useful for incident response or threat hunting, especially when combined with a well tuned Sysmon installation.

Though I'm sure many people have used PowerShell to look at Windows logs with the Get-WinEvent cmdlet, accessing the specific values out of a Windows log is something I've rarely seen in examples, so that is one technique I wanted to highlight here. If you aren't familiar with PowerShell, here's a quick breakdown of the first command using the example full Sysmon event below.

  • Get-WinEvent -filterhashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=1} - Grabs all logs from the "Microsoft-Windows-Sysmon/Operational" log and filters for only Id equal to 1. Add a | fl | more instead of the rest here if you want to see all the values, this is what produced the picture above.

  • %{$_.Properties[4].Value} - Takes the object output by the previous commands (Sysmon logs of type 1) and selects the 3rd (counting from 0) line - "Image:", and prints the value C:\Windows\system32\calc.exe. Alternatively, you could produce a list of executables including arguments passed if you use the 4th item, "CommandLine".

  • sort -Unique - Lists only each unique value once and sorts.

When entered in full, this leads to the output seen below. As you can imagine, there would be a pretty good chance you could find malicious events if you had lists like this for domains contacted, exectuables run, registry keys written, and other key host information that can be accessed via the Windows log. The list below contains some of the one-liners I've found useful.

The List
  • List all unique executable names recorded by Sysmon: Get-WinEvent -filterhashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=1} | %{$_.Properties[3].Value} | sort -Unique

  • List all unique executable names and command lines used when starting a new process: Get-WinEvent -filterhashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=1} | %{$_.Properties[4].Value} | sort -Unique

  • List domain names contacted recorded by Sysmon: Get-WinEvent -filterhashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=3} | %{$_.Properties[14].Value}| sort -Unique

  • Every destination port contacted: Get-WinEvent -filterhashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=3} | %{$_.Properties[15].Value}| sort -Unique

  • Hash of every executable run: Get-WinEvent -filterhashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=1} | %{$_.Properties[11].Value}| sort -Unique

Some non-Sysmon related commands, note that proper auditing of these events must be on so the event logs are cut in the first place:

  • All windows security log event IDs, ranked by least frequent: Get-WinEvent -FilterHashtable @{logname="security"}| Group-Object id -NoElement | sort count

  • All usernames who have logged in: Get-WinEvent -FilterHashtable @{logname="security";id=4624} | %{$_.Properties[5].Value} | sort -Unique

  • Count of logins by username: Get-WinEvent -FilterHashtable @{logname="security";id=4624} | %{$_.Properties[5].Value} | group-object -noelement | sort count

  • All domains for accounts that have logged in: Get-WinEvent -FilterHashtable @{logname="security";id=4624} | %{$_.Properties[6].Value} | sort -Unique

  • SIDs for all accounts that have logged in: Get-WinEvent -FilterHashtable @{logname="security";id=4624} | %{$_.Properties[4].Value} | sort -Unique

  • Failed login count by username: Get-WinEvent -FilterHashtable @{logname="security";id=4625} | %{$_.Properties[1].Value} | Group-Object -noelement

  • Invalid usernames used for login attempts (looking for account guessing) Get-WinEvent -FilterHashtable @{logname="security";id=4776} | %{$_.Properties[1].Value} | sort -Unique

  • AppLocker events - List all executables and scripts that were against policy.
    Use event ID 8003/8004 (audit mode/block mode) for exe and dll files: Get-WinEvent -FilterHashtable @{logname="Microsoft-Windows-AppLocker/EXE and DLL";id=8003} | select message.
    Use event ID 8006/8007 for script or MSI files: Get-WinEvent -FilterHashtable @{logname="Microsoft-Windows-AppLocker/MSI and Script";id=8006} | select message