Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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
NodePropertiescolumn 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-graphoperator.
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,isnotnulldon'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
SourceNodeIdcolumn uses the data in theExposureGraphEdgestable to theTargetNodeIdcolumn in theExposureGraphNodestable using themake-graphoperator to build a graph structure. - It then uses the
graph-matchoperator to match the graph pattern whereSourceNodeandNodeLabelmatchmicrosoft.compute/virtualmachines. - The
projectoperator 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
ExposureGraphNodesschema table. - When both
NodePropertiesexposedToInternetandvulnerableToRCEare 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
ExposureGraphNodesschema table. - When
NodePropertiesis both internet facing (IsInternetFacing) andVulnerableToPrivilegeEscalation, the query checks that the items inCategoriesare 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
IdentitiesAndCriticalDevicestable usingExposureGraphNodesdata filtered either by devices with a criticality level above 4 or byidentity. - It then makes a graph structure with the
make-graphoperator, where theEdgeLabelisCan Authenticate As. - It uses the
graph-matchoperator to match instances where adevicematches anidentity. - Then it uses the
projectoperator to keep identity IDs and device IDs. - The
mv-applyoperator 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
IdentitiesAndCriticalDevicestable 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 AsandCanRemoteInteractiveLogonTo.
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
ExposureGraphNodesandExposureGraphEdgesschema tables and themake-graphandgraph-matchoperators to create a graph structure. - With the
projectoperator, 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