Skip to main content

06 - Hands-on Lab: Risk-Based Prioritizer Tool

In this lab, we will build a Python tool that simulates ingesting a raw vulnerability scanner report (which contains too many "High" severity issues based purely on CVSS) and reprioritizes the output based on CISA KEV presence and EPSS probability scores.

Scenario

You exported a CSV from your vulnerability scanner. Everything is marked CVSS 7.0+. Your operations team only has bandwidth to patch 3 things today. Which ones do you choose?

Prerequisites

  • Python 3.8+
  • requests library (pip install requests)

Step 1: The Raw Scanner Data (Simulated)

Create a mock data list representing our scanner findings.

# scanner_data.py
vulnerabilities = [
{"cve": "CVE-2021-44228", "cvss": 10.0, "asset": "web-prod-01", "desc": "Log4Shell"},
{"cve": "CVE-2017-0144", "cvss": 8.1, "asset": "smb-legacy-02", "desc": "EternalBlue"},
{"cve": "CVE-2023-12345", "cvss": 9.8, "asset": "internal-db-03", "desc": "Random Theoretical Vuln"},
{"cve": "CVE-2020-1472", "cvss": 10.0, "asset": "dc-01", "desc": "Zerologon"},
{"cve": "CVE-2023-99999", "cvss": 7.5, "asset": "dev-server", "desc": "Low probability theoretical"}
]

Step 2: The Prioritization Script

Create a script prioritize.py that queries EPSS and CISA KEV data to enrich our findings.

import requests
import json

# 1. Mock Scanner Data
vulnerabilities = [
{"cve": "CVE-2021-44228", "cvss": 10.0, "asset": "web-prod-01"},
{"cve": "CVE-2017-0144", "cvss": 8.1, "asset": "smb-legacy-02"},
{"cve": "CVE-2023-12345", "cvss": 9.8, "asset": "internal-db-03"}, # Fake CVE for testing
{"cve": "CVE-2020-1472", "cvss": 10.0, "asset": "dc-01"},
{"cve": "CVE-2023-99999", "cvss": 7.5, "asset": "dev-server"} # Fake CVE for testing
]

# 2. Fetch CISA KEV Catalog
def fetch_cisa_kev():
print("[*] Fetching latest CISA KEV catalog...")
url = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
try:
resp = requests.get(url)
resp.raise_for_status()
data = resp.json()
kev_cves = {vuln['cveID'] for vuln in data['vulnerabilities']}
return kev_cves
except Exception as e:
print(f"Error fetching KEV: {e}")
return set()

# 3. Fetch EPSS Scores
def fetch_epss(cve_list):
print("[*] Fetching EPSS scores...")
url = "https://api.first.org/data/v1/epss"
cve_query = ",".join(cve_list)
params = {"cve": cve_query}
try:
resp = requests.get(url, params=params)
resp.raise_for_status()
data = resp.json()
results = {}
for item in data.get("data", []):
results[item["cve"]] = float(item["epss"]) * 100
return results
except Exception as e:
print(f"Error fetching EPSS: {e}")
return {}

def main():
kev_set = fetch_cisa_kev()
cve_list = [v["cve"] for v in vulnerabilities]
epss_data = fetch_epss(cve_list)

print("\n--- ENRICHED VULNERABILITY REPORT ---")
print(f"{'CVE':<18} | {'CVSS':<5} | {'KEV?':<5} | {'EPSS %':<7} | {'PRIORITY':<10} | {'ASSET'}")
print("-" * 75)

for v in vulnerabilities:
cve = v["cve"]
cvss = v["cvss"]
asset = v["asset"]

# Enrich
in_kev = "YES" if cve in kev_set else "NO"
epss_score = epss_data.get(cve, 0.0)

# Prioritize Logic
if in_kev == "YES" or epss_score > 50.0:
priority = "P0 (URGENT)"
elif epss_score > 20.0 or cvss >= 9.0:
priority = "P1 (HIGH)"
else:
priority = "P3 (LOW)"

print(f"{cve:<18} | {cvss:<5} | {in_kev:<5} | {epss_score:>6.2f}% | {priority:<10} | {asset}")

if __name__ == "__main__":
main()

Step 3: Run the Lab

Execute the script:

python prioritize.py

Analysis of the Output

Notice how the CVSS 9.8 theoretical vulnerability (CVE-2023-12345) drops in priority compared to CVE-2017-0144 (CVSS 8.1), because the latter is on the KEV list and actively exploited. This allows your team to focus their patching efforts on real-world risks first.

Share this guide