vulnerability management

Performing CVE Prioritization with KEV Catalog

Leverage the CISA Known Exploited Vulnerabilities catalog alongside EPSS and CVSS to prioritize CVE remediation based on real-world exploitation evidence.

bod-22-01cisa-kevcveepssremediationthreat-intelligencevulnerability-prioritization
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

The CISA Known Exploited Vulnerabilities (KEV) catalog, established through Binding Operational Directive (BOD) 22-01, is a living list of CVEs that have been actively exploited in the wild and carry significant risk. As of early 2026, the catalog contains over 1,484 entries, growing 20% in 2025 alone with 245 new additions. This skill covers integrating the KEV catalog into vulnerability prioritization workflows alongside EPSS (Exploit Prediction Scoring System) and CVSS to create a risk-based approach that prioritizes vulnerabilities with confirmed exploitation activity over theoretical severity alone.

When to Use

  • When conducting security assessments that involve performing cve prioritization with kev catalog
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Access to vulnerability scan results (Qualys, Nessus, Rapid7, etc.)
  • Familiarity with CVE identifiers and NVD
  • Understanding of CVSS scoring (v3.1 and v4.0)
  • API access to CISA KEV, EPSS, and NVD endpoints
  • Python 3.8+ with requests and pandas libraries

Core Concepts

CISA KEV Catalog Structure

Each KEV entry contains:

  • CVE ID: The CVE identifier (e.g., CVE-2024-3094)
  • Vendor/Project: Affected vendor and product name
  • Vulnerability Name: Short description of the vulnerability
  • Date Added: When CISA added it to the catalog
  • Short Description: Brief technical description
  • Required Action: Recommended remediation action
  • Due Date: Deadline for federal agencies (FCEB) to remediate
  • Known Ransomware Campaign Use: Whether ransomware groups exploit it

BOD 22-01 Remediation Timelines

CVE Publication Date Remediation Deadline
2021 or later 2 weeks from KEV listing
Before 2021 6 months from KEV listing

Multi-Factor Prioritization Model

Factor Weight Data Source Rationale
CISA KEV Listed 30% CISA KEV JSON feed Confirmed active exploitation
EPSS Score 25% FIRST EPSS API Predicted exploitation probability
CVSS Base Score 20% NVD API v2.0 Intrinsic vulnerability severity
Asset Criticality 15% CMDB/Asset inventory Business impact context
Network Exposure 10% Network architecture Attack surface accessibility

KEV + EPSS Decision Matrix

KEV Listed EPSS > 0.5 CVSS >= 9.0 Priority SLA
Yes Any Any P1-Emergency 48 hours
No Yes Yes P1-Emergency 48 hours
No Yes No P2-Critical 7 days
No No Yes P2-Critical 7 days
No No No (>= 7.0) P3-High 14 days
No No No (>= 4.0) P4-Medium 30 days
No No No (< 4.0) P5-Low 90 days

Workflow

Step 1: Fetch and Parse the KEV Catalog

import requests
import json
from datetime import datetime
 
KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
 
def fetch_kev_catalog():
    """Download and parse the CISA KEV catalog."""
    response = requests.get(KEV_URL, timeout=30)
    response.raise_for_status()
    data = response.json()
 
    catalog = {}
    for vuln in data.get("vulnerabilities", []):
        cve_id = vuln["cveID"]
        catalog[cve_id] = {
            "vendor": vuln.get("vendorProject", ""),
            "product": vuln.get("product", ""),
            "name": vuln.get("vulnerabilityName", ""),
            "date_added": vuln.get("dateAdded", ""),
            "description": vuln.get("shortDescription", ""),
            "action": vuln.get("requiredAction", ""),
            "due_date": vuln.get("dueDate", ""),
            "ransomware_use": vuln.get("knownRansomwareCampaignUse", "Unknown"),
        }
 
    print(f"[+] Loaded {len(catalog)} CVEs from CISA KEV catalog")
    print(f"    Catalog version: {data.get('catalogVersion', 'N/A')}")
    print(f"    Last updated: {data.get('dateReleased', 'N/A')}")
    return catalog
 
kev = fetch_kev_catalog()

Step 2: Enrich with EPSS Scores

EPSS_API = "https://api.first.org/data/v1/epss"
 
def get_epss_scores(cve_list):
    """Fetch EPSS scores for a batch of CVEs."""
    scores = {}
    batch_size = 100
    for i in range(0, len(cve_list), batch_size):
        batch = cve_list[i:i + batch_size]
        cve_param = ",".join(batch)
        response = requests.get(EPSS_API, params={"cve": cve_param}, timeout=30)
        if response.status_code == 200:
            for entry in response.json().get("data", []):
                scores[entry["cve"]] = {
                    "epss": float(entry.get("epss", 0)),
                    "percentile": float(entry.get("percentile", 0)),
                }
    return scores

Step 3: Build the Prioritization Engine

import pandas as pd
 
def prioritize_vulnerabilities(scan_results, kev_catalog, epss_scores):
    """Apply multi-factor prioritization to scan results."""
    prioritized = []
 
    for vuln in scan_results:
        cve_id = vuln.get("cve_id", "")
        cvss_score = float(vuln.get("cvss_score", 0))
        asset_criticality = float(vuln.get("asset_criticality", 3))
        exposure = float(vuln.get("network_exposure", 3))
 
        in_kev = cve_id in kev_catalog
        kev_data = kev_catalog.get(cve_id, {})
        epss_data = epss_scores.get(cve_id, {"epss": 0, "percentile": 0})
        epss_score = epss_data["epss"]
 
        # Composite risk score calculation
        risk_score = (
            (1.0 if in_kev else 0.0) * 10 * 0.30 +
            epss_score * 10 * 0.25 +
            cvss_score * 0.20 +
            (asset_criticality / 5.0) * 10 * 0.15 +
            (exposure / 5.0) * 10 * 0.10
        )
 
        # Assign priority level
        if in_kev or (epss_score > 0.5 and cvss_score >= 9.0):
            priority = "P1-Emergency"
            sla_days = 2
        elif epss_score > 0.5 or cvss_score >= 9.0:
            priority = "P2-Critical"
            sla_days = 7
        elif cvss_score >= 7.0:
            priority = "P3-High"
            sla_days = 14
        elif cvss_score >= 4.0:
            priority = "P4-Medium"
            sla_days = 30
        else:
            priority = "P5-Low"
            sla_days = 90
 
        prioritized.append({
            "cve_id": cve_id,
            "cvss_score": cvss_score,
            "epss_score": round(epss_score, 4),
            "epss_percentile": round(epss_data["percentile"], 4),
            "in_cisa_kev": in_kev,
            "ransomware_use": kev_data.get("ransomware_use", "N/A"),
            "kev_due_date": kev_data.get("due_date", "N/A"),
            "risk_score": round(risk_score, 2),
            "priority": priority,
            "sla_days": sla_days,
            "asset": vuln.get("asset", ""),
            "asset_criticality": asset_criticality,
        })
 
    df = pd.DataFrame(prioritized)
    df = df.sort_values("risk_score", ascending=False)
    return df

Step 4: Generate Prioritization Report

def generate_report(df, output_file="kev_prioritized_report.csv"):
    """Generate summary report from prioritized vulnerabilities."""
    print("\n" + "=" * 70)
    print("VULNERABILITY PRIORITIZATION REPORT - KEV + EPSS + CVSS")
    print("=" * 70)
 
    print(f"\nTotal vulnerabilities analyzed: {len(df)}")
    print(f"KEV-listed vulnerabilities:    {df['in_cisa_kev'].sum()}")
    print(f"Ransomware-associated:         {(df['ransomware_use'] == 'Known').sum()}")
 
    print("\nPriority Distribution:")
    print(df["priority"].value_counts().to_string())
 
    print("\nTop 15 Highest Risk Vulnerabilities:")
    top = df.head(15)[["cve_id", "cvss_score", "epss_score", "in_cisa_kev",
                        "risk_score", "priority"]]
    print(top.to_string(index=False))
 
    df.to_csv(output_file, index=False)
    print(f"\n[+] Full report saved to: {output_file}")

Best Practices

  1. Update the KEV catalog daily since CISA adds new entries multiple times per week
  2. Always cross-reference KEV with EPSS; a CVE may have high EPSS but not yet be in KEV
  3. Treat all KEV-listed CVEs as P1-Emergency regardless of CVSS score
  4. Pay special attention to KEV entries flagged with "Known Ransomware Campaign Use"
  5. Automate KEV comparison against your vulnerability scan results in CI/CD pipelines
  6. Track KEV due dates separately for FCEB compliance requirements
  7. Use KEV as a leading indicator for threat hunting; if a CVE is added, check for prior exploitation in your environment

Common Pitfalls

  • Relying solely on CVSS scores without checking KEV or EPSS data
  • Not updating the KEV catalog frequently enough (CISA updates multiple times weekly)
  • Treating non-KEV CVEs as safe; they may be exploited but not yet cataloged
  • Ignoring the "ransomware use" field which indicates highest-urgency threats
  • Using KEV only for compliance instead of integrating into overall risk management
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md5.2 KB

API Reference: CISA KEV Catalog CVE Prioritization

Libraries Used

Library Purpose
requests Fetch KEV catalog JSON from CISA
json Parse vulnerability entries and match against scan data
csv Read vulnerability scanner CSV exports
datetime Calculate remediation deadlines and SLA compliance

Installation

pip install requests

Data Sources

CISA KEV JSON Feed

URL: https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
Format: JSON
Authentication: None (public)
Update frequency: Updated as new exploited CVEs are added (typically several times per week)

CISA KEV CSV Feed

URL: https://www.cisa.gov/sites/default/files/csv/known_exploited_vulnerabilities.csv
Format: CSV

GitHub Mirror

URL: https://raw.githubusercontent.com/cisagov/kev-data/main/known_exploited_vulnerabilities.json

Core Operations

Fetch the KEV Catalog

import requests
from datetime import datetime
 
KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
 
def fetch_kev_catalog():
    resp = requests.get(KEV_URL, timeout=30)
    resp.raise_for_status()
    data = resp.json()
    return {
        "title": data["title"],
        "catalog_version": data["catalogVersion"],
        "date_released": data["dateReleased"],
        "count": data["count"],
        "vulnerabilities": data["vulnerabilities"],
    }

KEV Entry Schema

Field Type Description
cveID string CVE identifier (e.g., "CVE-2024-12345")
vendorProject string Affected vendor (e.g., "Microsoft")
product string Affected product (e.g., "Windows")
vulnerabilityName string Human-readable vulnerability description
dateAdded string Date added to KEV (YYYY-MM-DD)
shortDescription string Brief vulnerability description
requiredAction string CISA-recommended remediation action
dueDate string Remediation deadline for federal agencies (YYYY-MM-DD)
knownRansomwareCampaignUse string "Known" or "Unknown" ransomware association
notes string Additional context

Match Scan Results Against KEV

def match_scan_to_kev(scan_cves, kev_catalog):
    """Cross-reference vulnerability scan CVEs against the KEV catalog."""
    kev_lookup = {v["cveID"]: v for v in kev_catalog["vulnerabilities"]}
    matched = []
    unmatched = []
 
    for cve_id in scan_cves:
        if cve_id in kev_lookup:
            entry = kev_lookup[cve_id]
            matched.append({
                "cve": cve_id,
                "vendor": entry["vendorProject"],
                "product": entry["product"],
                "due_date": entry["dueDate"],
                "ransomware": entry["knownRansomwareCampaignUse"],
                "action": entry["requiredAction"],
                "overdue": datetime.strptime(entry["dueDate"], "%Y-%m-%d") < datetime.now(),
            })
        else:
            unmatched.append(cve_id)
 
    return {"kev_matches": matched, "non_kev": unmatched}

Prioritize by Risk

def prioritize_kev_findings(kev_matches):
    """Sort KEV matches by priority: overdue > ransomware > due date."""
    def priority_key(entry):
        score = 0
        if entry["overdue"]:
            score += 1000
        if entry["ransomware"] == "Known":
            score += 500
        # Earlier due dates get higher priority
        days_until = (datetime.strptime(entry["due_date"], "%Y-%m-%d") - datetime.now()).days
        score -= days_until
        return -score
 
    return sorted(kev_matches, key=priority_key)

Generate Remediation Report

def generate_report(scan_results, kev_catalog):
    matches = match_scan_to_kev(scan_results, kev_catalog)
 
    overdue = [m for m in matches["kev_matches"] if m["overdue"]]
    ransomware = [m for m in matches["kev_matches"] if m["ransomware"] == "Known"]
 
    return {
        "total_vulns_scanned": len(scan_results),
        "kev_matches": len(matches["kev_matches"]),
        "overdue_count": len(overdue),
        "ransomware_associated": len(ransomware),
        "critical_actions": prioritize_kev_findings(matches["kev_matches"])[:10],
        "non_kev_vulns": len(matches["non_kev"]),
    }

Monitor KEV Catalog Updates

def check_for_new_entries(last_known_count):
    """Check if new vulnerabilities have been added to KEV."""
    catalog = fetch_kev_catalog()
    current_count = catalog["count"]
    if current_count > last_known_count:
        new_entries = catalog["vulnerabilities"][last_known_count:]
        return {
            "new_entries": len(new_entries),
            "latest": new_entries,
            "total": current_count,
        }
    return {"new_entries": 0, "total": current_count}

Output Format

{
  "catalog_version": "2025.01.15",
  "total_kev_entries": 1150,
  "scan_matches": 12,
  "overdue": 3,
  "ransomware_associated": 5,
  "critical_actions": [
    {
      "cve": "CVE-2024-21887",
      "vendor": "Ivanti",
      "product": "Connect Secure",
      "due_date": "2024-01-31",
      "ransomware": "Known",
      "overdue": true,
      "action": "Apply mitigations per vendor instructions or discontinue use."
    }
  ]
}
standards.md1.2 KB

Standards and References - CVE Prioritization with KEV Catalog

Official CISA Resources

Scoring Systems

KEV Catalog Statistics (2025)

Metric Value
Total CVEs in catalog 1,484+
Added in 2025 245
Year-over-year growth 20%
Ransomware-associated (2025) 24

Regulatory Requirements for KEV Remediation

  • BOD 22-01: Federal agencies must remediate KEV CVEs per due dates
  • PCI DSS v4.0: Prioritize remediation of actively exploited vulns
  • NIST CSF 2.0: Risk-based vulnerability prioritization
  • ISO 27001:2022 A.8.8: Technical vulnerability management
workflows.md2.8 KB

Workflows - CVE Prioritization with KEV Catalog

Workflow 1: Daily KEV Integration Pipeline

┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│ Fetch KEV JSON   │────>│ Compare with     │────>│ Identify New     │
│ Feed (daily)     │     │ Previous Version │     │ KEV Entries      │
└──────────────────┘     └──────────────────┘     └──────────────────┘

        ┌────────────────────────────────────────────────┘
        v
┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│ Cross-Reference  │────>│ Flag Matching    │────>│ Escalate to P1   │
│ Scan Results     │     │ Vulns in Env     │     │ Emergency        │
└──────────────────┘     └──────────────────┘     └──────────────────┘

        v
┌──────────────────┐     ┌──────────────────┐
│ Notify Remediation│───>│ Track Against    │
│ Teams            │     │ KEV Due Date     │
└──────────────────┘     └──────────────────┘

Workflow 2: Multi-Factor Scoring Pipeline

For each CVE in scan results:
    1. Look up CVSS base score from NVD API
    2. Fetch EPSS probability from FIRST API
    3. Check presence in CISA KEV catalog
    4. Check if ransomware-associated in KEV
    5. Look up asset criticality from CMDB
    6. Determine network exposure (internet/DMZ/internal)
    7. Calculate composite risk score
    8. Assign priority level (P1-P5)
    9. Set remediation SLA based on priority
   10. Generate ticket in ITSM system

Workflow 3: KEV-Triggered Threat Hunt

When new CVE added to KEV:
    ├── Check if vulnerability exists in environment
    │   ├── Yes: Immediate P1 escalation
    │   │   ├── Search SIEM for exploitation indicators
    │   │   ├── Check EDR for related TTPs
    │   │   └── Initiate incident response if exploitation found
    │   └── No: Document non-applicability
    └── Update threat intelligence feeds with KEV IOCs

Scripts 2

agent.py8.7 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""CISA KEV CVE prioritization agent.

Downloads the CISA Known Exploited Vulnerabilities (KEV) catalog and
cross-references it against a list of CVEs from vulnerability scans to
prioritize remediation based on active exploitation status, due dates,
and vendor/product impact.
"""
import argparse
import csv
import io
import json
import os
import sys
from datetime import datetime, timezone

try:
    import requests
except ImportError:
    print("[!] 'requests' library required: pip install requests", file=sys.stderr)
    sys.exit(1)

KEV_JSON_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"


def download_kev_catalog():
    """Download the latest CISA KEV catalog as JSON."""
    print(f"[*] Downloading CISA KEV catalog from {KEV_JSON_URL}")
    resp = requests.get(KEV_JSON_URL, timeout=30)
    resp.raise_for_status()
    data = resp.json()
    vulns = data.get("vulnerabilities", [])
    catalog_version = data.get("catalogVersion", "unknown")
    count = data.get("count", len(vulns))
    date_released = data.get("dateReleased", "unknown")
    print(f"[+] KEV catalog v{catalog_version}: {count} vulnerabilities "
          f"(released: {date_released})")
    return vulns, {
        "catalog_version": catalog_version,
        "count": count,
        "date_released": date_released,
    }


def build_kev_index(kev_vulns):
    """Build a lookup dict keyed by CVE ID for fast matching."""
    index = {}
    for vuln in kev_vulns:
        cve_id = vuln.get("cveID", "")
        if cve_id:
            index[cve_id.upper()] = {
                "cve_id": cve_id,
                "vendor": vuln.get("vendorProject", ""),
                "product": vuln.get("product", ""),
                "vulnerability_name": vuln.get("vulnerabilityName", ""),
                "date_added": vuln.get("dateAdded", ""),
                "short_description": vuln.get("shortDescription", ""),
                "required_action": vuln.get("requiredAction", ""),
                "due_date": vuln.get("dueDate", ""),
                "known_ransomware_campaign": vuln.get("knownRansomwareCampaignUse", "Unknown"),
                "notes": vuln.get("notes", ""),
            }
    return index


def load_cve_list(source):
    """Load CVE list from file (one CVE per line or CSV) or comma-separated string."""
    cves = []
    if os.path.isfile(source):
        with open(source, "r") as f:
            content = f.read()
        for line in content.strip().splitlines():
            line = line.strip().strip(",").strip('"')
            if line.upper().startswith("CVE-"):
                cves.append(line.upper())
            elif "," in line:
                parts = line.split(",")
                for part in parts:
                    part = part.strip().strip('"')
                    if part.upper().startswith("CVE-"):
                        cves.append(part.upper())
    else:
        for part in source.split(","):
            part = part.strip()
            if part.upper().startswith("CVE-"):
                cves.append(part.upper())
    return list(set(cves))


def prioritize_cves(cve_list, kev_index):
    """Cross-reference CVEs against KEV catalog and prioritize."""
    in_kev = []
    not_in_kev = []

    for cve_id in cve_list:
        kev_entry = kev_index.get(cve_id)
        if kev_entry:
            entry = dict(kev_entry)
            entry["in_kev"] = True
            entry["priority"] = "CRITICAL"
            if entry.get("known_ransomware_campaign", "").lower() == "known":
                entry["priority"] = "CRITICAL-RANSOMWARE"
            try:
                due = datetime.strptime(entry["due_date"], "%Y-%m-%d")
                if due < datetime.now():
                    entry["overdue"] = True
                    entry["priority"] = "CRITICAL-OVERDUE"
                else:
                    entry["overdue"] = False
            except (ValueError, KeyError):
                entry["overdue"] = False
            in_kev.append(entry)
        else:
            not_in_kev.append({
                "cve_id": cve_id,
                "in_kev": False,
                "priority": "STANDARD",
            })

    # Sort: overdue first, then ransomware, then by due date
    priority_order = {"CRITICAL-OVERDUE": 0, "CRITICAL-RANSOMWARE": 1, "CRITICAL": 2}
    in_kev.sort(key=lambda x: (priority_order.get(x["priority"], 9), x.get("due_date", "")))

    return in_kev, not_in_kev


def format_summary(in_kev, not_in_kev, total_cves, catalog_info):
    """Print a human-readable prioritization report."""
    print(f"\n{'='*60}")
    print(f"  CISA KEV CVE Prioritization Report")
    print(f"{'='*60}")
    print(f"  KEV Catalog  : v{catalog_info['catalog_version']} "
          f"({catalog_info['count']} total KEVs)")
    print(f"  Input CVEs   : {total_cves}")
    print(f"  In KEV       : {len(in_kev)} (actively exploited)")
    print(f"  Not in KEV   : {len(not_in_kev)}")

    overdue = [v for v in in_kev if v.get("overdue")]
    ransomware = [v for v in in_kev if "RANSOMWARE" in v.get("priority", "")]

    print(f"\n  Priority Breakdown:")
    print(f"    CRITICAL-OVERDUE     : {len(overdue)}")
    print(f"    CRITICAL-RANSOMWARE  : {len(ransomware)}")
    print(f"    CRITICAL (in KEV)    : {len(in_kev)}")
    print(f"    STANDARD (not in KEV): {len(not_in_kev)}")

    if in_kev:
        print(f"\n  KEV-Listed CVEs (prioritize remediation):")
        for v in in_kev[:20]:
            overdue_flag = " [OVERDUE]" if v.get("overdue") else ""
            ransomware_flag = " [RANSOMWARE]" if "RANSOMWARE" in v.get("priority", "") else ""
            print(f"    {v['cve_id']:18s} | Due: {v.get('due_date', 'N/A'):10s} | "
                  f"{v.get('vendor', ''):15s} | {v.get('product', ''):20s}"
                  f"{overdue_flag}{ransomware_flag}")
            if v.get("required_action"):
                print(f"      Action: {v['required_action'][:70]}")


def kev_stats(kev_vulns):
    """Compute statistics about the KEV catalog."""
    by_vendor = {}
    ransomware_count = 0
    for v in kev_vulns:
        vendor = v.get("vendorProject", "Unknown")
        by_vendor[vendor] = by_vendor.get(vendor, 0) + 1
        if v.get("knownRansomwareCampaignUse", "").lower() == "known":
            ransomware_count += 1

    print(f"\n  KEV Catalog Statistics:")
    print(f"    Total vulnerabilities  : {len(kev_vulns)}")
    print(f"    Ransomware-associated  : {ransomware_count}")
    print(f"    Top vendors:")
    for vendor, count in sorted(by_vendor.items(), key=lambda x: -x[1])[:10]:
        print(f"      {vendor:30s}: {count}")


def main():
    parser = argparse.ArgumentParser(
        description="CISA KEV CVE prioritization agent"
    )
    parser.add_argument("--cves", required=True,
                        help="CVE list: comma-separated string or path to file")
    parser.add_argument("--kev-file",
                        help="Path to local KEV JSON (skip download)")
    parser.add_argument("--stats", action="store_true",
                        help="Show KEV catalog statistics")
    parser.add_argument("--output", "-o", help="Output JSON report path")
    parser.add_argument("--verbose", "-v", action="store_true")
    args = parser.parse_args()

    if args.kev_file:
        print(f"[*] Loading local KEV file: {args.kev_file}")
        with open(args.kev_file, "r") as f:
            data = json.load(f)
        kev_vulns = data.get("vulnerabilities", [])
        catalog_info = {
            "catalog_version": data.get("catalogVersion", "local"),
            "count": len(kev_vulns),
            "date_released": data.get("dateReleased", "unknown"),
        }
    else:
        kev_vulns, catalog_info = download_kev_catalog()

    if args.stats:
        kev_stats(kev_vulns)

    kev_index = build_kev_index(kev_vulns)
    cve_list = load_cve_list(args.cves)
    print(f"[*] Loaded {len(cve_list)} CVE(s) to check")

    in_kev, not_in_kev = prioritize_cves(cve_list, kev_index)
    format_summary(in_kev, not_in_kev, len(cve_list), catalog_info)

    report = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "tool": "CISA KEV Prioritization",
        "catalog_info": catalog_info,
        "input_cve_count": len(cve_list),
        "kev_matched": len(in_kev),
        "kev_unmatched": len(not_in_kev),
        "prioritized": in_kev,
        "standard_priority": not_in_kev,
        "risk_level": (
            "CRITICAL" if any(v.get("overdue") for v in in_kev)
            else "HIGH" if in_kev
            else "LOW"
        ),
    }

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"\n[+] Report saved to {args.output}")
    elif args.verbose:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
process.py9.4 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
CISA KEV-Based CVE Prioritization Engine

Fetches the CISA Known Exploited Vulnerabilities catalog,
enriches with EPSS scores, and prioritizes against scan results.

Requirements:
    pip install requests pandas

Usage:
    python process.py fetch                              # Download KEV catalog
    python process.py check --cve CVE-2024-3094          # Check single CVE
    python process.py prioritize --csv vulns.csv --output prioritized.csv
    python process.py stats                              # KEV catalog statistics
"""

import argparse
import json
import sys
from datetime import datetime, timedelta
from pathlib import Path

import pandas as pd
import requests


KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
EPSS_API = "https://api.first.org/data/v1/epss"
NVD_API = "https://services.nvd.nist.gov/rest/json/cves/2.0"
KEV_CACHE = "kev_catalog.json"


class KEVPrioritizer:
    """CISA KEV-based vulnerability prioritization engine."""

    def __init__(self):
        self.kev_catalog = {}
        self.session = requests.Session()
        self.session.headers.update({"User-Agent": "KEV-Prioritizer/1.0"})

    def fetch_kev(self, cache_file=KEV_CACHE):
        """Download and cache the KEV catalog."""
        print("[*] Fetching CISA KEV catalog...")
        response = self.session.get(KEV_URL, timeout=30)
        response.raise_for_status()
        data = response.json()

        for vuln in data.get("vulnerabilities", []):
            cve_id = vuln["cveID"]
            self.kev_catalog[cve_id] = {
                "vendor": vuln.get("vendorProject", ""),
                "product": vuln.get("product", ""),
                "name": vuln.get("vulnerabilityName", ""),
                "date_added": vuln.get("dateAdded", ""),
                "description": vuln.get("shortDescription", ""),
                "action": vuln.get("requiredAction", ""),
                "due_date": vuln.get("dueDate", ""),
                "ransomware_use": vuln.get("knownRansomwareCampaignUse", "Unknown"),
            }

        with open(cache_file, "w") as f:
            json.dump(data, f)

        print(f"[+] Loaded {len(self.kev_catalog)} CVEs from KEV catalog")
        print(f"    Version: {data.get('catalogVersion', 'N/A')}")
        print(f"    Released: {data.get('dateReleased', 'N/A')}")
        return self.kev_catalog

    def load_cached_kev(self, cache_file=KEV_CACHE):
        """Load KEV catalog from local cache."""
        if not Path(cache_file).exists():
            return self.fetch_kev(cache_file)

        with open(cache_file) as f:
            data = json.load(f)

        for vuln in data.get("vulnerabilities", []):
            cve_id = vuln["cveID"]
            self.kev_catalog[cve_id] = {
                "vendor": vuln.get("vendorProject", ""),
                "product": vuln.get("product", ""),
                "name": vuln.get("vulnerabilityName", ""),
                "date_added": vuln.get("dateAdded", ""),
                "description": vuln.get("shortDescription", ""),
                "action": vuln.get("requiredAction", ""),
                "due_date": vuln.get("dueDate", ""),
                "ransomware_use": vuln.get("knownRansomwareCampaignUse", "Unknown"),
            }
        print(f"[+] Loaded {len(self.kev_catalog)} CVEs from cache")
        return self.kev_catalog

    def get_epss_batch(self, cve_list):
        """Fetch EPSS scores for a batch of CVEs."""
        scores = {}
        batch_size = 100
        for i in range(0, len(cve_list), batch_size):
            batch = cve_list[i:i + batch_size]
            try:
                response = self.session.get(
                    EPSS_API, params={"cve": ",".join(batch)}, timeout=30
                )
                if response.status_code == 200:
                    for entry in response.json().get("data", []):
                        scores[entry["cve"]] = {
                            "epss": float(entry.get("epss", 0)),
                            "percentile": float(entry.get("percentile", 0)),
                        }
            except Exception as e:
                print(f"  [!] EPSS batch error: {e}")
        return scores

    def prioritize(self, vulnerabilities):
        """Prioritize vulnerabilities using KEV + EPSS + CVSS."""
        cve_list = [v.get("cve_id", "") for v in vulnerabilities if v.get("cve_id")]
        epss_scores = self.get_epss_batch(cve_list)

        results = []
        for vuln in vulnerabilities:
            cve_id = vuln.get("cve_id", "")
            cvss = float(vuln.get("cvss_score", 0))
            asset_crit = float(vuln.get("asset_criticality", 3))
            exposure = float(vuln.get("network_exposure", 3))

            in_kev = cve_id in self.kev_catalog
            kev_data = self.kev_catalog.get(cve_id, {})
            epss = epss_scores.get(cve_id, {"epss": 0, "percentile": 0})

            risk_score = (
                (1.0 if in_kev else 0.0) * 10 * 0.30 +
                epss["epss"] * 10 * 0.25 +
                cvss * 0.20 +
                (asset_crit / 5.0) * 10 * 0.15 +
                (exposure / 5.0) * 10 * 0.10
            )

            if in_kev or (epss["epss"] > 0.5 and cvss >= 9.0):
                priority, sla = "P1-Emergency", 2
            elif epss["epss"] > 0.5 or cvss >= 9.0:
                priority, sla = "P2-Critical", 7
            elif cvss >= 7.0:
                priority, sla = "P3-High", 14
            elif cvss >= 4.0:
                priority, sla = "P4-Medium", 30
            else:
                priority, sla = "P5-Low", 90

            results.append({
                **vuln,
                "in_cisa_kev": in_kev,
                "ransomware_use": kev_data.get("ransomware_use", "N/A"),
                "kev_due_date": kev_data.get("due_date", "N/A"),
                "epss_score": round(epss["epss"], 4),
                "epss_percentile": round(epss["percentile"], 4),
                "risk_score": round(risk_score, 2),
                "priority": priority,
                "sla_days": sla,
            })

        df = pd.DataFrame(results)
        return df.sort_values("risk_score", ascending=False)

    def check_cve(self, cve_id):
        """Check a single CVE against KEV and EPSS."""
        in_kev = cve_id in self.kev_catalog
        kev_data = self.kev_catalog.get(cve_id, {})
        epss = self.get_epss_batch([cve_id]).get(cve_id, {"epss": 0, "percentile": 0})

        print(f"\n{'=' * 60}")
        print(f"CVE:              {cve_id}")
        print(f"In CISA KEV:      {'YES' if in_kev else 'No'}")
        if in_kev:
            print(f"  Vendor:         {kev_data.get('vendor', 'N/A')}")
            print(f"  Product:        {kev_data.get('product', 'N/A')}")
            print(f"  Date Added:     {kev_data.get('date_added', 'N/A')}")
            print(f"  Due Date:       {kev_data.get('due_date', 'N/A')}")
            print(f"  Ransomware Use: {kev_data.get('ransomware_use', 'N/A')}")
            print(f"  Action:         {kev_data.get('action', 'N/A')}")
        print(f"EPSS Score:       {epss['epss']:.4f} ({epss['percentile'] * 100:.1f}th pct)")

    def catalog_stats(self):
        """Print KEV catalog statistics."""
        if not self.kev_catalog:
            print("[!] KEV catalog not loaded.")
            return

        df = pd.DataFrame(self.kev_catalog.values())

        print(f"\n{'=' * 60}")
        print(f"CISA KEV CATALOG STATISTICS")
        print(f"{'=' * 60}")
        print(f"Total CVEs: {len(self.kev_catalog)}")

        ransomware = df[df["ransomware_use"] == "Known"]
        print(f"Ransomware-associated: {len(ransomware)}")

        print(f"\nTop 10 Vendors:")
        print(df["vendor"].value_counts().head(10).to_string())

        df["date_added"] = pd.to_datetime(df["date_added"], errors="coerce")
        df["year_added"] = df["date_added"].dt.year
        print(f"\nAdditions by Year:")
        print(df["year_added"].value_counts().sort_index().to_string())


def main():
    parser = argparse.ArgumentParser(description="CISA KEV CVE Prioritization Engine")
    subparsers = parser.add_subparsers(dest="command")

    subparsers.add_parser("fetch", help="Download/update KEV catalog")
    subparsers.add_parser("stats", help="Show KEV catalog statistics")

    check_p = subparsers.add_parser("check", help="Check a single CVE")
    check_p.add_argument("--cve", required=True, help="CVE ID to check")

    pri_p = subparsers.add_parser("prioritize", help="Prioritize vulns from CSV")
    pri_p.add_argument("--csv", required=True, help="Input CSV (needs cve_id column)")
    pri_p.add_argument("--output", default="kev_prioritized.csv", help="Output CSV")

    args = parser.parse_args()
    engine = KEVPrioritizer()

    if args.command == "fetch":
        engine.fetch_kev()
    elif args.command == "stats":
        engine.load_cached_kev()
        engine.catalog_stats()
    elif args.command == "check":
        engine.load_cached_kev()
        engine.check_cve(args.cve)
    elif args.command == "prioritize":
        engine.load_cached_kev()
        df = pd.read_csv(args.csv)
        vulns = df.to_dict("records")
        result = engine.prioritize(vulns)
        result.to_csv(args.output, index=False)
        print(f"\n[+] Prioritized {len(result)} vulnerabilities to {args.output}")
        print(f"\nPriority Distribution:")
        print(result["priority"].value_counts().to_string())
    else:
        parser.print_help()


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 1.2 KB
Keep exploring