Skip to main content

Skillber v1.0 is here!

Learn more

Vulnerability Prioritization

Checking access...

Not all vulnerabilities are equal. A risk-based prioritization approach considers not just severity (CVSS) but also exploitability (EPSS), active exploitation (KEV), asset criticality, and business context.

The Prioritization Problem

A typical enterprise faces:

  • 10,000+ vulnerabilities across all assets
  • 500+ new vulnerabilities published per week
  • Limited remediation capacity (patches take time, require change windows)

Without prioritization, teams chase the loudest alerts rather than the most dangerous risks.

The 5% Rule:
└─ 5% of vulnerabilities account for 95% of exploitation
└─ Most CVSS Critical vulnerabilities are never exploited
└─ Some CVSS Medium vulnerabilities are weaponized immediately
└─ Prioritization based on CVSS alone is not risk-based

Prioritization Frameworks

CVSS (Base Severity)

Good for: Standardised severity scoring
Limitation: Measures technical severity, not likelihood of exploitation

CVSS ScoreSeverityResponse Time
9.0-10.0Critical7 days
7.0-8.9High30 days
4.0-6.9Medium90 days
0.1-3.9Low180 days

EPSS (Exploit Prediction Scoring System)

EPSS predicts the likelihood that a vulnerability will be exploited in the next 30 days.

EPSS Score:
└─ 0.0 (very unlikely) to 1.0 (almost certain to be exploited)
└─ Updated daily based on real-world exploitation data
└─ Measures exploitation LIKELIHOOD, not impact
Example EPSS Scores:
└─ CVE-2023-44487 (HTTP/2 Rapid Reset): EPSS 0.97 (97% chance)
└─ CVE-2024-1709 (ScreenConnect auth bypass): EPSS 0.95
└─ Average vulnerability: EPSS 0.002 (0.2% chance)

KEV (Known Exploited Vulnerabilities)

CISA’s catalog of vulnerabilities known to be actively exploited.

KEV Catalog:
└─ Maintained by CISA (US Cybersecurity & Infrastructure Security Agency)
└─ Vulnerabilities confirmed as actively exploited in the wild
└─ Binding Operational Directive (BOD) 22-01: Federal agencies must patch KEV within 14 days
└─ ~1,000+ vulnerabilities in the catalog (growing daily)
Priority: KEV vulnerabilities should be patched BEFORE any other vulnerability,
regardless of CVSS score. If it's being exploited, it poses active risk.

Combined Prioritization Model

Risk Score = f(CVSS, EPSS, KEV, AssetCriticality, ThreatContext)
Tier 1 — IMMEDIATE (Patch within 24-48 hours):
└─ CVE in KEV catalog AND on critical asset
└─ CVE with EPSS > 0.90 AND CVSS > 9.0 AND on internet-facing system
└─ Active exploit observed in the wild targeting your industry
Tier 2 — HIGH (Patch within 7 days):
└─ CVE in KEV catalog (any asset)
└─ CVE with EPSS > 0.50 AND CVSS > 7.0 AND on critical asset
└─ Newly published RCE with public exploit code
Tier 3 — MEDIUM (Patch within 30 days):
└─ CVSS > 7.0 on critical asset
└─ CVSS > 9.0 on non-critical asset
└─ EPSS > 0.50 any asset
Tier 4 — LOW (Patch within 90 days):
└─ CVSS < 7.0 on non-critical asset
└─ Low EPSS, not in KEV

Asset Criticality

Asset Criticality Tiers:
Tier 0 — Critical:
└─ Internet-facing systems
└─ PII/PHI databases
└─ Authentication systems (AD, SSO)
└─ Payment processing
└─ Patch target: CVSS Critical in 7 days, High in 30 days
Tier 1 — High:
└─ Internal production servers
└─ Internal databases
└─ CI/CD systems
└─ Patch target: CVSS Critical in 14 days, High in 45 days
Tier 2 — Medium:
└─ Development environments
└─ Corporate workstations
└─ Patch target: CVSS Critical in 30 days, High in 60 days
Tier 3 — Low:
└─ Test environments (isolated)
└─ Legacy systems (in process of decommission)

Automation

Terminal window
# Query CISA KEV catalog via API
curl -s "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json" | \
jq -r '.vulnerabilities[] | select(.dateAdded > "2024-01-01") | [.cveID, .vulnerabilityName, .dateAdded] | @tsv'
# Check EPSS score for a CVE
curl -s "https://api.first.org/data/v1/epss?cve=CVE-2024-1709" | \
jq -r '.data[0] | "\(.cve) — EPSS: \(.epss) — Percentile: \(.percentile)"'
# Prioritize vulnerabilities using combined score
python3 << 'PYEOF'
# Simple prioritization engine
vulnerabilities = [
{"cve": "CVE-2024-1709", "cvss": 9.1, "epss": 0.95, "kev": True, "asset": "internet-facing"},
{"cve": "CVE-2024-1234", "cvss": 7.5, "epss": 0.02, "kev": False, "asset": "internal"},
{"cve": "CVE-2023-44487", "cvss": 7.5, "epss": 0.97, "kev": True, "asset": "internal"},
{"cve": "CVE-2024-5678", "cvss": 5.0, "epss": 0.80, "kev": False, "asset": "critical"},
]
for vuln in vulnerabilities:
score = 0
score += vuln["cvss"] * 10 # CVSS contribution
score += vuln["epss"] * 100 # EPSS contribution
if vuln["kev"]:
score += 500 # KEV bonus — being exploited NOW
if vuln["asset"] == "internet-facing":
score += 200
elif vuln["asset"] == "critical":
score += 100
print(f'{vuln["cve"]:20s} Risk Score: {score:5.0f} | {"IMMEDIATE" if score > 800 else "HIGH" if score > 500 else "MEDIUM" if score > 200 else "LOW"}')
PYEOF

Key Takeaways

  • CVSS alone is insufficient for prioritization — it measures technical severity, not likelihood of exploitation
  • EPSS predicts exploitation likelihood based on real-world data — use it to filter the noise
  • CISA KEV catalog lists vulnerabilities being actively exploited NOW — these get highest priority regardless of CVSS
  • Asset criticality must be factored in — a CVSS 10 on an isolated test server is lower risk than a CVSS 7 on an internet-facing system
  • A combined risk score (CVSS × EPSS + KEV + AssetCriticality) provides better prioritization than any single metric
  • The 5% rule: 5% of vulnerabilities account for 95% of exploitation — prioritize the 5%
  • Automation is essential — manually triaging 10,000+ vulnerabilities is not feasible
  • The KEV catalog should be checked daily and integrated into automated patching workflows
  • Different assets have different SLA requirements — internet-facing critical systems need faster patching than isolated test environments
  • Prioritization is a continuous process — EPSS scores update daily, new KEV entries are added weekly, and asset criticality changes over time

Vulnerability Scoring in Practice

Real-World Scoring Examples

CVECVSSEPSSKEVCVSS-Based PriorityRisk-Based Priority
CVE-2023-44487 (HTTP/2 Rapid Reset)7.5 (High)0.97YesMedium (CVSS 7.5)IMMEDIATE (actively exploited DDoS)
CVE-2024-1709 (ScreenConnect)9.1 (Critical)0.95YesCriticalIMMEDIATE (RCE, public exploit)
CVE-2023-46604 (Apache ActiveMQ)10.0 (Critical)0.90YesCriticalIMMEDIATE (unauthenticated RCE)
CVE-2023-xxx (lib vulnerability)9.8 (Critical)0.001NoCriticalMEDIUM (no exploit seen, rare lib)
CVE-2024-xxx (obscure software)7.5 (High)0.0001NoMediumLOW (no exploit, no public PoC)

The key insight: CVSS 10 does not equal “patch immediately if EPSS is 0.001% and no exploitation is observed.

CVSS Limitations

Why CVSS Alone Fails for Prioritization:
└─ No exploitation likelihood: CVSS measures technical severity, not whether anyone is exploiting it
└─ No business context: CVSS 9.0 on a test server is lower risk than CVSS 7.0 on an internet-facing payment system
└─ No temporal scoring: A CVE published today should be scored differently than one from 5 years ago
└─ No environmental scoring: Most organisations never customize the environmental metrics
└─ Score inflation: Vendors push for higher CVSS scores, leading to "CVSS fatigue"
└─ Binary thinking: Organisations often treat CVSS 6.9 (Medium) as "ignore" and CVSS 7.0 (High) as "must fix" — a 0.1 difference should not drive prioritization

Prioritization Automation

Automated Prioritization Pipeline

#!/usr/bin/env python3
"""Automated vulnerability prioritization engine"""
import requests
import json
from datetime import datetime, timedelta
# Configuration
KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
EPSS_API = "https://api.first.org/data/v1/epss"
ASSET_CRITICALITY = {
"internet-facing": 200,
"critical": 100,
"high": 50,
"medium": 20,
"low": 5
}
def fetch_kev():
"""Fetch CISA KEV catalog"""
resp = requests.get(KEV_URL)
return {v['cveID'] for v in resp.json()['vulnerabilities']}
def fetch_epss(cve_ids):
"""Fetch EPSS scores for CVEs"""
cv = ','.join(cve_ids[:100]) # EPSS API limits to 100 per request
resp = requests.get(f"{EPSS_API}?cve={cv}")
scores = {}
for item in resp.json().get('data', []):
scores[item['cve']] = float(item['epss'])
return scores
def prioritize(vulnerabilities):
"""Calculate risk score and priority tier"""
kev = fetch_kev()
results = []
for vuln in vulnerabilities:
risk_score = 0
risk_score += vuln['cvss'] * 10 # CVSS contribution
# EPSS contribution (if available)
epss = vuln.get('epss', 0)
risk_score += epss * 100
# KEV bonus — being exploited NOW
if vuln['cve'] in kev:
risk_score += 500
# Asset criticality
asset = vuln.get('asset', 'medium')
risk_score += ASSET_CRITICALITY.get(asset, 20)
if risk_score >= 800:
tier = "T1-IMMEDIATE"
sla = "24-48 hours"
elif risk_score >= 500:
tier = "T2-HIGH"
sla = "7 days"
elif risk_score >= 200:
tier = "T3-MEDIUM"
sla = "30 days"
else:
tier = "T4-LOW"
sla = "90 days"
results.append({
'cve': vuln['cve'],
'risk_score': risk_score,
'tier': tier,
'sla': sla
})
# Sort by risk score descending
return sorted(results, key=lambda x: x['risk_score'], reverse=True)
# Example usage
findings = [
{"cve": "CVE-2024-1709", "cvss": 9.1, "asset": "internet-facing"},
{"cve": "CVE-2023-44487", "cvss": 7.5, "asset": "critical"},
{"cve": "CVE-2023-46604", "cvss": 10.0, "asset": "internet-facing"},
{"cve": "CVE-2024-xxxx", "cvss": 9.8, "asset": "low"},
{"cve": "CVE-2023-xxxx", "cvss": 5.0, "asset": "high"},
]
# Add EPSS scores
cves = [f['cve'] for f in findings]
epss_scores = fetch_epss(cves)
for f in findings:
f['epss'] = epss_scores.get(f['cve'], 0)
priority_list = prioritize(findings)
for p in priority_list:
print(f'{p["cve"]:20s} Score: {p["risk_score"]:4d}{p["tier"]:15s} SLA: {p["sla"]}')

Business Context in Prioritization

Technical severity is only half the equation. Business context determines actual risk:

Business Context Factors:
Revenue Impact:
└─ Does this system process payments? (PCI scope)
└─ Is downtime of this system revenue-critical?
└─ Are there contractual SLAs for this system?
Regulatory Impact:
└─ Does this system handle PII/PHI? (GDPR, HIPAA scope)
└─ Is this system in-scope for SOC 2, PCI DSS, or SOX?
└─ What are the regulatory penalties for a breach?
Reputational Impact:
└─ Is this system customer-facing?
└─ Would a breach of this system make headlines?
└─ Is this a high-visibility system (executive, board, investor-facing)?
Operational Impact:
└─ Is this a single point of failure?
└─ Are there compensating controls already in place?
└─ Can the system be quickly taken offline if needed?

Vulnerability Risk Score Calculation

Total Vulnerability Risk =
(CVSS_Score × 0.3) +
(Exploitability × 0.25) + # EPSS, public PoC, KEV
(Asset_Criticality × 0.25) + # Internet-facing, PII, revenue-impact
(Business_Impact × 0.20) # Regulatory, reputational, operational
Where:
Exploitability = 100 (KEV) | 70 (public PoC) | 50 (theoretical) | 0 (none)
Asset_Criticality = 100 (internet-facing + PII) | 70 (critical internal) | 30 (internal) | 10 (test)
Business_Impact = 100 (breach = existential) | 70 (major fine) | 30 (minor fine) | 10 (no impact)

Vulnerability Management Maturity Model

Level 1 — Initial (Reactive):
└─ Scan: Ad-hoc scanning (no schedule)
└─ Prioritization: Ignore until something breaks or auditor asks
└─ Remediation: When forced (breach, audit finding)
└─ Metrics: None
└─ Key risk: Significant unknown vulnerabilities in production
Level 2 — Defined (Scheduled):
└─ Scan: Monthly scheduled scans on production assets
└─ Prioritization: CVSS-based (fix all Critical)
└─ Remediation: Within 30-90 days
└─ Metrics: Scan coverage, findings per asset
└─ Key risk: Prioritization by CVSS alone leads to wasted effort
Level 3 — Managed (Risk-Based):
└─ Scan: Weekly scan with authenticated scanning
└─ Prioritization: CVSS + EPSS + KEV + asset criticality
└─ Remediation: SLA-based (Critical 7 days, High 30 days)
└─ Metrics: Mean time to remediate, fix rate, recurrence rate
└─ Key risk: Container/cloud scanning may not be fully integrated
Level 4 — Optimized (Continuous):
└─ Scan: Continuous scanning + CI/CD pipeline integration
└─ Prioritization: Fully automated risk scoring with business context
└─ Remediation: Automated patching for known-exploited vulnerabilities
└─ Metrics: Predictive risk scoring, trend analysis, SLA compliance > 95%
└─ Key risk: Staying current with evolving threat landscape

Key Takeaways

  • CVSS alone is insufficient for prioritization — it measures technical severity, not likelihood of exploitation or business impact
  • EPSS predicts exploitation likelihood based on real-world data — use it to filter the noise; a CVE with EPSS 0.001 is probably not going to be exploited even if CVSS is 9.8
  • CISA KEV catalog lists vulnerabilities being actively exploited NOW — these get highest priority regardless of CVSS score
  • Asset criticality must be factored in — a CVSS 10 on an isolated test server is lower risk than a CVSS 7 on an internet-facing system
  • A combined risk score (CVSS × EPSS + KEV + AssetCriticality + BusinessImpact) provides better prioritization than any single metric — automate this calculation
  • The 5% rule: 5% of vulnerabilities account for 95% of exploitation — prioritize the 5% that are being actively exploited (KEV) or highly likely to be exploited (EPSS > 0.5)
  • Automation is essential — manually triaging 10,000+ vulnerabilities is not feasible; build or buy an automated prioritization pipeline
  • The KEV catalog should be checked daily and integrated into automated patching workflows — if it’s being exploited, it should be patched within 24-48 hours
  • Mature vulnerability management programs operate at Level 3 or 4 (risk-based/continuous) — most organisations are at Level 1 or 2 (reactive/scheduled)
  • Business context (revenue impact, regulatory exposure, reputational risk) must be incorporated into prioritization — technical severity alone does not represent business risk
  • Vulnerability scoring is not a one-time calculation — EPSS scores update daily, new KEV entries are added weekly, and asset criticality changes over time as systems are decommissioned or reclassified