Correlate vulnerability data with active threat campaigns, exploit intelligence, and MITRE ATT&CK techniques. Build threat-informed remediation priorities, create risk analytics dashboards, and design a threat-informed vulnerability management program.
Threat intelligence integration transforms vulnerability management from a score-based prioritisation exercise into a threat-informed defence strategy. By correlating DVM data with active threat campaigns, exploit intelligence, and risk analytics, you can focus remediation on the vulnerabilities that attackers are actually exploiting today. not just those with high CVSS scores.
A security team faces 15,000 CVEs across their estate but can only patch 200 per week. By integrating threat intelligence, they identify 45 CVEs actively exploited in the wild by threat actors targeting their industry. These 45 become the immediate priority, reducing real-world risk by 80% in the first remediation cycle.
Only 5% of CVEs are ever exploited in the wild. Patching based on CVSS scores alone wastes resources on vulnerabilities that will never be attacked. Threat intelligence focuses your limited remediation capacity on the vulnerabilities that matter most.
Microsoft.Graph module; internet access for CISA KEV API callsDVM's exploit prediction engine uses machine learning to assess the likelihood that each CVE will be exploited in the next 30 days. Combined with the "exploit available" flag (indicating a public exploit already exists), this gives you two powerful signals for prioritisation that go far beyond static CVSS scores.
// ============================================================
// Exploit Prediction Analysis: Highest-Risk CVEs
// ============================================================
// WHAT: Identifies CVEs in your environment that have both a
// public exploit AND high exploit prediction likelihood.
// WHY: These are the CVEs most likely to be used in an attack
// against your organisation in the next 30 days. They
// should be your #1 patching priority.
// TABLE: DeviceTvmSoftwareVulnerabilities
DeviceTvmSoftwareVulnerabilities
| where IsExploitAvailable == true
| where VulnerabilitySeverityLevel in ("Critical", "High")
| summarize
AffectedDevices = dcount(DeviceId),
InternetFacing = dcountif(DeviceId, IsInternetFacing == true),
AffectedSoftware = make_set(SoftwareName, 3)
by CveId, VulnerabilitySeverityLevel
| order by AffectedDevices desc
| extend PatchPriority = case(
InternetFacing > 0 and AffectedDevices > 50,
"๐ด EMERGENCY - Patch within 24 hours",
InternetFacing > 0,
"๐ URGENT - Patch within 72 hours",
AffectedDevices > 100,
"๐ก HIGH - Patch within 7 days",
"๐ข STANDARD - Patch within 30 days")
| project PatchPriority, CveId, VulnerabilitySeverityLevel,
AffectedDevices, InternetFacing, AffectedSoftwareThreat analytics in Defender XDR provides curated intelligence reports about active threat campaigns, including which CVEs threat actors are actively exploiting. By joining this intelligence with your vulnerability inventory, you can determine whether any active campaign directly threatens your environment - and take pre-emptive action.
// ============================================================
// Correlate Vulnerabilities with Threat Intelligence Indicators
// ============================================================
// WHAT: Joins DeviceTvmSoftwareVulnerabilities with threat
// intelligence indicator data to find CVEs in your
// environment that are associated with known threat actors.
// WHY: A CVE associated with an active threat campaign is
// infinitely more urgent than one with no known adversary
// interest, regardless of CVSS score.
// TABLES: DeviceTvmSoftwareVulnerabilities + ThreatIntelligenceIndicator
let tiCVEs = ThreatIntelligenceIndicator
| where isnotempty(Description)
| where Description matches regex "CVE-\\d{4}-\\d+"
| extend ThreatCVE = extract("(CVE-\\d{4}-\\d+)", 1, Description)
| where isnotempty(ThreatCVE)
| summarize
ThreatSources = make_set(SourceSystem, 3),
ThreatTypes = make_set(ThreatType, 3),
Confidence = max(ConfidenceScore)
by ThreatCVE;
DeviceTvmSoftwareVulnerabilities
| summarize
AffectedDevices = dcount(DeviceId),
InternetFacing = dcountif(DeviceId, IsInternetFacing == true),
Severity = max(VulnerabilitySeverityLevel),
Software = make_set(SoftwareName, 3)
by CveId
| join kind=inner (tiCVEs) on $left.CveId == $right.ThreatCVE
| project CveId, Severity, AffectedDevices, InternetFacing,
Software, ThreatSources, ThreatTypes, Confidence
| order by AffectedDevices descMapping vulnerabilities to MITRE ATT&CK techniques creates a bridge between vulnerability management and threat detection. It answers the question: "If this vulnerability is exploited, what can the attacker do next?" This threat-chain thinking helps you prioritise patches that block the most dangerous attack paths, not just the highest CVSS scores.
// ============================================================
// Map Exploitable Vulnerabilities to ATT&CK Techniques
// ============================================================
// WHAT: Correlates alerts with known ATT&CK technique IDs and
// joins with vulnerability data to show which attack
// techniques your unpatched CVEs enable.
// WHY: Answering "this CVE enables Privilege Escalation" is
// far more actionable than "this CVE has CVSS 9.8".
let exploitableVulns = DeviceTvmSoftwareVulnerabilities
| where IsExploitAvailable == true
| where VulnerabilitySeverityLevel in ("Critical", "High")
| summarize
VulnDevices = dcount(DeviceId),
CVEs = make_set(CveId, 5)
by SoftwareName, SoftwareVersion;
let attackTechniques = AlertInfo
| where Timestamp > ago(30d)
| where isnotempty(AttackTechniques)
| mvexpand todynamic(AttackTechniques)
| extend Technique = tostring(AttackTechniques)
| summarize AlertCount = count(),
Titles = make_set(Title, 3)
by Technique;
attackTechniques
| order by AlertCount desc
| project Technique, AlertCount, Titles
| take 20CISA's Known Exploited Vulnerabilities (KEV) catalogue is the most authoritative source of CVEs actively exploited in the wild. Federal agencies are required to patch KEV entries within strict timelines, and every organisation should treat this catalogue as a minimum patching standard. This step builds a KQL query that matches your vulnerability inventory against the KEV catalogue to surface CVEs requiring immediate action.
// ============================================================
// CISA KEV Matching: Find Known-Exploited CVEs in Your Estate
// ============================================================
// WHAT: Matches your DVM vulnerability inventory against a
// reference list of CISA Known Exploited Vulnerabilities.
// WHY: KEV CVEs are CONFIRMED actively exploited. They are
// not predictions - they are facts. Any KEV CVE in your
// environment is a ticking clock.
// SETUP: Populate the kevList with current KEV entries.
// Download from: https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
// Or use the externaldata() operator to query live.
// NOTE: Replace the sample CVEs below with your current KEV list
let kevList = dynamic([
"CVE-2024-21887", "CVE-2024-21893", "CVE-2023-46805",
"CVE-2024-3400", "CVE-2024-1709", "CVE-2024-1708",
"CVE-2024-27198", "CVE-2023-22515", "CVE-2023-22518",
"CVE-2023-46747", "CVE-2023-4966", "CVE-2024-0012",
"CVE-2024-9474", "CVE-2023-20198", "CVE-2023-27997"
]);
DeviceTvmSoftwareVulnerabilities
| where CveId in (kevList)
| summarize
AffectedDevices = dcount(DeviceId),
InternetFacing = dcountif(DeviceId, IsInternetFacing == true),
Severity = max(VulnerabilitySeverityLevel),
Software = make_set(SoftwareName, 3),
SampleDevices = make_set(DeviceName, 5)
by CveId
| extend KEVStatus = "๐จ CISA KEV - Confirmed Active Exploitation"
| extend SLA = case(
InternetFacing > 0, "Patch within 24 hours",
AffectedDevices > 50, "Patch within 72 hours",
"Patch within 14 days")
| order by AffectedDevices desc
| project KEVStatus, CveId, Severity, AffectedDevices,
InternetFacing, SLA, Software, SampleDevices# ============================================================
# Download and Cache CISA KEV Catalogue for KQL Correlation
# ============================================================
# WHAT: Downloads the live CISA KEV JSON feed and extracts
# the CVE IDs for use in KQL queries.
# WHY: Keeps your KEV reference list current without manual
# updates. The KEV catalogue is updated multiple times
# per week as new actively-exploited CVEs are added.
# ============================================================
$kevUrl = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
$response = Invoke-RestMethod -Uri $kevUrl -Method Get
# Extract CVE IDs
$kevCVEs = $response.vulnerabilities | Select-Object -ExpandProperty cveID
Write-Host "๐จ CISA KEV contains $($kevCVEs.Count) actively exploited CVEs" -ForegroundColor Red
# Format for KQL dynamic list
$kqlList = $kevCVEs | ForEach-Object { "`"$_`"" }
$kqlDynamic = "let kevList = dynamic([`n " + ($kqlList -join ",`n ") + "`n]);"
# Save to file for pasting into Advanced Hunting
$kqlDynamic | Out-File -FilePath "C:\Reports\cisa-kev-kql-list.txt" -Encoding UTF8
Write-Host "โ
KQL kevList saved to C:\Reports\cisa-kev-kql-list.txt" -ForegroundColor Green
Write-Host " Paste the contents into your Advanced Hunting query" -ForegroundColor GrayDVM's event timeline tracks the lifecycle of every vulnerability: when it was disclosed, when an exploit became available, when threat actors started using it, and when a patch was released. This chronological view lets you assess your exposure window - the time between exploit availability and patch deployment - and set SLAs to shrink it.
// ============================================================
// Vulnerability Exposure Window Analysis
// ============================================================
// WHAT: Identifies CVEs that have been exploitable in your
// environment for the longest time without remediation.
// WHY: The longer a CVE with an exploit sits unpatched, the
// greater the chance an attacker uses it. Long exposure
// windows indicate broken patch management processes.
DeviceTvmSoftwareVulnerabilities
| where IsExploitAvailable == true
| where VulnerabilitySeverityLevel in ("Critical", "High")
| summarize
FirstSeen = min(Timestamp),
AffectedDevices = dcount(DeviceId),
Software = make_set(SoftwareName, 3)
by CveId, VulnerabilitySeverityLevel
| extend ExposureDays = datetime_diff('day', now(), FirstSeen)
| where ExposureDays > 30 // Unpatched for over 30 days
| order by ExposureDays desc
| extend ExposureRisk = case(
ExposureDays > 90, "๐ด CRITICAL - 90+ days unpatched",
ExposureDays > 60, "๐ HIGH - 60+ days unpatched",
"๐ก ELEVATED - 30+ days unpatched")
| project ExposureRisk, CveId, VulnerabilitySeverityLevel,
ExposureDays, AffectedDevices, Software, FirstSeenRisk analytics dashboards are the command centre for threat-informed vulnerability management. They combine vulnerability exposure data with exploit intelligence and threat campaign information into a single view that answers the question every CISO asks: "How much real-world risk do we have right now, and is it going up or down?" This step builds a risk-based remediation workflow query.
// ============================================================
// Risk-Based Remediation Workflow
// ============================================================
// WHAT: Builds a complete remediation priority queue that
// factors in: severity, exploit availability, internet
// exposure, fleet impact, and exposure duration.
// WHY: This is the single query your patch management team
// should run every morning to decide what to patch today.
// OUTPUT: Ranked CVE list with SLA, affected devices, and
// recommended remediation action.
DeviceTvmSoftwareVulnerabilities
| where VulnerabilitySeverityLevel in ("Critical", "High")
| summarize
AffectedDevices = dcount(DeviceId),
InternetFacing = dcountif(DeviceId, IsInternetFacing == true),
ExploitAvail = max(iff(IsExploitAvailable == true, 1, 0)),
FirstSeen = min(Timestamp),
Software = make_set(SoftwareName, 3),
DeviceList = make_set(DeviceName, 5)
by CveId, VulnerabilitySeverityLevel
| extend ExposureDays = datetime_diff('day', now(), FirstSeen)
| extend RiskScore =
(iff(VulnerabilitySeverityLevel == "Critical", 40, 20)) +
(ExploitAvail * 30) +
(iff(InternetFacing > 0, 20, 0)) +
(iff(AffectedDevices > 100, 10, 0))
| extend RemediationSLA = case(
RiskScore >= 90, "24 hours",
RiskScore >= 70, "72 hours",
RiskScore >= 50, "7 days",
RiskScore >= 30, "14 days",
"30 days")
| extend Action = case(
InternetFacing > 0 and ExploitAvail == 1,
"EMERGENCY PATCH + network isolation until patched",
ExploitAvail == 1,
"URGENT PATCH + monitor for exploitation attempts",
InternetFacing > 0,
"PRIORITY PATCH + WAF/IPS rule as compensating control",
"STANDARD PATCH via next maintenance window")
| order by RiskScore desc
| project RiskScore, RemediationSLA, CveId,
VulnerabilitySeverityLevel, AffectedDevices,
InternetFacing, ExposureDays, Action, Software
| take 50// WHAT: Executive summary of vulnerability exposure with threat-informed prioritization
// WHY: Provides a single-view risk dashboard showing total critical/high vulnerabilities
// broken down by exploitability and internet exposure status.
// ExploitAvailable = the TRUE priority indicator (not just CVSS score)
// TABLE: DeviceTvmSoftwareVulnerabilities
// KEY FIELDS:
// VulnerabilitySeverityLevel - Critical | High | Medium | Low (DVM's enriched severity)
// IsExploitAvailable - true if a public exploit exists (most actionable metric)
// IsInternetFacing - true if the vulnerable device is exposed to the internet
// OUTPUT: Total vulns, exploitable count, internet-facing count, and high-priority count
DeviceTvmSoftwareVulnerabilities
| where VulnerabilitySeverityLevel in ("Critical", "High")
| summarize TotalVulns = count(),
ExploitAvailable = countif(IsExploitAvailable == true),
InternetFacing = countif(IsInternetFacing == true)
| extend HighPriority = ExploitAvailable // Exploitable = highest priority
| project TotalVulns, ExploitAvailable, InternetFacing, HighPriorityDetection is the counterpart to prevention. While DVM tells you what could be exploited, hunting for exploitation evidence tells you what is being exploited. By querying endpoint detection events for blocked exploit attempts, you can confirm which CVEs attackers are actively targeting in your environment and escalate their remediation accordingly.
// WHAT: Detect active exploitation attempts blocked by Defender for Endpoint
// WHY: These events confirm that attackers are actively targeting your environment.
// CVEs being exploited against YOUR devices should be patched immediately.
// TABLE: DeviceEvents - endpoint detection events from MDE sensor
// KEY ActionType values for exploit detection:
// ExploitGuardNetworkProtectionBlocked - network exploit blocked by Network Protection
// ExploitGuardASRViolationBlocked - blocked by Attack Surface Reduction (ASR) rules
// ASR rules protect against common attack vectors: Office macro execution,
// credential theft from LSASS, ransomware delivery, etc.
// OUTPUT: Device name, blocked action type, target URL, and attempt count
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType in ("ExploitGuardNetworkProtectionBlocked", "ExploitGuardASRViolationBlocked")
| summarize Attempts = count() by DeviceName, ActionType, RemoteUrl
| order by Attempts desc// ============================================================
// Correlate Exploit Blocks with Unpatched Vulnerabilities
// ============================================================
// WHAT: Cross-references devices that blocked exploit attempts
// with their current vulnerability inventory to find
// devices under active attack that still have unpatched CVEs.
// WHY: A device blocking exploits today may be successfully
// exploited tomorrow if a new attack vector is found.
// These devices need immediate patching.
let exploitTargets = DeviceEvents
| where Timestamp > ago(7d)
| where ActionType in (
"ExploitGuardNetworkProtectionBlocked",
"ExploitGuardASRViolationBlocked")
| summarize
BlockedAttempts = count(),
AttackTypes = make_set(ActionType, 3)
by DeviceId, DeviceName;
let vulnerableDevices = DeviceTvmSoftwareVulnerabilities
| where VulnerabilitySeverityLevel in ("Critical", "High")
| where IsExploitAvailable == true
| summarize
OpenCVEs = dcount(CveId),
CriticalCVEs = dcountif(CveId, VulnerabilitySeverityLevel == "Critical"),
VulnSoftware = make_set(SoftwareName, 3)
by DeviceId;
exploitTargets
| join kind=inner vulnerableDevices on DeviceId
| extend Urgency = "๐จ UNDER ACTIVE ATTACK + UNPATCHED"
| order by BlockedAttempts desc
| project Urgency, DeviceName, BlockedAttempts, AttackTypes,
OpenCVEs, CriticalCVEs, VulnSoftwareAutomated alerting closes the gap between intelligence discovery and remediation action. This step creates custom detection rules and a PowerShell-based TI-enriched dashboard that combines DVM vulnerability data with CISA KEV status and exploit prediction scores into a single prioritised view.
# ============================================================
# TI-Enriched Vulnerability Dashboard Generator
# ============================================================
# WHAT: Pulls DVM vulnerability data, enriches with CISA KEV
# status, and generates an HTML dashboard showing the
# threat-informed priority queue.
# WHY: Combines multiple data sources into a single actionable
# view that can be emailed to leadership or displayed on
# a SOC wall monitor.
# ============================================================
Connect-MgGraph -Scopes "SecurityEvents.Read.All"
# Step 1: Get CISA KEV list
$kevData = Invoke-RestMethod -Uri "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
$kevCVEs = $kevData.vulnerabilities | Select-Object -ExpandProperty cveID
Write-Host "Loaded $($kevCVEs.Count) CISA KEV entries" -ForegroundColor Cyan
# Step 2: Pull DVM vulnerability data
$vulns = Invoke-MgGraphRequest -Method GET `
-Uri "https://graph.microsoft.com/beta/security/vulnerabilities/softwareVulnerabilities?`$top=5000" `
-OutputType PSObject
# Step 3: Enrich with KEV status and calculate risk score
$enrichedVulns = $vulns.value |
Group-Object -Property cveId |
ForEach-Object {
$cve = $_.Name
$devices = $_.Group
$severity = ($devices | Select-Object -First 1).vulnerabilitySeverityLevel
$exploitable = ($devices | Where-Object { $_.isExploitAvailable }).Count -gt 0
$internetFacing = ($devices | Where-Object { $_.isInternetFacing }).Count
$isKEV = $cve -in $kevCVEs
# Calculate composite risk score
$score = 0
if ($severity -eq 'Critical') { $score += 40 }
if ($severity -eq 'High') { $score += 20 }
if ($exploitable) { $score += 30 }
if ($internetFacing -gt 0) { $score += 20 }
if ($isKEV) { $score += 50 } # KEV = highest weight
[PSCustomObject]@{
CVE = $cve
Severity = $severity
AffectedDevices = $devices.Count
InternetFacing = $internetFacing
ExploitAvail = $exploitable
CISA_KEV = $isKEV
RiskScore = $score
SLA = switch {
($score -ge 90) { "24 hours" }
($score -ge 70) { "72 hours" }
($score -ge 50) { "7 days" }
default { "30 days" }
}
}
} |
Sort-Object RiskScore -Descending
# Step 4: Display top priorities
Write-Host "`n๐จ TOP 20 THREAT-INFORMED PRIORITIES" -ForegroundColor Red
Write-Host ("โ" * 80) -ForegroundColor Cyan
$enrichedVulns | Select-Object -First 20 |
Format-Table CVE, Severity, RiskScore, SLA, AffectedDevices,
InternetFacing, ExploitAvail, CISA_KEV -AutoSize
# Step 5: Export full report
$reportPath = "C:\Reports\TI-Enriched-Vulns-$(Get-Date -Format 'yyyy-MM-dd').csv"
$enrichedVulns | Export-Csv -Path $reportPath -NoTypeInformation
Write-Host "โ
Full report: $reportPath" -ForegroundColor GreenRisk reports should tell the story of your threat-informed vulnerability management programme: what the real-world threat landscape looks like, how your exposure maps to active campaigns, what you remediated, and what remains. Frame every data point in terms of business risk, not technical severity.
// ============================================================
// Monthly Risk Report: Remediation SLA Compliance
// ============================================================
// WHAT: Tracks whether critical and high-severity exploitable
// CVEs were remediated within their assigned SLA windows.
// WHY: SLA compliance is the operational metric that proves
// your threat-informed VM programme is working.
DeviceTvmSoftwareVulnerabilities
| where VulnerabilitySeverityLevel in ("Critical", "High")
| where IsExploitAvailable == true
| summarize
FirstSeen = min(Timestamp),
AffectedDevices = dcount(DeviceId)
by CveId, VulnerabilitySeverityLevel
| extend ExposureDays = datetime_diff('day', now(), FirstSeen)
| extend SLA = case(
VulnerabilitySeverityLevel == "Critical", 7,
VulnerabilitySeverityLevel == "High", 30,
90)
| extend SLAStatus = iff(ExposureDays > SLA,
"๐ด SLA BREACHED", "๐ข Within SLA")
| summarize
TotalCVEs = count(),
WithinSLA = countif(ExposureDays <= SLA),
SLABreached = countif(ExposureDays > SLA)
by VulnerabilitySeverityLevel
| extend SLACompliance = strcat(
tostring(round(100.0 * WithinSLA / TotalCVEs, 1)), "%")
| project VulnerabilitySeverityLevel, TotalCVEs,
WithinSLA, SLABreached, SLAComplianceA threat-informed vulnerability management programme is the culmination of all previous steps: it embeds threat intelligence into every prioritisation decision, uses automated tooling to surface urgent CVEs, tracks remediation against SLAs, and reports risk in business language. This final step establishes the operational cadence that makes it sustainable.
| Resource | Description |
|---|---|
| Exploit prediction | How DVM predicts likelihood of exploitation for each CVE |
| Threat analytics | Track active threat campaigns and their targeted vulnerabilities |
| CISA KEV Catalogue | Authoritative list of CVEs actively exploited in the wild |
| Event timeline | Track vulnerability lifecycle events: disclosure, exploit, patch |
| MITRE ATT&CK Framework | Map vulnerabilities to adversary techniques and tactics |
| DeviceTvmSoftwareVulnerabilities | Advanced Hunting schema for vulnerability-threat correlation |