Building a Custom UEBA with KQL to Hunt for Lateral Movement

Let’s Connect | LinkedIn | Twitter

Identity is the new perimeter, and monitoring of the identities has become crucial for organizations. Since I’ve only seen unsuccessful UEBA implementations so far, I’ve developed my own custom UEBA-like solution using KQL and Microsoft 365 Defender logs to hunt for account anomalies(T1078.002) and Lateral Movement. Depending on the details that Windows logon events have, the logic explained in this post can be implemented on Azure Sentinel and Splunk.

Lateral Movement consists of techniques that adversaries use to enter and control remote systems on a network. Following through on their primary objective often requires exploring the network to find their target and subsequently gaining access to it. Reaching their objective often involves pivoting through multiple systems and accounts to gain. Adversaries might install their own remote access tools to accomplish Lateral Movement or use legitimate credentials with native network and operating system tools, which may be stealthier.

As mentioned in the MITRE definition, Lateral Movement requires legitimate credentials to gain access to systems for pivoting. It should be noted that an adversary must already have succeeded in Initial Access and possibly in other tactics like Execution before starting to move laterally. So, if we look at an attack as a whole:

  • A system/user gets compromised
  • Some activities are performed on the system on which the compromised user is active/inactive
  • A privileged and valid account is obtained via several techniques
  • The privileged account is used from the compromised system first to access other systems. Then it can be used from the systems that are accessed to continue moving laterally.

Now, since there are several security controls in an environment (IDS, spam gateway, AV, EDR, etc.), there is at least an alert triggered for one of the activities before or just after the lateral movement. The possibility of the whole attack having no alerts is close to 0 (but I’ll cover that as well).

The problem is that even if there is an alert triggered at some point, it can be a low/informational severity that an analyst often skips because of the alert fatigue forcing the analyst to look at high severity alerts first. Also, the triggered alert can’t explain if it’s a part of an attack. This is because the alert is related only to the impacted entity, and there is no link between other related entities like the first compromised machine or the first target system that is accessed. Therefore, it becomes impossible to detect the ongoing attack because of a low/informational severity alert being skipped.

Connecting the dots and solving the issue

As we are talking about an attack involving Lateral Movement, we can find unusual logon activities first(or assume all logons are suspicious), then enrich them with useful information and apply some analytics to uncover highly suspicious ones for further analysis. This would be a simple and custom UEBA solution.


Logon activity baseline for each privileged account
In an enterprise, there is always a naming convention for accounts and computers. So, it is easy to tell if the account is a service account or a privileged(admin) account. To demonstrate:

Personal account           : FirstName.LastName
Personal privileged account: FirstName.LastName-admin
Service Account : svc-app01
Global privileged account : sccmadmin
Personal computer : WS<user/location>
Server : SRV<appname>01

Having a naming convention makes it is easy to define a condition while searching through the logs. Next, some environmental knowledge that applies possibly to all organizations (know your environment!):

  • Some accounts have admin rights on specific systems
  • Some accounts have admin rights on all systems
  • Some accounts are used only from specific systems

So, we need to generate a baseline for each privileged account as follows(lateral movement requires a privileged account in almost all cases):

  1. Get the number of source and destination systems that the account logged on in the last 15/30d except for the last day(it will be used for the baseline comparison)
  2. Get the source and destination system names(hostnames) that the account logged on in the last 15/30d except for the last day(it will be used for the baseline comparison)
  3. Get the source and destination system type (Server/Workstation) for each logon performed by the privileged account
  4. Since the whole attack will have an alert somewhere in the chain:
    Get all alerts that impacted/related to an account or system
    There is a trick here: a user having a separate admin account can get compromised. We need to treat both accounts as the same identity. Having a naming convention makes this possible.

Now, time to cook!

Since service accounts are slightly different from personal privileged(admin) accounts, we will develop one query for each one with a slight difference. The logic is the same:

  1. Generate logon activity baseline of privileged accounts for the last 15/30d except for the last day
  2. Get logon activities of privileged accounts for the last 1d (assume all of them are suspicious).
  3. Enrich the results with the privileged account baseline and alerts
  4. Apply some filters to display only the highly suspicious activities:
    For example, display the results where the account is used from a new device, or there is an alert related to the account or source/destination system.

By using IdentityLogonEvents and other relevant tables in Microsoft 365 Defender, the result of the query looks as follows(the data is manipulated):

Now, just by looking at the result, an analyst/hunter can tell the story very quickly:

A workstation was probably compromised, then a port scan was performed. Meanwhile, probably a valid account(the service/admin account) was obtained, the server was discovered(maybe via a password spray). Finally, the account was used from the compromised workstation to log on to the server, and a malicious service was created on the server.

The analyst/hunter can take a closer look at the low severity alerts and investigate the hosts.


If we start focusing on the attacks as a whole, instead of focusing only on the attack techniques individually, we can develop more robust detections and become more resilient to cyber attacks. This is like combining western and eastern medicine to heal a person.

You can find the query for privileged(admin) accounts here, and the query for service accounts here, in my Github repo. You’ll need to modify the queries according to your service and privileged(admin) account naming conventions. You can further improve the result of the queries with the Jupyter notebook.

In the coming days, I’ll try to cover Lateral Movement with local accounts. Also, I’ll try to implement the same solution on Azure Sentinel by using Windows logs.

Cyber Defense Professional. @Cyb3rMonk ( Threat Hunting | Active Defense | Cyber Deception | SOC | SIEM )

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store