Identify internet-facing vulnerabilities, exposed services, and browser extension risks. Create attack surface reduction plans, build KQL-based exposure monitoring, and establish a continuous attack surface management program.
Exposed attack surfaces are the external-facing entry points attackers can target: internet-facing services, misconfigured firewall rules, vulnerable applications, and exposed management interfaces. DVM provides attack surface insights that go beyond traditional vulnerability scanning to show how your organisation looks from an attacker''s perspective.
A penetration test reveals that 15 internet-facing servers have known vulnerabilities, 8 endpoints expose RDP to the internet, and 3 web applications have critical CVEs. The security team must use DVM to identify and prioritise these exposed attack surfaces, create remediation tasks, and monitor reduction over time.
Attackers follow the path of least resistance. Internet-facing vulnerabilities and exposed services are the first targets in any attack campaign. Reducing your exposed attack surface is the single most impactful security action you can take.
Microsoft.Graph module for exposure report automationYour exposure score is the single metric that captures how vulnerable your organisation looks from the outside. Before diving into specific attack surfaces, you need to understand the overall exposure landscape: which devices carry the most risk, how many are internet-facing, and where the critical exploitable vulnerabilities cluster.
// ============================================================
// Internet-Facing Devices with Critical Vulnerabilities
// ============================================================
// WHAT: Identifies devices marked as internet-facing that have
// critical or high severity vulnerabilities with exploits.
// WHY: Internet-facing + exploitable = attacker's first target.
// These devices should be patched within 24โ72 hours.
// TABLE: DeviceTvmSoftwareVulnerabilities
// KEY FIELDS:
// IsInternetFacing - DVM flag based on network topology analysis
// IsExploitAvailable - public exploit exists for this CVE
// VulnerabilitySeverityLevel - Critical/High/Medium/Low
DeviceTvmSoftwareVulnerabilities
| where IsInternetFacing == true
| where VulnerabilitySeverityLevel in ("Critical", "High")
| where IsExploitAvailable == true
| summarize
ExploitableVulns = dcount(CveId),
CveList = make_set(CveId, 5)
by DeviceName, DeviceId, SoftwareName
| order by ExploitableVulns desc
| project DeviceName, SoftwareName, ExploitableVulns, CveListExposed services are the doors attackers walk through. RDP exposed to the internet is responsible for over 50% of ransomware initial access. This step combines vulnerability data with network information to discover exactly which services are listening on internet-facing interfaces and which of those services have known vulnerabilities.
// ============================================================
// Discover Internet-Facing Services with Vulnerable Software
// ============================================================
// WHAT: Joins DeviceTvmSoftwareVulnerabilities with
// DeviceNetworkInfo to find devices that have both
// vulnerable software AND network interfaces with
// public-facing IP addresses.
// WHY: A vulnerability on an internal-only device is far less
// urgent than the same vulnerability on a device with a
// public IP. This query surfaces the highest-risk combo.
// TABLES: DeviceTvmSoftwareVulnerabilities + DeviceNetworkInfo
let vulnerableDevices = DeviceTvmSoftwareVulnerabilities
| where VulnerabilitySeverityLevel in ("Critical", "High")
| where IsExploitAvailable == true
| summarize
CriticalCVEs = dcount(CveId),
VulnSoftware = make_set(SoftwareName, 5)
by DeviceId, DeviceName;
let networkInfo = DeviceNetworkInfo
| where Timestamp > ago(1d)
| mvexpand todynamic(IPAddresses)
| extend IPAddress = tostring(IPAddresses.IPAddress)
| extend IsPublicIP = not(
IPAddress startswith "10." or
IPAddress startswith "172.16." or
IPAddress startswith "172.17." or
IPAddress startswith "172.18." or
IPAddress startswith "172.19." or
IPAddress startswith "172.20." or
IPAddress startswith "172.21." or
IPAddress startswith "172.22." or
IPAddress startswith "172.23." or
IPAddress startswith "172.24." or
IPAddress startswith "172.25." or
IPAddress startswith "172.26." or
IPAddress startswith "172.27." or
IPAddress startswith "172.28." or
IPAddress startswith "172.29." or
IPAddress startswith "172.30." or
IPAddress startswith "172.31." or
IPAddress startswith "192.168." or
IPAddress startswith "127." or
IPAddress startswith "169.254.")
| where IsPublicIP == true
| summarize PublicIPs = make_set(IPAddress, 3) by DeviceId;
vulnerableDevices
| join kind=inner networkInfo on DeviceId
| project DeviceName, CriticalCVEs, VulnSoftware, PublicIPs
| order by CriticalCVEs desc// Find devices with exposed services
DeviceTvmSoftwareVulnerabilities
| where VulnerabilitySeverityLevel == "Critical"
| where IsExploitAvailable == true
| summarize CriticalExploitable = count() by DeviceName, DeviceId
| top 20 by CriticalExploitableBrowser extensions are a blind spot in most security programmes. A single malicious or overly permissive extension can exfiltrate credentials, inject ads into banking sites, or act as a keylogger. DVM's browser extension inventory lets you see every extension installed across your fleet, assess their permissions, and identify the ones that pose the greatest risk.
// ============================================================
// Browser Extension Risk Assessment
// ============================================================
// WHAT: Queries the software inventory for browser extensions
// and counts installations across the fleet.
// WHY: Extensions with broad permissions installed on many
// devices represent high-value targets for supply chain
// attacks. An extension update can push malware to
// thousands of devices in minutes.
DeviceTvmBrowserExtensions
| summarize
InstalledOn = dcount(DeviceId),
SampleDevices = make_set(DeviceName, 3)
by ExtensionName, BrowserName, ExtensionId
| order by InstalledOn desc
| project ExtensionName, BrowserName, InstalledOn, SampleDevicesAttack surface reduction goes beyond patching individual CVEs - it means systematically eliminating the exposure vectors attackers use. This step builds a KQL-based attack surface scoring model that combines vulnerability count, exploit availability, and internet exposure into a single per-device risk score, enabling you to prioritise which devices need attention first.
// ============================================================
// Attack Surface Risk Score per Device
// ============================================================
// WHAT: Calculates a composite risk score for each device based
// on weighted factors: critical vulns, exploit availability,
// internet exposure, and configuration compliance.
// WHY: A single score per device lets you stack-rank your fleet
// and allocate remediation resources where they matter most.
// SCORING MODEL:
// +10 points per Critical CVE with exploit available
// +5 points per High CVE with exploit available
// +3 points per Critical CVE without exploit
// +20 bonus if device is internet-facing
// +15 bonus if device has failed secure config checks
let vulnScores = DeviceTvmSoftwareVulnerabilities
| summarize
CritExploit = countif(VulnerabilitySeverityLevel == "Critical" and IsExploitAvailable == true),
HighExploit = countif(VulnerabilitySeverityLevel == "High" and IsExploitAvailable == true),
CritNoExploit = countif(VulnerabilitySeverityLevel == "Critical" and IsExploitAvailable == false),
IsInternetFacing = max(iff(IsInternetFacing == true, 1, 0))
by DeviceId, DeviceName;
let configScores = DeviceTvmSecureConfigurationAssessment
| where IsApplicable == true and IsCompliant == false
| summarize FailedConfigs = dcount(ConfigurationId) by DeviceId;
vulnScores
| join kind=leftouter configScores on DeviceId
| extend FailedConfigs = coalesce(FailedConfigs, 0)
| extend RiskScore =
(CritExploit * 10) +
(HighExploit * 5) +
(CritNoExploit * 3) +
(IsInternetFacing * 20) +
(iff(FailedConfigs > 5, 15, 0))
| extend RiskTier = case(
RiskScore >= 100, "๐ด Critical",
RiskScore >= 50, "๐ High",
RiskScore >= 20, "๐ก Medium",
"๐ข Low")
| order by RiskScore desc
| project RiskTier, DeviceName, RiskScore, CritExploit,
HighExploit, IsInternetFacing, FailedConfigs
| take 50The exposure score is your north-star metric for attack surface management. Tracking it over time reveals whether your remediation efforts are outpacing new vulnerabilities, whether new deployments are introducing risk, and whether your overall security posture is improving or degrading.
// ============================================================
// Exposure Trend: Exploitable Vulnerability Count Over Time
// ============================================================
// WHAT: Tracks the daily count of unique exploitable CVEs
// across the fleet over the past 30 days.
// WHY: If the line is going up, new vulns are outpacing your
// patching. If it's going down, remediation is winning.
DeviceTvmSoftwareVulnerabilities
| where Timestamp > ago(30d)
| where IsExploitAvailable == true
| where VulnerabilitySeverityLevel in ("Critical", "High")
| summarize
UniqueCVEs = dcount(CveId),
AffectedDevices = dcount(DeviceId)
by Day = bin(Timestamp, 1d)
| order by Day asc
| project Day, UniqueCVEs, AffectedDevicesKQL is the powerhouse behind proactive attack surface monitoring. Beyond the pre-built dashboard, custom queries let you build remediation priority lists that combine vulnerability severity, exploit availability, device criticality, and business context. This step creates a remediation prioritisation query that ranks every open vulnerability by real-world risk.
// ============================================================
// Remediation Prioritisation: Top CVEs by Fleet Impact
// ============================================================
// WHAT: Ranks CVEs by the number of affected devices, showing
// which single patch would remediate the most devices.
// WHY: Patching one widely-deployed CVE can remediate hundreds
// of devices in a single maintenance window - maximum ROI.
DeviceTvmSoftwareVulnerabilities
| where VulnerabilitySeverityLevel in ("Critical", "High")
| summarize
AffectedDevices = dcount(DeviceId),
ExploitAvail = max(iff(IsExploitAvailable == true, "Yes", "No")),
InternetFacing = dcountif(DeviceId, IsInternetFacing == true),
Software = make_set(SoftwareName, 3)
by CveId, VulnerabilitySeverityLevel
| extend Priority = case(
ExploitAvail == "Yes" and InternetFacing > 0, "๐ด P0 - Immediate",
ExploitAvail == "Yes", "๐ P1 - Urgent",
InternetFacing > 0, "๐ก P2 - High",
"๐ข P3 - Standard")
| order by AffectedDevices desc
| project Priority, CveId, VulnerabilitySeverityLevel,
AffectedDevices, InternetFacing, ExploitAvail, Software
| take 30// WHAT: Summarize exploitable vulnerability exposure by device group (MachineGroup)
// WHY: Shows which business units or device groups carry the most risk,
// enabling targeted remediation campaigns per team/department.
// HOW: Joins DeviceInfo (latest device metadata) with vulnerability data
// filtered to Critical/High severity CVEs with known exploits
// KEY FIELDS:
// MachineGroup - the device group assigned in Defender portal (e.g., "Finance Servers",
// "Engineering Workstations") - used for RBAC and reporting segmentation
// IsExploitAvailable - true means a public exploit exists for this CVE
// OUTPUT: Device group, total devices, and total exploitable vulnerabilities per group
DeviceInfo
| where Timestamp > ago(1d)
| summarize arg_max(Timestamp, *) by DeviceId
| join kind=inner (
DeviceTvmSoftwareVulnerabilities
| where VulnerabilitySeverityLevel in ("Critical", "High")
| where IsExploitAvailable == true
| summarize ExploitableVulns = count() by DeviceId
) on DeviceId
| summarize TotalDevices = count(), TotalExploitableVulns = sum(ExploitableVulns) by MachineGroup
| order by TotalExploitableVulns descVulnerability data is most powerful when it informs network segmentation decisions. By sharing DVM exposure findings with your network security team, you can validate that firewall rules actually protect the assets DVM identifies as vulnerable, and create micro-segmentation plans that isolate high-risk devices until they can be patched.
// ============================================================
// Network Exposure Correlation: Vulnerable Devices by Subnet
// ============================================================
// WHAT: Groups vulnerable devices by their network subnet to
// identify which network segments carry the most risk.
// WHY: Network segmentation is a compensating control. If you
// can't patch immediately, isolating the vulnerable subnet
// limits blast radius until remediation is complete.
DeviceTvmSoftwareVulnerabilities
| where VulnerabilitySeverityLevel in ("Critical", "High")
| where IsExploitAvailable == true
| summarize ExploitableVulns = dcount(CveId) by DeviceId, DeviceName
| join kind=inner (
DeviceNetworkInfo
| where Timestamp > ago(1d)
| mvexpand todynamic(IPAddresses)
| extend IPAddress = tostring(IPAddresses.IPAddress)
| extend Subnet = strcat(
extract("^(\\d+\\.\\d+\\.\\d+)", 1, IPAddress), ".0/24")
| where IPAddress !startswith "127."
| summarize arg_max(Timestamp, Subnet) by DeviceId
) on DeviceId
| summarize
DevicesAtRisk = dcount(DeviceId),
TotalVulns = sum(ExploitableVulns),
Devices = make_set(DeviceName, 5)
by Subnet
| order by TotalVulns desc
| project Subnet, DevicesAtRisk, TotalVulns, DevicesNew assets appear constantly: a developer spins up a test VM, a vendor installs monitoring software, or a cloud migration adds internet-facing services. This step uses PowerShell to generate a comprehensive exposure report that can be emailed to leadership weekly, providing visibility into new and changing attack surfaces.
# ============================================================
# Generate Weekly Exposure Report
# ============================================================
# WHAT: Pulls exposure data from the Defender API and generates
# a CSV report with per-device vulnerability summaries,
# internet-facing status, and risk scores.
# WHY: Automated weekly reports ensure leadership has continuous
# visibility into the attack surface without relying on
# manual portal exports.
# PREREQ: Connect-MgGraph -Scopes "SecurityEvents.Read.All"
# ============================================================
Connect-MgGraph -Scopes "SecurityEvents.Read.All"
# Pull vulnerability data
$vulns = Invoke-MgGraphRequest -Method GET `
-Uri "https://graph.microsoft.com/beta/security/vulnerabilities/softwareVulnerabilities?`$top=5000" `
-OutputType PSObject
# Build exposure summary per device
$deviceExposure = $vulns.value |
Group-Object -Property deviceName |
ForEach-Object {
$criticalCount = ($_.Group | Where-Object {
$_.vulnerabilitySeverityLevel -eq 'Critical' -and
$_.isExploitAvailable -eq $true
}).Count
$highCount = ($_.Group | Where-Object {
$_.vulnerabilitySeverityLevel -eq 'High' -and
$_.isExploitAvailable -eq $true
}).Count
$internetFacing = ($_.Group | Where-Object {
$_.isInternetFacing -eq $true
}).Count -gt 0
[PSCustomObject]@{
DeviceName = $_.Name
TotalVulns = $_.Count
CritExploitable = $criticalCount
HighExploitable = $highCount
InternetFacing = $internetFacing
RiskScore = ($criticalCount * 10) + ($highCount * 5) +
$(if ($internetFacing) { 20 } else { 0 })
}
} |
Sort-Object RiskScore -Descending
# Export report
$reportPath = "C:\Reports\DVM-Exposure-$(Get-Date -Format 'yyyy-MM-dd').csv"
$deviceExposure | Export-Csv -Path $reportPath -NoTypeInformation
Write-Host "โ
Exposure report: $reportPath" -ForegroundColor Green
Write-Host " Devices analysed: $($deviceExposure.Count)"
Write-Host " Critical risk: $(($deviceExposure | Where-Object { $_.RiskScore -ge 100 }).Count)"
Write-Host " Internet-facing: $(($deviceExposure | Where-Object { $_.InternetFacing }).Count)"Executive-level attack surface reports must speak the language of risk, not CVE IDs. This step transforms raw vulnerability data into business-relevant metrics: exposure score trends, devices at risk from active campaigns, remediation progress as a percentage, and estimated risk reduction from planned patches.
// ============================================================
// Executive Dashboard: Attack Surface Summary
// ============================================================
// WHAT: Single-query executive summary showing total exposure,
// remediation coverage, and internet-facing risk.
// WHY: Provides a 30-second snapshot for CISO briefings.
DeviceTvmSoftwareVulnerabilities
| summarize
TotalDevices = dcount(DeviceId),
TotalCVEs = dcount(CveId),
CriticalCVEs = dcountif(CveId, VulnerabilitySeverityLevel == "Critical"),
ExploitableCVEs = dcountif(CveId, IsExploitAvailable == true),
InternetFacingDevs = dcountif(DeviceId, IsInternetFacing == true),
CritInternetFacing = dcountif(CveId,
VulnerabilitySeverityLevel == "Critical" and
IsExploitAvailable == true and
IsInternetFacing == true)
| extend RiskLevel = case(
CritInternetFacing > 10, "๐ด CRITICAL - Internet-facing exploitable CVEs exceed threshold",
CritInternetFacing > 0, "๐ HIGH - Some internet-facing exploitable CVEs remain",
ExploitableCVEs > 50, "๐ก MEDIUM - Internal exploitable CVEs need attention",
"๐ข LOW - Exposure well managed")
| project TotalDevices, TotalCVEs, CriticalCVEs,
ExploitableCVEs, InternetFacingDevs,
CritInternetFacing, RiskLevelAttack surface management is not a project with an end date - it is an ongoing programme. This final step establishes the operational cadence, roles, and metrics that turn one-time assessments into a sustainable, continuous process that keeps your organisation's exposure under control.
| Resource | Description |
|---|---|
| Exposure score overview | Understand how DVM calculates your organisation's exposure score |
| DeviceTvmSoftwareVulnerabilities | Advanced Hunting schema for software vulnerability data |
| DeviceNetworkInfo | Network configuration and connectivity data per device |
| Browser extensions inventory | Discover and assess browser extension risks across your fleet |
| Remediation activities | Create and track vulnerability remediation tasks |
| MITRE ATT&CK T1190 | Initial Access via Exploit Public-Facing Application |