npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Overview
The Exploit Prediction Scoring System (EPSS) is a data-driven model developed by FIRST (Forum of Incident Response and Security Teams) that estimates the probability of a CVE being exploited in the wild within the next 30 days. EPSS produces scores from 0.0 to 1.0 (0% to 100%) using machine learning trained on real-world exploitation data. Unlike CVSS which measures severity, EPSS measures likelihood of exploitation, making it essential for risk-based vulnerability prioritization.
When to Use
- When deploying or configuring implementing epss score for vulnerability prioritization capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Python 3.9+ with
requests,pandas,matplotlib - Access to FIRST EPSS API (https://api.first.org/data/v1/epss)
- Vulnerability scan results with CVE identifiers
- Optional: NVD API key for CVSS enrichment
EPSS API Usage
Query Single CVE
# Get EPSS score for a specific CVE
curl -s "https://api.first.org/data/v1/epss?cve=CVE-2024-3400" | python3 -m json.tool
# Response:
# {
# "status": "OK",
# "status-code": 200,
# "version": "1.0",
# "total": 1,
# "data": [
# {
# "cve": "CVE-2024-3400",
# "epss": "0.95732",
# "percentile": "0.99721",
# "date": "2024-04-15"
# }
# ]
# }Query Multiple CVEs
# Batch query up to 100 CVEs
curl -s "https://api.first.org/data/v1/epss?cve=CVE-2024-3400,CVE-2024-21887,CVE-2023-44228" | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
for item in data['data']:
pct = float(item['epss']) * 100
print(f\"{item['cve']}: {pct:.2f}% exploitation probability (percentile: {item['percentile']})\")
"Download Full EPSS Dataset
# Download complete daily EPSS scores (CSV format)
curl -s "https://epss.cyentia.com/epss_scores-current.csv.gz" | gunzip > epss_scores_current.csv
# Check size and preview
wc -l epss_scores_current.csv
head -5 epss_scores_current.csvQuery Historical EPSS Scores
# Get EPSS score for a specific date
curl -s "https://api.first.org/data/v1/epss?cve=CVE-2024-3400&date=2024-04-12"
# Get time series data
curl -s "https://api.first.org/data/v1/epss?cve=CVE-2024-3400&scope=time-series"Prioritization Strategy
EPSS + CVSS Combined Approach
| EPSS Score | CVSS Score | Priority | Action |
|---|---|---|---|
| > 0.7 | >= 9.0 | P0 - Immediate | Remediate within 24 hours |
| > 0.7 | >= 7.0 | P1 - Urgent | Remediate within 48 hours |
| > 0.4 | >= 7.0 | P2 - High | Remediate within 7 days |
| > 0.1 | >= 4.0 | P3 - Medium | Remediate within 30 days |
| <= 0.1 | >= 7.0 | P3 - Medium | Remediate within 30 days |
| <= 0.1 | < 7.0 | P4 - Low | Remediate within 90 days |
EPSS Percentile Thresholds
- Top 1% (percentile >= 0.99): Extremely likely to be exploited; treat as Critical
- Top 5% (percentile >= 0.95): High exploitation probability; prioritize remediation
- Top 10% (percentile >= 0.90): Elevated risk; schedule for near-term remediation
- Bottom 50%: Low exploitation probability; handle in normal patch cycle
Implementation
import requests
import pandas as pd
from datetime import datetime
def fetch_epss_scores(cve_list):
"""Fetch EPSS scores for a list of CVEs from FIRST API."""
scores = {}
batch_size = 100
for i in range(0, len(cve_list), batch_size):
batch = cve_list[i:i + batch_size]
resp = requests.get(
"https://api.first.org/data/v1/epss",
params={"cve": ",".join(batch)},
timeout=30
)
if resp.status_code == 200:
for entry in resp.json().get("data", []):
scores[entry["cve"]] = {
"epss": float(entry["epss"]),
"percentile": float(entry["percentile"]),
"date": entry.get("date", ""),
}
return scores
def prioritize_vulnerabilities(scan_results_csv, output_csv):
"""Enrich scan results with EPSS scores and assign priorities."""
df = pd.read_csv(scan_results_csv)
cve_list = df["cve_id"].dropna().unique().tolist()
epss_data = fetch_epss_scores(cve_list)
df["epss_score"] = df["cve_id"].map(lambda c: epss_data.get(c, {}).get("epss", 0))
df["epss_percentile"] = df["cve_id"].map(lambda c: epss_data.get(c, {}).get("percentile", 0))
def assign_priority(row):
epss = row.get("epss_score", 0)
cvss = row.get("cvss_score", 0)
if epss > 0.7 and cvss >= 9.0:
return "P0"
if epss > 0.7 and cvss >= 7.0:
return "P1"
if epss > 0.4 and cvss >= 7.0:
return "P2"
if epss > 0.1 or cvss >= 7.0:
return "P3"
return "P4"
df["priority"] = df.apply(assign_priority, axis=1)
df = df.sort_values(["priority", "epss_score"], ascending=[True, False])
df.to_csv(output_csv, index=False)
print(f"[+] Prioritized {len(df)} vulnerabilities -> {output_csv}")
print(f" P0: {len(df[df['priority']=='P0'])}")
print(f" P1: {len(df[df['priority']=='P1'])}")
print(f" P2: {len(df[df['priority']=='P2'])}")
print(f" P3: {len(df[df['priority']=='P3'])}")
print(f" P4: {len(df[df['priority']=='P4'])}")
return dfEPSS Trend Analysis
def fetch_epss_timeseries(cve_id):
"""Get historical EPSS scores for trend analysis."""
resp = requests.get(
"https://api.first.org/data/v1/epss",
params={"cve": cve_id, "scope": "time-series"},
timeout=30
)
if resp.status_code == 200:
return resp.json().get("data", [])
return []
def detect_epss_spikes(cve_id, threshold=0.3):
"""Detect significant EPSS score increases indicating emerging threats."""
timeseries = fetch_epss_timeseries(cve_id)
if len(timeseries) < 2:
return False
sorted_data = sorted(timeseries, key=lambda x: x.get("date", ""))
latest = float(sorted_data[-1].get("epss", 0))
previous = float(sorted_data[-2].get("epss", 0))
increase = latest - previous
if increase >= threshold:
print(f"[!] EPSS spike detected for {cve_id}: {previous:.3f} -> {latest:.3f} (+{increase:.3f})")
return True
return FalseReferences
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.6 KB
API Reference — Implementing EPSS Score for Vulnerability Prioritization
Libraries Used
- requests: HTTP client for FIRST.org EPSS API
- csv: Parse and enrich vulnerability scan CSV files
CLI Interface
python agent.py score --cves CVE-2024-1234 CVE-2024-5678
python agent.py enrich --scan-file scan.csv [--output enriched.csv]Core Functions
get_epss_scores(cve_list)
Fetches EPSS scores from the FIRST.org API (batches of 100).
API Endpoint: GET https://api.first.org/data/v1/epss?cve=CVE-1,CVE-2
Returns: dict with scores list, each containing cve, epss (0.0-1.0), percentile (0.0-1.0).
prioritize_vulnerabilities(cve_scores, epss_threshold=0.1, percentile_threshold=0.9)
Classifies CVEs into priority buckets based on EPSS probability.
Priority Buckets:
| Priority | Criteria |
|---|---|
| CRITICAL | EPSS >= 0.1 or percentile >= 90th |
| HIGH | EPSS >= 0.05 |
| MEDIUM | EPSS >= 0.01 |
| LOW | EPSS < 0.01 |
enrich_from_scan(scan_file, output_file=None)
Reads a CSV vulnerability scan, fetches EPSS for all CVEs, and writes enriched output.
Auto-detects columns: CVE, cve, CVE-ID, cve_id, vulnerability_id.
FIRST.org EPSS API
| Parameter | Description |
|---|---|
cve |
Comma-separated CVE IDs (max 100 per request) |
envelope |
Wrap response in metadata envelope |
date |
Get scores for a specific date (YYYY-MM-DD) |
Response Fields:
epss: Probability of exploitation in next 30 days (0.0–1.0)percentile: Percentile rank relative to all scored CVEs
Dependencies
pip install requests>=2.31standards.md2.7 KB
Standards and References - EPSS Vulnerability Prioritization
Primary Standards
FIRST EPSS
- Source: Forum of Incident Response and Security Teams
- URL: https://www.first.org/epss/
- API: https://api.first.org/data/v1/epss
- Model: Machine learning trained on real exploitation events, updated daily
- Versions: v1 (2021), v2 (2022), v3 (2023), v4 (2025)
CVSS v3.1 and v4.0
- Source: FIRST
- URL: https://www.first.org/cvss/
- Relevance: EPSS complements CVSS; CVSS measures severity, EPSS measures exploitation probability
CISA Stakeholder-Specific Vulnerability Categorization (SSVC)
- URL: https://www.cisa.gov/stakeholder-specific-vulnerability-categorization-ssvc
- Relevance: SSVC uses exploitation status as a key decision point; EPSS provides data-driven input
CISA Known Exploited Vulnerabilities (KEV)
- URL: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
- Relevance: KEV confirms active exploitation; EPSS predicts future exploitation probability
Research Papers
Original EPSS Paper
- Title: "Improving Vulnerability Remediation Through Better Exploit Prediction"
- Authors: Jay Jacobs, Sasha Romanosky, Benjamin Edwards, Michael Roytman, Idris Adjerid
- Published: Workshop on the Economics of Information Security (WEIS), 2021
EPSS v3 Model
- Features: 1,477 features including CVE properties, vendor data, social media mentions, exploit code availability
- Training Data: Historical exploitation events from multiple sources
- Performance: AUC of 0.85+ for 30-day exploitation prediction
Data Sources Used by EPSS
| Source | Data Type | Update Frequency |
|---|---|---|
| NVD | CVE metadata, CVSS scores | Real-time |
| CISA KEV | Confirmed exploitation | As new CVEs added |
| Exploit-DB | Public exploit code | Daily |
| GitHub | Exploit PoC repositories | Daily |
| Metasploit | Exploit modules | Weekly |
| SecurityFocus | Vulnerability discussions | Daily |
| Social Media | Twitter/X mentions of CVEs | Real-time |
| Fortinet | Exploitation telemetry | Daily |
| AlienVault OTX | Threat intelligence | Daily |
API Reference
Endpoints
- Single CVE:
GET https://api.first.org/data/v1/epss?cve=CVE-YYYY-NNNNN - Multiple CVEs:
GET https://api.first.org/data/v1/epss?cve=CVE-1,CVE-2,... - Date-specific:
GET https://api.first.org/data/v1/epss?cve=CVE-YYYY-NNNNN&date=YYYY-MM-DD - Time series:
GET https://api.first.org/data/v1/epss?cve=CVE-YYYY-NNNNN&scope=time-series - Top scoring:
GET https://api.first.org/data/v1/epss?percentile-gt=0.95 - Full download:
https://epss.cyentia.com/epss_scores-current.csv.gz
workflows.md1.3 KB
Workflows - EPSS Vulnerability Prioritization
Workflow 1: Daily EPSS Enrichment Pipeline
Steps
- Download full EPSS dataset from https://epss.cyentia.com/epss_scores-current.csv.gz
- Load into local database for fast lookups
- Query open vulnerabilities from vulnerability management platform
- Enrich each CVE with current EPSS score and percentile
- Apply priority matrix combining EPSS and CVSS scores
- Update priority fields in DefectDojo/Jira/tracking system
- Alert on any CVEs that crossed EPSS threshold (e.g., jumped above 0.4)
Workflow 2: EPSS Spike Detection
Steps
- Compare today's EPSS scores against yesterday's scores for all open CVEs
- Identify CVEs with EPSS increase > 0.2 in past 24 hours
- Cross-reference spike CVEs with asset inventory
- Send high-priority alert for spiking CVEs affecting production assets
- Automatically escalate to P1 if EPSS crosses 0.7 threshold
Workflow 3: Prioritized Remediation Report
Steps
- Pull all open vulnerabilities from scanner
- Enrich with EPSS scores and CISA KEV membership
- Apply combined EPSS + CVSS + KEV priority matrix
- Group by priority tier (P0-P4)
- Within each tier, sort by EPSS score descending
- Generate report showing estimated risk reduction per remediation action
- Distribute to asset owners with assigned remediation timelines
Scripts 2
agent.py5.0 KB
#!/usr/bin/env python3
"""Agent for implementing EPSS (Exploit Prediction Scoring System) for vulnerability prioritization."""
import json
import argparse
import csv
try:
import requests
except ImportError:
requests = None
EPSS_API_URL = "https://api.first.org/data/v1/epss"
def get_epss_scores(cve_list):
"""Fetch EPSS scores for a list of CVE IDs from the FIRST.org API."""
if not requests:
return {"error": "requests library not installed"}
results = []
# API supports up to 100 CVEs per request
for i in range(0, len(cve_list), 100):
batch = cve_list[i:i + 100]
params = {"cve": ",".join(batch)}
resp = requests.get(EPSS_API_URL, params=params, timeout=30)
resp.raise_for_status()
data = resp.json()
for item in data.get("data", []):
results.append({
"cve": item["cve"],
"epss": float(item["epss"]),
"percentile": float(item["percentile"]),
})
return {"total": len(results), "scores": results}
def get_epss_csv():
"""Download the full EPSS score CSV from FIRST.org."""
if not requests:
return {"error": "requests library not installed"}
resp = requests.get(f"{EPSS_API_URL}?envelope=true&pretty=true", timeout=60)
resp.raise_for_status()
return resp.json()
def prioritize_vulnerabilities(cve_scores, epss_threshold=0.1, percentile_threshold=0.9):
"""Prioritize vulnerabilities based on EPSS score and percentile."""
critical = []
high = []
medium = []
low = []
for item in cve_scores:
epss = item["epss"]
pct = item["percentile"]
if epss >= epss_threshold or pct >= percentile_threshold:
item["priority"] = "CRITICAL"
critical.append(item)
elif epss >= 0.05:
item["priority"] = "HIGH"
high.append(item)
elif epss >= 0.01:
item["priority"] = "MEDIUM"
medium.append(item)
else:
item["priority"] = "LOW"
low.append(item)
return {
"thresholds": {"epss": epss_threshold, "percentile": percentile_threshold},
"summary": {
"critical": len(critical),
"high": len(high),
"medium": len(medium),
"low": len(low),
},
"critical": sorted(critical, key=lambda x: x["epss"], reverse=True),
"high": sorted(high, key=lambda x: x["epss"], reverse=True),
}
def enrich_from_scan(scan_file, output_file=None):
"""Enrich a vulnerability scan CSV with EPSS scores."""
with open(scan_file, "r") as f:
reader = csv.DictReader(f)
rows = list(reader)
cve_col = None
for col in ["CVE", "cve", "CVE-ID", "cve_id", "vulnerability_id"]:
if col in (rows[0] if rows else {}):
cve_col = col
break
if not cve_col:
return {"error": "No CVE column found in scan file"}
cves = [row[cve_col] for row in rows if row.get(cve_col, "").startswith("CVE-")]
if not cves:
return {"error": "No CVE IDs found in scan file"}
epss_data = get_epss_scores(cves)
epss_map = {s["cve"]: s for s in epss_data.get("scores", [])}
enriched = []
for row in rows:
cve = row.get(cve_col, "")
epss_info = epss_map.get(cve, {})
row["epss_score"] = epss_info.get("epss", "N/A")
row["epss_percentile"] = epss_info.get("percentile", "N/A")
enriched.append(row)
if output_file:
fieldnames = list(enriched[0].keys())
with open(output_file, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(enriched)
prioritized = prioritize_vulnerabilities(
[s for s in epss_data.get("scores", [])]
)
return {
"scan_file": scan_file,
"total_cves": len(cves),
"enriched_count": sum(1 for r in enriched if r["epss_score"] != "N/A"),
"prioritization": prioritized["summary"],
"top_10_exploitable": prioritized.get("critical", [])[:10],
}
def main():
parser = argparse.ArgumentParser(description="EPSS Vulnerability Prioritization Agent")
sub = parser.add_subparsers(dest="command")
s = sub.add_parser("score", help="Get EPSS scores for CVE IDs")
s.add_argument("--cves", nargs="+", required=True, help="CVE IDs (e.g., CVE-2024-1234)")
e = sub.add_parser("enrich", help="Enrich vulnerability scan with EPSS scores")
e.add_argument("--scan-file", required=True, help="CSV vulnerability scan report")
e.add_argument("--output", help="Output enriched CSV file")
args = parser.parse_args()
if args.command == "score":
epss = get_epss_scores(args.cves)
result = prioritize_vulnerabilities(epss.get("scores", []))
result["raw_scores"] = epss["scores"]
elif args.command == "enrich":
result = enrich_from_scan(args.scan_file, args.output)
else:
parser.print_help()
return
print(json.dumps(result, indent=2, default=str))
if __name__ == "__main__":
main()
process.py7.5 KB
#!/usr/bin/env python3
"""EPSS Vulnerability Prioritization Tool.
Fetches EPSS scores from FIRST API and prioritizes vulnerabilities
using a combined EPSS + CVSS matrix approach.
"""
import argparse
import csv
import gzip
import io
import json
import sys
import time
from datetime import datetime, timezone
from pathlib import Path
import requests
EPSS_API = "https://api.first.org/data/v1/epss"
EPSS_BULK_URL = "https://epss.cyentia.com/epss_scores-current.csv.gz"
KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
PRIORITY_MAP = {
"P0": {"label": "Immediate", "sla_hours": 24},
"P1": {"label": "Urgent", "sla_hours": 48},
"P2": {"label": "High", "sla_days": 7},
"P3": {"label": "Medium", "sla_days": 30},
"P4": {"label": "Low", "sla_days": 90},
}
def fetch_epss_bulk():
"""Download full EPSS dataset for local lookups."""
print("[*] Downloading full EPSS dataset...")
resp = requests.get(EPSS_BULK_URL, timeout=60)
resp.raise_for_status()
content = gzip.decompress(resp.content).decode("utf-8")
reader = csv.DictReader(io.StringIO(content))
scores = {}
for row in reader:
cve = row.get("cve", "").strip()
if cve:
scores[cve] = {
"epss": float(row.get("epss", 0)),
"percentile": float(row.get("percentile", 0)),
}
print(f" Loaded EPSS scores for {len(scores)} CVEs")
return scores
def fetch_epss_api(cve_list):
"""Fetch EPSS scores for specific CVEs via API."""
scores = {}
batch_size = 100
for i in range(0, len(cve_list), batch_size):
batch = cve_list[i : i + batch_size]
try:
resp = requests.get(
EPSS_API, params={"cve": ",".join(batch)}, timeout=30
)
if resp.status_code == 200:
for entry in resp.json().get("data", []):
scores[entry["cve"]] = {
"epss": float(entry.get("epss", 0)),
"percentile": float(entry.get("percentile", 0)),
}
except requests.RequestException as e:
print(f"[-] EPSS API error: {e}")
time.sleep(0.5)
return scores
def fetch_kev_catalog():
"""Download CISA KEV catalog."""
resp = requests.get(KEV_URL, timeout=30)
resp.raise_for_status()
return {v["cveID"] for v in resp.json().get("vulnerabilities", [])}
def assign_priority(epss_score, cvss_score, in_kev=False):
"""Assign priority based on EPSS + CVSS + KEV matrix."""
if in_kev:
if cvss_score >= 9.0:
return "P0"
return "P1"
if epss_score > 0.7 and cvss_score >= 9.0:
return "P0"
if epss_score > 0.7 and cvss_score >= 7.0:
return "P1"
if epss_score > 0.4 and cvss_score >= 7.0:
return "P2"
if epss_score > 0.1 or cvss_score >= 7.0:
return "P3"
return "P4"
def prioritize_scan_results(input_csv, output_csv, use_bulk=False):
"""Enrich vulnerability scan results with EPSS and prioritize."""
vulnerabilities = []
with open(input_csv, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
vulnerabilities.append(row)
cve_list = list({v.get("cve_id", "") for v in vulnerabilities if v.get("cve_id")})
print(f"[*] Processing {len(vulnerabilities)} findings ({len(cve_list)} unique CVEs)")
if use_bulk:
epss_scores = fetch_epss_bulk()
else:
epss_scores = fetch_epss_api(cve_list)
print("[*] Fetching CISA KEV catalog...")
kev_set = fetch_kev_catalog()
print(f" {len(kev_set)} CVEs in KEV catalog")
results = []
for vuln in vulnerabilities:
cve_id = vuln.get("cve_id", "")
cvss = float(vuln.get("cvss_score", 0))
epss_data = epss_scores.get(cve_id, {"epss": 0, "percentile": 0})
in_kev = cve_id in kev_set
priority = assign_priority(epss_data["epss"], cvss, in_kev)
results.append({
**vuln,
"epss_score": round(epss_data["epss"], 5),
"epss_percentile": round(epss_data["percentile"], 5),
"in_cisa_kev": in_kev,
"priority": priority,
"priority_label": PRIORITY_MAP[priority]["label"],
})
results.sort(key=lambda r: (
{"P0": 0, "P1": 1, "P2": 2, "P3": 3, "P4": 4}[r["priority"]],
-r["epss_score"],
))
if results:
with open(output_csv, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=results[0].keys())
writer.writeheader()
writer.writerows(results)
priority_counts = {}
for r in results:
p = r["priority"]
priority_counts[p] = priority_counts.get(p, 0) + 1
print(f"\n[+] Prioritization Results -> {output_csv}")
for p in ["P0", "P1", "P2", "P3", "P4"]:
count = priority_counts.get(p, 0)
print(f" {p} ({PRIORITY_MAP[p]['label']}): {count}")
print(f" KEV matches: {sum(1 for r in results if r['in_cisa_kev'])}")
return results
def detect_epss_spikes(previous_csv, current_scores, threshold=0.2):
"""Compare EPSS scores to detect significant increases."""
previous = {}
with open(previous_csv, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
cve = row.get("cve_id", "")
if cve:
previous[cve] = float(row.get("epss_score", 0))
spikes = []
for cve, prev_score in previous.items():
current = current_scores.get(cve, {}).get("epss", 0)
increase = current - prev_score
if increase >= threshold:
spikes.append({
"cve_id": cve,
"previous_epss": prev_score,
"current_epss": current,
"increase": round(increase, 5),
})
spikes.sort(key=lambda s: s["increase"], reverse=True)
if spikes:
print(f"\n[!] EPSS Spikes Detected ({len(spikes)} CVEs):")
for s in spikes[:20]:
print(f" {s['cve_id']}: {s['previous_epss']:.4f} -> {s['current_epss']:.4f} (+{s['increase']:.4f})")
return spikes
def main():
parser = argparse.ArgumentParser(description="EPSS Vulnerability Prioritization Tool")
parser.add_argument("--input", help="Input CSV with vulnerability scan results")
parser.add_argument("--output", default="epss_prioritized.csv", help="Output prioritized CSV")
parser.add_argument("--bulk", action="store_true", help="Use bulk EPSS download instead of API")
parser.add_argument("--detect-spikes", help="Previous results CSV for spike detection")
parser.add_argument("--spike-threshold", type=float, default=0.2, help="EPSS increase threshold")
parser.add_argument("--query", help="Query EPSS for specific CVE(s), comma-separated")
args = parser.parse_args()
if args.query:
cves = [c.strip() for c in args.query.split(",")]
scores = fetch_epss_api(cves)
for cve, data in scores.items():
pct = data["epss"] * 100
print(f"{cve}: {pct:.2f}% exploitation probability (percentile: {data['percentile']:.4f})")
elif args.input:
results = prioritize_scan_results(args.input, args.output, args.bulk)
if args.detect_spikes:
current_scores = {r["cve_id"]: {"epss": r["epss_score"]} for r in results if r.get("cve_id")}
detect_epss_spikes(args.detect_spikes, current_scores, args.spike_threshold)
else:
parser.print_help()
if __name__ == "__main__":
main()