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.