Query the enterprise exposure graph
Use the enterprise exposure graph in Microsoft Security Exposure Management to proactively hunt for enterprise exposure threats in advanced hunting in the Microsoft Defender portal.
This article provides some examples, tips, and hints for constructing queries in the enterprise exposure graph.
Prerequisites
- Read about attack surface management.
- Review required permissions for working with the graph.
Build advanced hunting queries
- Review best practices for building advanced hunting queries
- Get started with Kusto Query Language (KQL)
Use the make-graph operator
Kusto's make-graph
operator loads nodes and edges data into memory.
- Since Kusto only loads the columns that are in use, there's no need to explicitly select columns.
- However, the
NodeProperties
column contains all node information and so is large. - In most scenarios, it's useful to extract only the information required before feeding it into the
make-graph
operator.
Example
let FilteredNodes = ExposureGraphNodes
| extend ContainsSensetiveData = NodeProperties has "containsSensitiveData"
| project Id, ContainsSensetiveData, Label, EntityIds, Categories;
Edges
| make-graph SourceNodeId --> TargetNodeId with FilteredNodes on Id
..
Use dynamic columns and smart-indexing
NodeProperties
and Categories
are dynamic columns.
- Kusto knows those columns contain json-like content, and applies smart indexing.
- However, not all Kusto operators use the index. For example,
set_has_element
,isempty
,isnotnull
don't use the index when they're applied to a dynamic column andisnotnull(Properties["containsSensitiveData"]
doesn't use the index. - Instead, use the
has()
operator, which always uses the index.
Example
In the following query the has
operator checks for the data
string, and set_has_element
checks for the data
element.
Using both operators is important as the has()
operator returns true even for a category prefix_data
.
Categories has('data') and set_has_element(Categories, 'data')
Learn more about understanding string terms.
Example exposure queries
The following examples can help you write queries to understand the security exposure data in your tenant.
List all node labels in your tenant
The following query groups the data in the ExposureGraphNodes
table and uses Kusto's summarize
operator to list it by NodeLabel
.
ExposureGraphNodes
| summarize by NodeLabel
List all edge labels in your tenant
The following query groups the data in the ExposureGraphEdges
table and uses Kusto's summarize
operator to list it by edge labels (EdgeLabel
).
ExposureGraphEdges
| summarize by EdgeLabel
List all connections from a specified node label
The following query groups the data in the ExposureGraphEdges
table, and where the source node label is microsoft.compute/virtualmachines
, it summarizes the virtual machine's by EdgeLabel
. It summarizes the edges that connect assets to virtual machines in your security exposure graph.
ExposureGraphEdges
| where SourceNodeLabel == "microsoft.compute/virtualmachines"
| summarize by EdgeLabel
List all connections to a specific node label
The following query summarizes edges that connect virtual machines to other security exposure graph assets. It groups the data in the ExposureGraphEdges
table, and where the target node label is microsoft.compute/virtualmachines
, it uses Kusto's summarize
operator to list the target node label by EdgeLabel
.
ExposureGraphEdges
| where TargetNodeLabel == "microsoft.compute/virtualmachines"
| summarize by EdgeLabel
List properties of a specific node label
The following query lists properties of the virtual machine node label. It groups the data in the ExposureGraphNodes
table, filtered to show the node label "microsoft.compute/virtualmachines" results only. With the project-keep
operator, the query keeps the NodeProperties
column. The data returned is limited to one row.
ExposureGraphNodes
| where NodeLabel == "microsoft.compute/virtualmachines"
| project-keep NodeProperties
| take 1
Query the exposure graph
To query the exposure graph:
In the Microsoft Defender portal, select hunting -> advanced hunting.
In the Query area, type your query. Use the graph schema, functions, and operator tables or the following examples to help you build your query.
Select run query.
Graph-oriented query examples
Use these graph-oriented query examples to help you write better security exposure queries. The examples search for patterns to expose relationships between entities that can uncover risk. They show you how to correlate context with incident/alert signals.
List all node labels with an edge to a specific node label
The following query results in a list of all incoming node labels with a connector to the virtual machine node label. It builds a graph structure by mapping the SourceNodeId
column data in the ExposureGraphEdges
table to the TargetNodeId
column in the ExposureGraphNodes
table with the make-graph
operator to build a graph structure.
It then uses the graph-match
operator to make a graph pattern where the target node TargetNode
and NodeLabel
match microsoft.compute/virtualmachines
. The project
operator is used to keep only the IncomingNodeLabels
. It lists the results by IncomingNodeLabels
.
ExposureGraphEdges
| make-graph SourceNodeId --> TargetNodeId with ExposureGraphNodes
on NodeId
| graph-match (SourceNode)-[edges]->(TargetNode)
where TargetNode.NodeLabel == "microsoft.compute/virtualmachines"
project IncomingNodeLabels = SourceNode.NodeLabel
| summarize by IncomingNodeLabels
List all node labels edging a specific node label
The following query results in a list of all the outgoing node labels with a connector to the virtual machine node label.
- It builds a graph structure by mapping the
SourceNodeId
column uses the data in theExposureGraphEdges
table to theTargetNodeId
column in theExposureGraphNodes
table using themake-graph
operator to build a graph structure. - It then uses the
graph-match
operator to match the graph pattern whereSourceNode
andNodeLabel
matchmicrosoft.compute/virtualmachines
. - The
project
operator is used to keep only theOutgoingNodeLabels
. It lists the results byOutgoingNodeLabels
.
ExposureGraphEdges
| make-graph SourceNodeId --> TargetNodeId with ExposureGraphNodes
on NodeId
| graph-match (SourceNode)-[edges]->(TargetNode)
where SourceNode.NodeLabel == "microsoft.compute/virtualmachines"
project OutgoingNodeLabels = SourceNode.NodeLabel
| summarize by OutgoingNodeLabels
Discover VMs exposed to the internet with an RCE vulnerability
The following query allows you to discover virtual machines exposed to the internet and to a Remote Code Execution (RCE) vulnerability.
- It uses the
ExposureGraphNodes
schema table. - When both
NodeProperties
exposedToInternet
andvulnerableToRCE
are true, it checks that the category (Categories
) is virtual machines (virtual_machine
).
ExposureGraphNodes
| where isnotnull(NodeProperties.rawData.exposedToInternet)
| where isnotnull(NodeProperties.rawData.vulnerableToRCE)
| where Categories has "virtual_machine" and set_has_element(Categories, "virtual_machine")
Discover internet facing devices with a privilege escalation vulnerability
The following query looks for internet facing devices exposed to a privilege escalation vulnerability, which could allow access to higher level privileges within the system.
- It uses the
ExposureGraphNodes
schema table. - When
NodeProperties
is both internet facing (IsInternetFacing
) andVulnerableToPrivilegeEscalation
, the query checks that the items inCategories
are actually devices (device
).
ExposureGraphNodes
| where isnotnull(NodeProperties.rawData.IsInternetFacing)
| where isnotnull(NodeProperties.rawData.VulnerableToPrivilegeEscalation)
| where set_has_element(Categories, "device")
Show all users logged in to more than one critical device
This query results in a list of users logged into more than one critical device, along with the number of devices they're logged into.
- It creates an
IdentitiesAndCriticalDevices
table usingExposureGraphNodes
data filtered either by devices with a criticality level above 4 or byidentity
. - It then makes a graph structure with the
make-graph
operator, where theEdgeLabel
isCan Authenticate As
. - It uses the
graph-match
operator to match instances where adevice
matches anidentity
. - Then it uses the
project
operator to keep identity IDs and device IDs. - The
mv-apply
operator filters device IDs and identity IDs by type. It summarizes them and displays the results in a table with the headers,Number Of devices user is logged-in to
, andUser Id
.
let IdentitiesAndCriticalDevices = ExposureGraphNodes
| where
// Critical Device
(set_has_element(Categories, "device") and isnotnull(NodeProperties.rawData.criticalityLevel) and NodeProperties.rawData.criticalityLevel.criticalityLevel < 4)
// or identity
or set_has_element(Categories, "identity");
ExposureGraphEdges
| where EdgeLabel == "Can Authenticate As"
| make-graph SourceNodeId --> TargetNodeId with IdentitiesAndCriticalDevices on NodeId
| graph-match (Device)-[canConnectAs]->(Identity)
where set_has_element(Identity.Categories, "identity") and set_has_element(Device.Categories, "device")
project IdentityIds=Identity.EntityIds, DeviceIds=Device.EntityIds
| mv-apply DeviceIds on (
where DeviceIds.type == "DeviceInventoryId")
| mv-apply IdentityIds on (
where IdentityIds.type == "SecurityIdentifier")
| summarize NumberOfDevicesUserLoggedinTo=count() by tostring(IdentityIds.id)
| where NumberOfDevicesUserLoggedinTo > 1
| project ["Number Of devices user is logged-in to"]=NumberOfDevicesUserLoggedinTo, ["User Id"]=IdentityIds_id
Show client devices with a critical vulnerability/users that have access to high value servers
The following query results in a list of devices with RCE vulnerabilities and their device IDs, and devices with high critical vulnerabilities and their device IDs.
- It creates an
IdentitiesAndCriticalDevices
table that includes devices (device
) with RCE vulnerabilities with criticality lower than four, and identities (identity
) that with through filtering and pattern matching, show devices with critical vulnerabilities. - The list is filtered to show only those connections that have edge labels
Can Authenticate As
andCanRemoteInteractiveLogonTo
.
let IdentitiesAndCriticalDevices = ExposureGraphNodes // Reduce the number of nodes to match
| where
// Critical devices & devices with RCE vulnerabilities
(set_has_element(Categories, "device") and
(
// Critical devices
(isnotnull(NodeProperties.rawData.criticalityLevel) and NodeProperties.rawData.criticalityLevel.criticalityLevel < 4)
or
// Devices with RCE vulnerability
isnotnull(NodeProperties.rawData.vulnerableToRCE)
)
)
or
// identity
set_has_element(Categories, "identity");
ExposureGraphEdges
| where EdgeLabel in~ ("Can Authenticate As", "CanRemoteInteractiveLogonTo") // Reduce the number of edges to match
| make-graph SourceNodeId --> TargetNodeId with IdentitiesAndCriticalDevices on NodeId
| graph-match (DeviceWithRCE)-[CanConnectAs]->(Identity)-[CanRemoteLogin]->(CriticalDevice)
where
CanConnectAs.EdgeLabel =~ "Can Authenticate As" and
CanRemoteLogin.EdgeLabel =~ "CanRemoteInteractiveLogonTo" and
set_has_element(Identity.Categories, "identity") and
set_has_element(DeviceWithRCE.Categories, "device") and isnotnull(DeviceWithRCE.NodeProperties.rawData.vulnerableToRCE) and
set_has_element(CriticalDevice.Categories, "device") and isnotnull(CriticalDevice.NodeProperties.rawData.criticalityLevel)
project DeviceWithRCEIds=DeviceWithRCE.EntityIds, DeviceWithRCEName=DeviceWithRCE.NodeName, CriticalDeviceIds=CriticalDevice.EntityIds, CriticalDeviceName=CriticalDevice.NodeName
Provide all paths from specific node ID to a node with a specific label
This query displays the path from a specific IP node, passing through up to three assets that results in a connection to the virtual machine node label.
- It uses the
ExposureGraphNodes
andExposureGraphEdges
schema tables and themake-graph
andgraph-match
operators to create a graph structure. - With the
project
operator, it displays a list of IP IDs, IP properties, virtual machine IDs, and virtual machine properties.
let IPsAndVMs = ExposureGraphNodes
| where (set_has_element(Categories, "ip_address") or set_has_element(Categories, "virtual_machine"));
ExposureGraphEdges
| make-graph SourceNodeId --> TargetNodeId with IPsAndVMs on NodeId
| graph-match (IP)-[anyEdge*1..3]->(VM)
where set_has_element(IP.Categories, "ip_address") and set_has_element(VM.Categories, "virtual_machine")
project IpIds=IP.EntityIds, IpProperties=IP.NodeProperties.rawData, VmIds=VM.EntityIds, VmProperties=VM.NodeProperties.rawData