Last week, I wrote the first part of the XSPM blog series: Microsoft Security Exposure Management (XSPM) Overview. Now, it’s time to continue the story, delve deep into the XSPM solution, and find out how to achieve more with it. The first part can be found here: https://bit.ly/3PZd3BC.

In part two, I will explore the Enterprise Exposure Graph, explain critical assets and their meaning to XSPM, and demonstrate the attack surface map without forgetting raw event data.

Background

Microsoft Security Exposure Management (XSPM) is integrated natively into the Defender XDR portal. It can be imagined as a combination of the next-generation vulnerability management & posture management solution that modernizes posture management in the same way XDR modernizes threat management. Where XDR (detect, investigate, and respond) provides unified threat management for workloads, the XSPM (identify and protect) provides unified exposure management for the same workloads.

As you can see from the figure below (from Microsoft), it complements the story with AI, XDR, SIEM, and Threat Intelligence solutions. These solutions can now be found underneath one portal on the unified Defender XDR platform.

Enterprise Exposure Graph

An Enterprise Exposure graph is in the center of the XSPM solution and stores asset information about different workloads. What is pretty neat is that the exposure graph schema is extended to the existing Defender XDR advanced hunting schemas. This helps to build queries in Defender XDR advanced hunting. At the time of writing, XSPM raw data is divided into two different data tables; ExposureGraphEdges & ExposureGraphNodes.

Table nameDescription
ExposureGraphNodesThis table contains information about entities (devices, users, groups, VMs, and more), and each node corresponds to an individual entity
ExposureGraphEdgesThis table represents the relationships between the nodes in the graph. It contains edges connecting organizational entities such as devices, identities, and virtual machines (VMs)

Critical Assets

What are critical assets in Defender XDR, and how are these related to XSPM? When an asset is identified as critical, the information can be leveraged during investigation. Security teams can prioritize security investigations, posture recommendations, and remediation steps to focus on critical assets first.

Identifying critical assets helps protect your organization’s most important assets against the risk of data breaches and operational disruptions. Defender XDR defines Critical assets automatically (XDR), manually by admin through device inventory or custom queries. Defender XDR has pre-defined templates that identify the following classifications:

  • Domain controllers
  • Databases with sensitive data
  • Groups such as Power Users
  • Roles like Privileged Role Administrator

Below is a list of critical assets in my demo environment. As you can see, I’ve created three custom classifications that can be created based on your organization’s needs to identify assets that are critical for you but not necessarily identified with pre-defined classifications.

Critical asset protection is actually one of the available initiatives in XSPM. If you select that one from the critical asset management blade on the top left corner, you can drill down to initiative metrics and their respective status.

More information about critical asset management can be found on Microsoft Learn and the brand-new Microsoft Critical Asset Protection blog.

Attack Surface Map

Let’s delve deeper into the XSPM and the attack surface (map & attack paths), which are at the heart of the solution. In my opinion, the attack surface feature is pretty neat. When you open it, you will see all your assets (devices, identities, and cloud assets – including critical assets) in a dashboard combining data from various sources. Someone said just a while ago that it’s like a bloodhound in the cloud or even more, but you need to test it yourself and identify whether you find it as beneficial as I do.

When using the search feature (on the top left corner), you can drill down to individual assets and evaluate their connections in the environment. In this example, I’ve selected the ‘mdiadminpaw.feta.fi’ device and can see that it has connections to other resources in the environment. These connections might present an attack path in the environment. Connections in the figure below are:

  • Routes traffic to
  • Can authenticate to
  • Has credentials of & Can authenticate as
  • Contains

In this example, I’m keen to know more about the ‘has credentials of & can authenticate as’ relation. When selecting the user entity, I can see more data and relations from Stefan Hansson’s user entity.

After selecting the user entity, I can see that the user is a member of the sensitive group at on-prem AD (HelpDeskGroup), which can create a possible attack path in my environment. It also shows me the lack of a proper IAM process covering admin accounts.

Critical assets can be easily identified in the figure below, which has a crown icon in the top right corner. The blade on the right opens when I select the group icon containing 4 groups. From here, I can see the sensitive group (HelpDeskGroup). By selecting extract and opening group connections

I’m ending up here, and I can identify Stefan’s connections through used workstations and groups. And voila, Mr.Hansson is a member of the on-prem domain admin & account operators built-in group through HelpDeskGroup membership. Maybe it’s time to redesign the delegation model in my environment 🙂

Identity Connections in Advanced Hunting

If I want to elaborate on this through Defender XDR advanced hunting, here are a few examples that will do the trick. All queries can also be found in the GitHub repo.

//List Sensitive accounts
ExposureGraphNodes
| where NodeLabel == 'user'
| extend Tags = parse_json(tostring(parse_json(tostring(NodeProperties.rawData)).tags.[0]))
| extend NestedADGroups = parse_json(tostring(parse_json(tostring(NodeProperties.rawData)).nestedAdGroupNames))
| mv-expand NestedADGroups
| where Tags == 'Sensitive'

From the output, we can see that the user is a member of high-privileged nested AD groups.

If we want to investigate these further, we can drill down to individual user group memberships by joining the ‘ExposureGraphEdges’ table, which contains information about the relations between the assets.

ExposureGraphNodes
| where NodeName has 'Stefan Hansson'
| where NodeId == '9e75ffd96e0942428351140072a8bea0'
| extend NestedADGroups = parse_json(tostring(parse_json(tostring(NodeProperties.rawData)).nestedAdGroupNames))
| mv-expand NestedADGroups
| where NestedADGroups has 'Domain Admins'
| join kind=inner ExposureGraphEdges on $left.NodeId == $right.SourceNodeId
| where TargetNodeName !has 'SE-Group-1'
| project NodeName, NestedADGroups, SourceNodeId, TargetNodeName, TargetNodeId

Attack Paths

Attack paths show how attackers can get from an entry point to critical assets. XSPM attack paths visualize potential paths attackers might use to breach your environment. Attack paths are created based on critical asset definitions, so it’s important to identify what assets are most important to your organization. Automation helps in this area, but it doesn’t cover all possible scenarios, only the most obvious ones. By understanding attack paths, you can better defend against potential cyber threats and protect your sensitive data and critical assets.

  • Define which assets are critical to your organization. Microsoft uses these to detect potential threats to the most important assets. Once you define your critical assets, it may take up to 12 hours to detect potential attack paths.

XSPM automatically generates attack paths based on the data collected across assets and workloads. It simulates attack scenarios and identifies vulnerabilities and weaknesses that an attacker could exploit.

Side note, according to Microsoft, Attack paths might not be fully representative if you don’t have licenses defined for workloads integrated and represented in the attack path or if you haven’t fully defined critical assets. The value of attack paths increases based on the data used as a source. If no data is available or doesn’t reflect your organization’s environment, attack paths might not appear. Attack paths might not be fully representative if you don’t have licenses defined for workloads integrated and represented in the attack path or if you haven’t fully defined critical assets.

Next, I will elaborate on the XSPM attack paths in my demo environment. I have 6 attack paths related to the virtual machine and storage account in my environment.

When I open one of the attack paths, I can see details from it, including a graph, recommendations, and the possibility of opening the attack path on the map, which helps visualize the big picture.

Attack surface view of hp1 connections and attack paths in the Exposure Management attack surface map.

Defender for Cloud’s (MDC) Attack Path Analysis

What about Defender for Cloud’s security graph explorer and attack paths? Some might wonder about the differences between these two and the future of MDC attack path analysis, considering that XSPM is already ingesting data from MDC.

There is some inconsistency in the attack paths, at least in my environment. XSPM needs Defender CSPM to be enabled to ingest data from MDC. That said, the complete cloud data is available for environments where Defender CSPM is turned on. If it is missing, only partial cloud data will be displayed.

The XSPM attack path relies on Defender XDR’s critical asset definition and MDC data. To the best of my knowledge, findings from MDC and XSPM should be visible in the XSPM attack path, but that’s not the case in my environment, as seen in the figure above. In MDC, I have 12 attack paths, and in XSPM, I have 6.

The future of the MDC attack path analysis feature is a good question. I think it’s not going anywhere because many organizations leverage Azure and MDC features but are protecting devices, collaboration workloads, and identities with non-MS solutions. In such a scenario, MDC attack path analysis would be an awesome addition to the security stack.

Raw Data

As stated earlier, the XSPM raw data is divided into two tables: ExposureGraphNodes and ExposureGraphEdges. The Defender XDR portal offers an advanced hunting feature for querying the data.

During my research, I wrote the following queries: The first is finding Entra ID service principals with contributor roles at the Azure subscription level. By joining the IdentityInfo table into the query, I can get AccountName as the output.

//List Sensitive users, nestedAdGroupNames (DA) and relations
ExposureGraphNodes
| where NodeLabel == 'user'
| extend Tags = parse_json(tostring(parse_json(tostring(NodeProperties.rawData)).tags.[0]))
| extend NestedADGroups = parse_json(tostring(parse_json(tostring(NodeProperties.rawData)).nestedAdGroupNames))
| where Tags == 'Sensitive'
| mv-expand NestedADGroups
| where NestedADGroups has 'Domain Admins'
| join kind=inner ExposureGraphEdges on $left.NodeId == $right.SourceNodeId
| project NodeName, NestedADGroups, SourceNodeId, TargetNodeName


//List SP contributor perms to subscriptions
let Identity = IdentityInfo
| distinct AccountObjectId, AccountName;
ExposureGraphNodes
| where NodeLabel == 'serviceprincipal'
| mv-expand EntityIds
| extend EntityType = tostring(EntityIds.id)
| extend Tags = parse_json(tostring(parse_json(tostring(NodeProperties.rawData)).tags.[0]))
| join kind=inner ExposureGraphEdges on $left.NodeName == $right.SourceNodeName
| extend Permissions = parse_json(tostring(parse_json(tostring(EdgeProperties.rawData)).permissions.roles.[0]))
| extend Role = tostring(Permissions.name)
| where SourceNodeLabel has 'serviceprincipal'
| where EdgeLabel has 'has role on'
| where EdgeProperties has 'contributor'
| where TargetNodeLabel has 'subscriptions'
| join kind=inner Identity on $left.SourceNodeName == $right.AccountObjectId
| project NodeLabel, NodeName,NodeId, EntityIds, EdgeId, EdgeLabel, SourceNodeName, TargetNodeName, TargetNodeLabel, Role, AccountName, AccountObjectId

Microsoft make-graph operator examples

XSPM relies on exposure graph tables and unique exposure graph operators to enable operations over graph structures. The graph is built from tabular data using the make-graph operator and then queried using graph operators. Here are a few graph-oriented query examples by Microsoft that can be used to get started with the make-graph operator.

//List all node labels with an edge to a specific node label
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

//Show all users logged in to more than one critical device
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


//Provide all paths from specific node ID to a node with a specific label
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

Guidance and example queries for the exposure graph are found here.

Summary

Microsoft Security Exposure Management (XSPM) is taking its first steps in the journey, but it seems to be a game changer for helping organizations improve their environmental security posture. I’m already a fan of attack maps and attack path features, and I’m eager to see what’s to come in this area and how Microsoft is developing the solution further.

References

XSPM docs in MS Learn

Queries in GitHub

Query the enterprise exposure graph in XSPM

Overview of attack surface management

XSPM Part 1