mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-16 16:03:17 +03:00
246 lines
9.4 KiB
Python
246 lines
9.4 KiB
Python
#!/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()
|