Detecting Lateral Movement with Windows Event IDs

How can you use PowerShell and Event IDs to detect the most important events you need to be aware of and respond to?

Detecting Lateral Movement with Windows Event IDs

Nothing beats a state of the art Endpoint Detection and Response (EDR) capability. EDR's have much deeper insight on the endpoint, are much harder to evade, and can bring much more actionable information to the operations team vs traditional logging.

However, not everyone is blessed with an EDR across all assets. So, with that caveat on how EDR is better than raw logs, there is still much importance in collecting logs, especially in a time of crisis.

Detecting lateral movement can also help you create your own, custom, containment strategy, tailored to your environment and your Active Directory configuration. I hit on this in another blog, here, which I highly recommend reading!

With that caveat, let's get on with the show!

Why Lateral Movement is important to detect/see

The goal of an EDR is to keep the bad guy/gals out. Period. And if they get in, stop them in their tracks, contain them, and make sure you're made aware of this, hopefully temporary, intrusion.

But one thing is certain--adversaries eventually will get in and eventually will need to laterally move to carry out their modus operandi. They might have 900 ways to get into your environment, wether it be spearphish, watering-hole, whatever... but their post-exploit activity is normally the same--they need to escalate their privileges, and in order to do that, usually this means jump across machines.

So, how do we do this exactly, with an EDR? Or... for those with an EDR, how can you augment your EDR with this knowledge to better your ability to respond?

Event IDs and PowerShell

Of course, there are always assumptions when dealing with a non-EDR solution--or at least a good one which doesn't rely on logs from the underlying OS. Since we are using logs here, the assumptions are: 1.) Logging is enabled (Audit Logon Events), for both Success and Failures, and 2.) PowerShell 2.0+ is availble to us.  As I said in the past, logging is easy to evade--especially Microsoft's. You can quickly disable it by modifying what is logged by changing the registry, you can disable the logging service via Phat0m, and best yet, defenders who rely on this data most likely will have no idea they are flying blind!

But again, with my caveats...

Getting the right logs

The “Audit Logon Events” needs to have the security settings “Success, Failure”.  When off, this will not actually log the failed attempts of logons, specifically when the GPO is applied to prevent logons via the Network (network or interactive (i.e. RDP).  That said it is imperative to ensure this feature is on—measuring compliance for this setting is outside the scope of this document, however, this can be applied via a GPO (although the best way to measure compliance isn’t reviewing the GPO but measuring it at the endpoint itself).  This GPO is in line with the Defense Information Systems Agency (DISA) Security Technical Implementation Guide (STIG).

Unfortunately some of these Event IDs are noisy or don’t always map to actual issues.  For example, Event ID 4648 (logon attempt with explicit credentials) was seen accessing with different credentials when accessing a SharePoint website (i.e. User ‘a’ on the machine authenticating with different credentials to a remote server).

That said, reducing noise is imperative as no team has the proper resources to sift through countless logs.  The follow sections provide insight on how to fine tune the policy, specifically, how to use PowerShell for filtering which previously limited to ‘eventvwr’ filtering (XPath queries).

So what are the Authentication Events?
The following Event IDs are deemed important as they identify

  • Event ID 4648 - A logon was attempted using explicit credentials. Key fields: Account Name (Subject), Account Name (account whose credentials were used), Process Name
  • Event ID 4624 - An account was successfully logged on. Key fields: Account Name, Logon type
  • Event ID 4625 - An account was unsuccessfully logged on. Key fields, Account Name, Logon type
  • Event ID 4672 - A special privileges assigned to new logon. (reference)

These logs can be found in “Windows Logs > Security” whose event provider is “Microsoft-Windows-Security-Auditing”.  This information will be useful when developing the script.

Based on analysis and testing, 4648 is chatty although should be used on critical infrastructure where logon attempts should be very limited (i.e. domain controller).  The same goes for event 4672, which is chatty especially in environments with users being granted special privileges.  This is essentially useless on a domain controller since all users/services on a DC are domain admins.  That said, to use this event more testing would need to be conducted.

The core purpose of this document is to detect lateral movement attempts, regardless if they are successful or not.  With this in mind, the best events to detect are specially filtered events for IDs 4624 and 4625.  Furthermore, it is imperative to log both failure and successes of these events, as identified in section 2.2 of this document.However, event 4624 and 4625 have shown to be noisy, especially in Enterprise environments.  Thus the following sections illustrate additional filtering that can be done via PowerShell to look for specific values in those events that illustrate a high likelihood of lateral movement--but first, we need to know what we are actually looking at.

Tell me more about these "Logon Types"

There are various Microsoft Windows Logon types.  For workstations, we are specifically interested in ones that are mapped to the Local Accounts groups, and which are done via the network.

Here is a table of the Windows Logon Types:

Logon Type





Logon at the console in front of a computer.



Remote access to a computer from elsewhere in the network



Logon session via a scheduled task



Service account specific logons



When a user unlocks a computer (i.e. screensaver)


New Credentials

This is used when you run an application using the RunAs command.


Remote Interactive

This is used for the RDP applications like Terminal Services or Remote Assistance.


Cached Interactive

This is logged when users log on using cached credentials.

For our PowerShell script, we only care about Logon Type 3, or “Network Logon”.  That said, we will be filtering for Logon Events only for where the event itself maps to a Logon Type 3.  For example, here is testing of the event after the GPO has been applied.

Reasons for a 'failed' logon

All events, when triggered by a failed ability to logon, this doesn’t mean the username/password was wrong but the account was prevented from gaining access to the computer, specifically based on the logon type.  Again, this policy was identified in another document illustrating how to prevent lateral movement, while this document extends that while providing the ability to detect attempts (failures and successes).

However, when we tested a PowerShell ‘–match’ (regex expression) looking for the “Failure Reason”, the script returned no results.  After digging into this, it was identified that the event object itself returns a different value for the Failure Reason which is the value “%%2308”.

Now what? Using PowerShell to find the important queries!

Now with all the information necessary we can go ahead and make the appropriate PowerShell script:

This script follows the above guidance:

  • Filtering for events 4624 and 4625
  • When the assumptions section is met, this logs both success and failures
  • Search for the “%%2308” text in the error message
  • Search for the “logon type:`t`t`t3” in the message, which equates to Network Logons as illustrated earlier

When we return $events we will be provided the respective events with high probability that lateral movement has occurred or is being attempted.  From a reporting perspective, now that we have the events in a PSObject, we can easily report to SQL or via HBSS.Since this is a proof of concept, this does not include the reporting mechanism included.  However, the hard part is done which is the actual detection of the event, and parsing through all the other events to find the ones environments should really care about.  This will not detect users putting in the wrong username/password as we have filtered out those failures.  These are high risk events.


Good security is much more than logging, with close to no dependencies on the underlying OS for the reasons we talk about. But, at some point, you will need logs to augment your operations, nor is everyone well funded to have best-of-breed EDR across all endpoints/workloads.

After seeing the different parameters and authentication logon logs, we can use PowerShell to quickly raise events of major concern. Anytime you see an event 4624 or 4625, and the raw event has %%2308 (again, this is used so it maps to your respective language pack!) in it's error message, you have one of the highest alerts you need to triage!

Stay safe out there friends.