6 min read

MDE Device Discovery - Improving the monitored network page

MDE Device Discovery - Improving the monitored network page
Photo by Thomas Jensen / Unsplash

Introduction

This blogpost is probably the first of a series that I will create in the coming months on Device Discovery. I regularly see organizations buy a specific tool to create an asset inventory list of what lives in their networks, while this is something we can actually do with Microsoft Defender for Endpoint (with some nuances). By using the tools we already have, we can save costs, and make sure that everything is as much centralized as possible in the Defender XDR asset inventory. But to be completely honest, the Microsoft tooling that provide these insights can use some improvements here and there. Because of this, I wanted to talk in this blogpost what I think could be done better, and how you can actually get these insights via some KQL queries.

The technology

Microsoft Defender for Endpoint has a specific feature capability baked in called 'Device Discovery'. This feature uses onboarded endpoints in your network to collect, probe, or scan your network to discover unmanaged devices. This capability allows you to discover:

  • Enterprise endpoints - workstations, servers, and mobile devices
  • Network devices like routers and switches
  • eIoT devices like printers and cameras

Since I would like this blogpost to be technical as much as possible, I assume you have some basic knowledge on how MDE Device Discovery works. When this is not the case, I recommend to first read the Microsoft Learning pages if you would like to know more about what Device Discovery is and how it is configured: https://learn.microsoft.com/en-us/defender-endpoint/device-discovery

The monitored networks page

If you have worked with Device Discovery before, you will probably know the Monitored Networks page in Defender XDR. This is the page with all networks found by Device Discovery, that provides an overview of which networks are monitored and which not.

For me, there are a couple of challenges with this page for which we will find a solution later in this blogpost:

  • No possibility to search based on subnet range, default gateway, or other properties (only network name)
  • List by default only contains networks identified as 'corporate' networks
  • List show only 50 networks with no possibility to show more
  • List does not show which subnets are behind the network names

Interesting to note regarding this page is that:

  • The list is sorted based upon the total number of devices seen on the network in the last seven days.
  • If less than 50 'corporate' networks are found, the list contains networks with the most onboarded devices.
  • Networks are marked as 'corporate' when multiple devices report the same network name, default gateway, and DHCP server address. These are typically chosen to be monitored automatically by Defender XDR.

I found that the most easy way to search for identified networks is by searching for them via KQL instead. Even though the GUI only show 50 networks by default, you can perfectly find and list all of the networks with a KQL query on the DeviceNetworkInfo table in Defender XDR. If you want to know which subnet ranges are connected to a specific network name, you can also find this information via KQL.

Solve the monitored networks challenges

In below query we count the amount of devices found on a specific network, where we group the networks based on their network name and DHCP server address (just like the corporate network identification but without default gateway to reduce clutter).

By doing it via KQL we can:

  • List all networks even when they are not identified as 'corporate' by Device Discovery
  • Show more than 50 networks
  • Find the subnets behind the network name
// OPTIONAL - Device cap used to ignore network with less then X devices in them
let device_cap = 0;
DeviceNetworkInfo
| where Timestamp > ago(7d)
// Ignore empty networks
| where ConnectedNetworks  != ""
// Get networks data
| extend ConnectedNetworksExp = parse_json(ConnectedNetworks)
| mv-expand bagexpansion = array ConnectedNetworks=ConnectedNetworksExp
| extend NetworkName = tostring(ConnectedNetworks ["Name"]), NetworkDescription = tostring(ConnectedNetworks ["Description"]), NetworkCategory = tostring(ConnectedNetworks ["Category"])
// Get subnet data for IPv4 Addresses
| extend IPAddressesExp = parse_json(IPAddresses)
| mv-expand bagexpansion = array IPAddresses=IPAddressesExp
| extend IPAddress = tostring(IPAddresses ["IPAddress"]), SubnetPrefix = tolong(IPAddresses ["SubnetPrefix"])
| extend NetworkAddress = format_ipv4(IPAddress, SubnetPrefix)
| extend SubnetRange = strcat(NetworkAddress, "/", SubnetPrefix)
// Exclude IPv6 and APIPPA
| where SubnetPrefix <= 32
| where IPAddress !startswith "169.254"
// Ignore unidentified networks
| where not(NetworkName has_any ("Unidentified", "Identifying..."))
// Provide list
| distinct DeviceId, NetworkName, IPv4Dhcp, SubnetRange
| summarize Devices = count(), SubnetRanges = make_set(SubnetRange) by NetworkName, IPv4Dhcp
// Ignore network with very low device count
| where Devices >= device_cap
| sort by Devices desc
💡
This query should give you better insights into what networks or subnets are behind the Device Discovery networks page in Defender XDR.

Once you know which subnets are linked to which network names, you can go back to the Monitored Networks page and manually change the monitored state for the networks you want Device Discovery to scan on.

Find by subnet range

If you want to know if a specific subnet in your corporate network is being monitored by Device Discovery or not, you can extend the above query to search for your subnet and check under which network names this subnet range lives:

// OPTIONAL - Device cap used to ignore network with less then X devices in them
let device_cap = 0;
DeviceNetworkInfo
| where Timestamp > ago(7d)
// Ignore empty networks
| where ConnectedNetworks  != ""
// Get networks data
| extend ConnectedNetworksExp = parse_json(ConnectedNetworks)
| mv-expand bagexpansion = array ConnectedNetworks=ConnectedNetworksExp
| extend NetworkName = tostring(ConnectedNetworks ["Name"]), NetworkDescription = tostring(ConnectedNetworks ["Description"]), NetworkCategory = tostring(ConnectedNetworks ["Category"])
// Get subnet data for IPv4 Addresses
| extend IPAddressesExp = parse_json(IPAddresses)
| mv-expand bagexpansion = array IPAddresses=IPAddressesExp
| extend IPAddress = tostring(IPAddresses ["IPAddress"]), SubnetPrefix = tolong(IPAddresses ["SubnetPrefix"])
| extend NetworkAddress = format_ipv4(IPAddress, SubnetPrefix)
| extend SubnetRange = strcat(NetworkAddress, "/", SubnetPrefix)
// Exclude IPv6 and APIPPA
| where SubnetPrefix <= 32
| where IPAddress !startswith "169.254"
// Ignore unidentified networks
| where not(NetworkName has_any ("Unidentified", "Identifying..."))
// Provide list
| distinct DeviceId, NetworkName, IPv4Dhcp, SubnetRange
| summarize Devices = count(), SubnetRanges = make_set(SubnetRange) by NetworkName, IPv4Dhcp
// Ignore network with very low device count
| where Devices >= device_cap
| sort by Devices desc
// Search specific subnet range
| where tostring(SubnetRanges) contains "172.16.76.0/24"

Interesting to note is that some devices report a network name with a specific number at the back of the network name:

These numbers seem to be filtered out and merged in the Device Discovery settings page:

Find networks based on documentation

In one of my previous blog posts I talked about how you can save your networking documentation in Microsoft Sentinel Watchlists, to use that information in queries later on. If you have done this, you can use the below query to correlate the subnet ranges from the watchlist with the subnet ranges from the DeviceNetworkInfo table, showing you which networks you need to configure as 'monitored' in Defender XDR.

// OPTIONAL - Device cap used to ignore network with less then X devices in them
let device_cap = 5;
// Get subnet ranges of network docs
let network_docs = _GetWatchlist('networks')
    | extend SubnetRange = strcat(NetworkAddress, "/", Prefix)
    | distinct Name, Location, SubnetRange;
// Get Networks in Defender XDR Device Discovery
DeviceNetworkInfo
| where TimeGenerated > ago(7d)
// Ignore empty networks
| where ConnectedNetworks  != ""
// Get networks data
| extend ConnectedNetworksExp = parse_json(ConnectedNetworks)
| mv-expand bagexpansion = array ConnectedNetworks=ConnectedNetworksExp
| extend NetworkName = tostring(ConnectedNetworks ["Name"]), NetworkDescription = tostring(ConnectedNetworks ["Description"]), NetworkCategory = tostring(ConnectedNetworks ["Category"])
// Get subnet data for IPv4 Addresses
| extend IPAddressesExp = parse_json(IPAddresses)
| mv-expand bagexpansion = array IPAddresses=IPAddressesExp
| extend IPAddress = tostring(IPAddresses ["IPAddress"]), SubnetPrefix = tolong(IPAddresses ["SubnetPrefix"])
| extend NetworkAddress = format_ipv4(IPAddress, SubnetPrefix)
| extend SubnetRange = strcat(NetworkAddress, "/", SubnetPrefix)
// Join the network docs to only find networks from Defender XDR that are in the docs
| join kind=inner network_docs on SubnetRange
// Exclude IPv6 and APIPPA
| where SubnetPrefix <= 32
| where IPAddress !startswith "169.254"
// Ignore unidentified networks
| where not(NetworkName has_any ("Unidentified", "Identifying..."))
// Provide list
| distinct DeviceId, NetworkName, IPv4Dhcp, SubnetRange
| summarize Devices = count(), SubnetRanges = make_set(SubnetRange) by NetworkName, IPv4Dhcp
// Ignore network with very low device count
| where Devices >= device_cap
| sort by Devices desc

Conclusion

With above queries you should be able to get a better understanding which subnet ranges are being monitored by Device Discovery, and which networks monitoring state you need to change based on your corporate subnet ranges.