8 min read

Defender for Identity NNR and health monitoring

Defender for Identity NNR and health monitoring
Photo by Joshua Chehov / Unsplash

Introduction

Defender for Identity is a very important sensor to detect threats in an Active Directory environment. Therefore, it is important to make sure the sensors are performing well, and no health issues are being reported. When a sensor is in an unhealthy state, detections can be missed, or False Positives can lead to alert fatigue on the SOC.

In this blogpost, I wanted to talk about one of the important features of Defender for Identity, which in practice has a lot of dependencies for it to work properly. We will go over these dependencies, how you can spot unhealthy scenarios, and how you can identify possible pain points.

What is Defender for Identity analyzing?

Defender for Identity analyzes multiple data sources in order to throw alerts:

  • Network traffic
  • Windows Events
  • Event Tracing for Windows (ETW)

In this post, we will zoom in more on the network traffic that is being inspected. More specifically, I want to talk about the Network Name Resolution (NNR) feature that is being used. But what is NNR?

Network Name Resolution

As Microsoft describes, "Network Name Resolution (NNR) is a main component of Microsoft Defender for Identity functionality". NNR is used to correlate raw activities (which contains IP addresses), and the relevant computer involved in each activity. Without this, Defender for Identity is not able to profile entities and generate accurate security alerts for suspicious behavior.

In other words, Defender for Identity uses NNR to resolve IP addresses to computer names. This is done by using four methods, of which the primary used methods are the following:

  • NTLM over RPC (TCP port 135)
  • NetBIOS (UDP port 137)
  • RDP (TCP port 3389) - Only uses the first 'Client hello' packet

One of the above methods is sufficient for NNR to work. When none of the above methods returned the computer name related to the IP address, or there is a conflict between at least two above methods, one last fallback method is used:

  • DNS query using reverse DNS lookup of the IP address (UDP port 53)

Once the computer name is retrieved, Defender for Identity checks Active Directory and uses TCP fingerprints if there is a correlated computer object with the same computer name. This helps detect unregistered and non-windows devices. When a correlation is found, the IP address gets associated with the computer object. In cases where no name is retrieved, an unresolved computer profile by IP is created with the IP and the relevant detected activity.

For which threats is NNR data crucial?

NNR data is crucial for detecting the following threats:

  • Suspected identity theft (pass-the-ticket)
  • Suspected DCSync attack (replication of directory services)
  • Network-mapping reconnaissance (DNS)

If you work in a SOC or you are actively checking your Defender for Identity alerts, you probably have seen these alerts before. These alerts often contain more information about the source device that triggered them, but sometimes information is missing or Defender for Identity seems to be flagging normal behavior. Changes are that NNR is not performing well in these cases. When NNR is not working properly, you might be encountering the following False Positives:

  • Suspected identity theft (pass-the-ticket) – the alert was triggered for the same computer.
  • Suspected DCSync attack (replication of directory services) – the alert was triggered from a domain controller.
  • Network-mapping reconnaissance (DNS) – the alert was triggered from a DNS Server.

In fact, you can actually see when NNR is not working properly via the alert you are investigating. Defender XDR tells you this by providing the certainty a hostname got properly resolved from an IP address:

Evidence certainty.

If you regularly encounter low mapping certainties here, there is probably something wrong.

Okay, let's say you get a lot of False Positives for the alerts mentioned above. How can we fix these issues? Let's go over how we can identify where the problem resides.

Primary methods

Since the primary methods are used first, we need to focus on these as a start. For the primary methods of NNR to work, the Defender for Identity sensors needs to have a connection to at least one of the three protocols to all network devices as described in the prerequisites:

Prerequisites - Microsoft Defender for Identity
This article describes the prerequisites required for a successful Microsoft Defender for Identity deployment.

This means that your network needs to be configured in such a way that all Defender for Identity sensors can reach all subnets over ports 135/TCP, 137UDP, and 3389/TCP. When this is not the case, this will in most cases be due to corporate firewalls. If you do not want to enable all ports, you can choose to allow only one of the three ports, since one should be sufficient for NNR to work. By default, all services should be enabled by default in an average Windows deployment. Make sure you verify this with your endpoint management team and set up appropriate and well-scoped rules based on your findings together with the networking team.

To help you identify which sensors were not able to reach certain subnets over port 135, 137, or 3389, my colleague Jani Vleurinck (https://www.linkedin.com/in/jani-vleurinck/) and I created a KQL query that you can use to identify this:

let networks = DeviceNetworkInfo
    // Expand all IPs
    | mv-expand todynamic(IPAddresses)
    // Get the network address related to the IP
    | extend NetworkAddress = format_ipv4(tostring(IPAddresses.IPAddress), tolong(IPAddresses.SubnetPrefix))
    // Build the IP with the CIDR notation
    | extend IPAddress = strcat(tostring(IPAddresses.IPAddress), "/", tolong(IPAddresses.SubnetPrefix))
    // Save the Prefix as an extra property
    | extend Prefix = tostring(IPAddresses.SubnetPrefix)
    // Make a set of all the IP's belonging to the same subnet
    | summarize make_set(IPAddress) by NetworkAddress, Prefix
    // Count how many IPs there are in one subnet
    | extend CountIPs = array_length(set_IPAddress)
    | extend Joiner = 1;
// Network Information
let network_info = DeviceNetworkInfo
    | mv-expand todynamic(IPAddresses)
    | extend IPAddress = tostring(IPAddresses.IPAddress);
// Ports used in NNR
let nnr_ports = dynamic(["3389", "135", "137"]);
let mdi_servers = dynamic([]);
// Query network connections
DeviceNetworkEvents
// Get events from Defender for Identity sensors - fill in mdi-servers variable for more complete results
| where InitiatingProcessFileName == "Microsoft.Tri.Sensor.exe" or DeviceName has_any (mdi_servers)
// Check traffic for NNR ports
| where RemotePort in (nnr_ports)
// Join the network info for more destination context
| join kind=inner network_info on $left.RemoteIP == $right.IPAddress
// Get distinct values
| project-rename RemoteDeviceName = DeviceName1
| distinct DeviceName, ActionType, RemoteIP, RemotePort, RemoteDeviceName
// Join all network addresses
| extend Joiner = 1
| join kind=inner networks on Joiner
// Check if remote ip is in a certain network address
| extend NetworkAddrPrefix = strcat(NetworkAddress, "/", Prefix)
| where ipv4_is_in_range(RemoteIP, NetworkAddrPrefix)
// Create Object to reuse later
| extend Obj = pack(
    "DeviceName", DeviceName,
    "NetworkAddrPrefix", NetworkAddrPrefix,
    "RemotePort", RemotePort,
    "RemoteIP", RemoteIP
)
// Count amount of failed and succeeded logins
| summarize FailedConnections = countif(ActionType == "ConnectionFailed"), 
    SucceededConnections = countif(ActionType == "ConnectionSuccess") by tostring(Obj)
// Extract the columns from the object again
| extend Obj = todynamic(Obj)
// Save the properties for later use
| extend DeviceName = tostring(Obj.DeviceName),
    NetworkAddrPrefix = tostring(Obj.NetworkAddrPrefix),
    RemotePort = tostring(Obj.RemotePort),
    RemoteIP = tostring(Obj.RemoteIP)
// Create a new object to save the amount of failed and succeeded attempts per IP
| extend Obj = pack(
    "RemoteIP", RemoteIP,
    "SucceededConnections", SucceededConnections,
    "FailedConnections", FailedConnections
)
// Create a list of the remote ips and their connections by MDI sensor, destination subnet and RemoteIP
// Subnets with only fails on both ports will fail in NNR
| summarize ConnectionDetails = make_set(Obj), 
    TotalSucceededConnections = sum(SucceededConnections), 
    TotalFailedConnections = sum(FailedConnections) by DeviceName, NetworkAddrPrefix, RemotePort
// Filter out /32 addresses
| where NetworkAddrPrefix !contains "/32"
// Sorting
| sort by TotalFailedConnections desc
// Reorder
| project-reorder DeviceName, NetworkAddrPrefix, RemotePort, TotalSucceededConnections, TotalFailedConnections, ConnectionDetails
Note that above query gets events from MDI servers by filtering on the sensor as initiating process. In some environments failed connections are not correlated with MDI sensors as initiating process (I guess that is when you use old versions of MDI). When you do not find any failed events, make sure to fill in the mdi_servers variable as a fallback for a more complete result.

This query shows you which Defender for Identity sensors (DeviceName column) can or cannot connect to certain subnet ranges found in your environment, over ports 135 and 3389. You will find the total amount of failed and succeeded connections to the entire subnet, but also the amount of failed and succeeded connections for each IP in that subnet.

Since MDE does not log UDP traffic in the DeviceNetworkEvents table, we cannot verify if connections for NetBIOS (UDP port 137) succeed. If you want to verify this explicitly, you will unfortunately need firewall logs.

With the above insights, you should have all the information you need to troubleshoot together with the networking team why certain connections to certain subnets fail.

Secondary method

In the secondary method, Defender for Identity searches the hostname of the device via PRT DNS lookups. For this to work, a couple of things need to be correct:

  • Defender for Identity needs to be able to reach the DNS server on port 53
  • Reverse lookup zones need to be enabled
  • Clients need to be enabled to dynamically register their PRT record to the same DNS server Defender for Identity is using

However, since these are specific dependencies that vary a lot in each environment, it is hard to create an auditing rule for this. I would recommend to make sure at least one of the primary methods discussed earlier works for all subnet ranges instead.

Health alerts

If you want to quickly check if your Defender for Identity sensors has any health alerts, you can log in to security.microsoft.com and navigate to Settings > Identities > Sensors. Here you will find your sensors and all their related health issues.

You will also be able to find the NNR health issues if a low success rate is detected:

A full list of all the health issues can be found at:

Microsoft Defender for Identity health issues - Microsoft Defender for Identity
This article describes all the health issues that can occur for each component, listing the cause and the steps needed to resolve the problem

Monitoring MDI health

So we talked about the importance of NNR and the health issues in Defender for Identity. Now you are probably wondering, can I get an alert when a health issue is thrown? Fabian Bader wrote a great blog post on how you can accomplish this by monitoring the alerting mailbox you can configure in Defender for Identity:

Integrate MDI health alerts in Microsoft Sentinel
Integrate MDI health alerts in Microsoft Sentinel or how to turn every e-mail notification in a custom alert in Sentinel and customize alert details for your benefit.

However, since the recent launch of the Beta Graph API, you can now get your Defender for Identity alerts by using API calls.

healthIssue resource type - Microsoft Graph beta
Represents potential issues within a customer’s Microsoft Defender for Identity configuration that Microsoft Defender for Identity identified.

This opens a lot of possibilities to monitor MDI health alerts more easily since you can create API integration using various technologies. One simple example would be a Logic App that periodically checks for MDI health alerts via the Graph API, which creates a Sentinel incident when a health alert is found. Sadly enough, I did not find the time yet to create such a Logic App. But if you create one yourself, feel free to share it with the community 😋.