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-basedPrioritization Frameworks
CVSS (Base Severity)
Good for: Standardised severity scoring
Limitation: Measures technical severity, not likelihood of exploitation
| CVSS Score | Severity | Response Time |
|---|---|---|
| 9.0-10.0 | Critical | 7 days |
| 7.0-8.9 | High | 30 days |
| 4.0-6.9 | Medium | 90 days |
| 0.1-3.9 | Low | 180 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 KEVAsset 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
# Query CISA KEV catalog via APIcurl -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 CVEcurl -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 scorepython3 << 'PYEOF'# Simple prioritization enginevulnerabilities = [ {"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"}')PYEOFKey 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
| CVE | CVSS | EPSS | KEV | CVSS-Based Priority | Risk-Based Priority |
|---|---|---|---|---|---|
| CVE-2023-44487 (HTTP/2 Rapid Reset) | 7.5 (High) | 0.97 | Yes | Medium (CVSS 7.5) | IMMEDIATE (actively exploited DDoS) |
| CVE-2024-1709 (ScreenConnect) | 9.1 (Critical) | 0.95 | Yes | Critical | IMMEDIATE (RCE, public exploit) |
| CVE-2023-46604 (Apache ActiveMQ) | 10.0 (Critical) | 0.90 | Yes | Critical | IMMEDIATE (unauthenticated RCE) |
| CVE-2023-xxx (lib vulnerability) | 9.8 (Critical) | 0.001 | No | Critical | MEDIUM (no exploit seen, rare lib) |
| CVE-2024-xxx (obscure software) | 7.5 (High) | 0.0001 | No | Medium | LOW (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 prioritizationPrioritization Automation
Automated Prioritization Pipeline
#!/usr/bin/env python3"""Automated vulnerability prioritization engine"""
import requestsimport jsonfrom datetime import datetime, timedelta
# ConfigurationKEV_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 usagefindings = [ {"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 scorescves = [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 landscapeKey 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