FortWatch.ai

EPSS vs CVSS vs CISA KEV: The Patch-First Rule a Five-Person Team Can Run

FortWatch

FortWatch Team

EPSS vs CVSS vs CISA KEV: The Patch-First Rule a Five-Person Team Can Run

Microsoft rated CVE-2023-24880, a Windows SmartScreen bypass, at CVSS 4.4 — Medium. Google's Threat Analysis Group had by then watched Magniber ransomware push "over 100,000 downloads of the malicious MSI files since January 2023, with over 80% to users in Europe" through that exact bug. Microsoft patched on 2023-03-14, CISA added the CVE to its Known Exploited Vulnerabilities catalog the same day, and EPSS read 0.004 the next day. That is the EPSS vs CVSS argument in a single CVE: the score said Medium, the wire said ransomware.

A CVSS base score tells you how bad a bug could be in a lab; it tells you nothing about whether anyone is using it, or whether they can reach it on your network. Sorted by CVSS, that 4.4 sat below thousands of Criticals nobody has ever exploited.

The fix is not a better severity score. It is two free feeds and an inventory of what an attacker can reach.

The short version:

  • CVSS measures severity in the abstract. FIRST's own user guide says a CVSS-B score "should not be used alone to assess risk" — that is the spec author talking, not a critic.
  • CISA KEV is a fact, not a model: 1,713 CVEs with confirmed exploitation in the wild, roughly half a percent of everything EPSS scores. It goes first, whatever the CVSS says.
  • EPSS is a forecast: the probability that exploitation activity will be observed in the next 30 days. Free, no key, republished daily.
  • Neither signal subsumes the other. Today 24.4% of KEV entries score below EPSS 0.10, and 92.5% of the CVEs above 0.10 are not in KEV.
  • Exposure multiplies all three. The order that works: in KEV and internet-reachable, then EPSS at or above 0.10 and reachable, then CVSS as the tie-breaker — never the sort key.
SignalWhat it measuresScale, source and cadenceWhat it cannot tell you
CVSS-B (base score) The severity of a bug in the abstract: intrinsic exploitability and impact against a reachable, worst-case target. 0 to 10, per the FIRST specification. Scored by NVD or by the vendor's CNA, and by design it does not move when exploitation starts. Whether anyone is exploiting it, whether anyone ever will, or whether the affected service is exposed in your environment.
EPSS The probability that exploitation activity against the CVE will be observed in the wild in the next 30 days. 0 to 1, with a percentile rank alongside. FIRST and Empirical Security, republished daily "typically within a minute or two after 13:30 UTC", free and without a key. Impact, whether an observed attempt actually succeeded, or whether you run the affected software at all.
CISA KEV Confirmed exploitation in the wild: a record of what has already happened, with a required remediation action. Membership, yes or no, plus a due date. Published by CISA as JSON and CSV, in batches most weeks. Anything it has not catalogued yet — and past the deadline tier CISA sets on each entry, it does not rank the 1,713 entries against one another.

Dates and denominators: EPSS API spot checks 2026-09-16; the EPSS bulk file with score_date 2026-09-17, model v2026.06.15; KEV catalogVersion 2026.09.16, 1,713 entries; NVD API 2.0 hasKev and date-range queries pulled 2026-09-17; every join between the feeds run on 2026-09-18. Every percentage below uses the EPSS file, 375,610 scored CVEs, as its denominator. EPSS moves every day, so every score here is a dated snapshot, not a property of the bug.

CVSS measures severity in a lab. Most CVEs are never exploited.

CVSS is a severity score — an estimate of how bad exploitation would be if it happened — and FIRST, which owns the standard, says so in its own documentation: "CVSS is designed to measure the severity of a vulnerability and should not be used alone to assess risk." That is section 2.1 of the v3.1 user guide, and it survives almost verbatim in the v4.0 guide. The reason is structural, not editorial. The Base group "represents the intrinsic qualities of a vulnerability that are constant over time and across user environments" — constant across environments means it cannot know whether anyone is attacking the bug today, or whether you are even running the affected code. v4.0 replaced v3.1's Temporal group with a Threat group whose single Exploit Maturity metric exists to pull those numbers back down, "addressing concerns that many CVSS (Base) scores are too high." A bare NVD or vendor number is what v4.0's nomenclature calls a CVSS-B value, and CVSS-B is precisely what FIRST says not to use alone.

The base rate is why it matters. About 6% of all published CVEs have ever been observed with exploitation activity in the wild. In any 30-day window, across every EPSS data partner, activity shows up against 2.5-3%. CISA's KEV catalog holds 1,713 entries against the 375,610 CVEs EPSS scored on 2026-09-17: 0.46%.

That gap is paid for in engineer-hours. 48,185 CVEs were published in 2025, 3,984 of them Critical and 15,003 High. That is roughly 19,000 items a CVSS-sorted queue stamps "fix now" in a single year, drawn from a population whose all-time exploitation rate is the 6% above.

The trade has been measured by the people who build the models. At EPSS v4's release, Empirical Security scored the "patch everything CVSS 7 and above" strategy at 50.7% effort for 74.6% coverage of exploited CVEs; EPSS held that same coverage at 6% effort, cutting the work by more than eight times. Cyentia ran it at fixed effort and got 93% coverage from EPSS versus 37% from a CVSS 9+ strategy.

Make it concrete. Ask NVD for every CVE published in June 2024 with a v3 severity of Critical (cvssV3Severity=CRITICAL, pubStartDate=2024-06-01 to pubEndDate=2024-06-30) and you get 256 records, all of them EPSS-scored. Scored against the 2026-09-17 bulk file, more than two years after publication: 200 (78.1%) sit below EPSS 0.01, 237 (92.6%) below 0.10, and exactly three are in KEV — CVE-2024-4577, CVE-2024-37079 and CVE-2024-4885. A 9.8 with no exploitation signal behind it is the normal outcome, not the exception.

Now invert it. Take the 1,712 KEV entries NVD holds a record for and give each the highest CVSS v3.x base score from any source on that record — the most generous possible reading for a CVSS-first program, since a different metric-source rule produces a different split.

CVSS v3.x band (highest score on record)KEV entriesShare
9.0 - 10.0 (Critical)64837.9%
7.0 - 8.9 (High)88951.9%
4.0 - 6.9 (Medium)17210.0%
Below 4.0 (Low)30.2%

1,064 of the 1,712 (62.1%) score below 9.0 and 175 (10.2%) below 7.0; the three under 4.0 are CVE-2023-26083 (3.3), CVE-2013-2423 (3.7) and CVE-2023-20867 (3.9). A "Criticals only" queue skips nearly two-thirds of the bugs we have confirmed are exploited — and that is before the number you sorted by turns out to be disputed between assessors or missing altogether, which is the failure-modes section below.

EPSS: a daily probability, a percentile, and a free API

EPSS is the probability, between 0 and 1, that exploitation activity against a CVE will be observed in the wild by EPSS's data partners in the next 30 days. A score of 0.05 means a 5% estimated probability. FIRST's own definition is one sentence: "a data-driven model that estimates the probability a vulnerability will be exploited in the wild within the next 30 days." What it counts is attempted exploitation seen by sensors. Not impact. Not whether the attempt worked. Not whether you run the software: EPSS "does not measure how much damage successful exploitation would cause, or whether it affects your environment."

The model is built by Empirical Security and published by FIRST, trained on exploitation observed by sensor partners. It takes "all individual CVSS metrics as input features (scores are ignored)" — AV, AC, PR and the rest, never the headline number — plus exploit code availability, malware and endpoint detections, Shodan scans, HackerOne Hacktivity reports, and CNA/ADP data from cve.org. Model v5 has been publishing since 2026-06-15 and scores every published CVE.

New scores go out daily, "typically within a minute or two after 13:30 UTC." There have been five model versions: v1 on 2021-04-14, v2 on 2022-02-04, v3 on 2023-03-07, v4 on 2025-03-17, v5 on 2026-06-15. Each boundary shifted scores on its first day, and FIRST's warning is the part most explainers skip: "a time series that crosses one of these boundaries reflects a change in methodology, not a change in the vulnerability itself." A CVE that jumped across 2025-03-17 or 2026-06-15 did not attract new attacker interest. It got a new model.

What does the EPSS percentile mean?

The probability is absolute; the percentile is a rank against every other scored CVE. A vulnerability at the 90th percentile scores higher than 90% of all scored vulnerabilities — no more than that. The distribution is brutally skewed: on the 2026-09-17 bulk file the median is 0.0066 and the mean 0.0274, so a probability of 0.04 already sits at roughly the 90th percentile and 0.10 at about the 95th. Filter on the probability. Quote the percentile for context only.

What is a good EPSS score?

There is no good score. FIRST prescribes no cutoff — it publishes reference points and leaves the choice to you, because a threshold is a statement about your capacity rather than about danger. The decision rule below uses 0.10 and shows the arithmetic behind it.

Both sources are free, with no key and no account. The REST API takes up to 2,000 characters of comma-separated CVE IDs, plus date, epss-gt, percentile-gt and scope=time-series; the v1 in the URL is the API version, not the model version. For joining a whole inventory, FIRST points you at the daily bulk CSV instead — one download beats thousands of calls. The bulk host is now epss.empiricalsecurity.com; the old epss.cyentia.com URL redirects, so pass -L.

curl -s "https://api.first.org/data/v1/epss?cve=CVE-2023-24880,CVE-2021-44228" \
  | jq -r '.data[] | "\(.cve) epss=\(.epss) pct=\(.percentile) \(.date)"'

Add date= to ask what the score was on a given day. This is how you audit whether a signal would have caught something — here, the day after Microsoft patched the SmartScreen bypass from the top of this post:

curl -s "https://api.first.org/data/v1/epss?cve=CVE-2023-24880&date=2023-03-15" | jq '.data[0].epss'
# "0.004040000"

And the bulk file, whose first line names exactly which model produced the numbers you are about to join against:

curl -sL -o epss.csv.gz https://epss.empiricalsecurity.com/epss_scores-current.csv.gz \
  && zcat epss.csv.gz | head -1
# #model_version:v2026.06.15,score_date:2026-09-17T12:00:22Z

Read that header every time you pull. It is the difference between "this CVE got riskier" and "the model changed."

The CISA KEV catalog: three criteria, BOD 26-04, and the JSON feed

The KEV catalog is CISA's list of vulnerabilities with reliable evidence of active exploitation in the wild — a record of what has already happened, not a forecast of what might. Three things get a CVE in: an assigned CVE ID, reliable evidence of active exploitation, and a clear remediation action. Read that third criterion precisely. It is a documented remediation action, not necessarily a vendor patch: about 80 live entries have no update path at all and tell you to retire the product instead — 52 read "The impacted product is end-of-life and should be disconnected if still in use", eight more name end-of-life or end-of-service status and say to discontinue use, and the rest are one-off wordings for Ivanti CSA, an end-of-life SharePoint and legacy D-Link hardware. Do not try to read patch availability out of the requiredAction text itself: 893 entries carry the generic "Apply updates per vendor instructions", and every entry added since 2026-06-11 carries the same BOD 26-04 action string whether or not the vendor has shipped a fix. Only federal civilian executive branch agencies are bound by the directive behind it; CISA's own position is that every organization strengthens its posture by prioritizing KEV.

What is BOD 26-04, and what happened to BOD 22-01?

BOD 22-01 created the catalog on 2021-11-03 with flat deadlines: six months for CVEs assigned before 2021, two weeks for everything else. In practice 21 days became the most common gap between dateAdded and dueDate — 1,025 entries added between 2022-01-01 and 2026-06-09. That model is gone. BOD 26-04, issued 2026-06-10, supersedes and revokes both BOD 22-01 and BOD 19-02, and derives every due date from four variables: KEV status, public exposure, exploit automation, and technical impact. CERT/CC formalized it as a 16-row SSVC table with five outcomes: 3 days plus forensic triage of the asset, 3 days, 14 days, 60 days, or fix on the next system upgrade. All 96 entries added since 2026-06-10 carry a 3-day due date (73) or a 14-day one (23), with no other gap in the feed.

That federal table is worth borrowing even if no directive binds you, because it is the first federal remediation rule that multiplies exploitation by exposure — exactly the shape of the rule below. Note what "publicly exposed" means there: a property of the asset, "accessible to unauthenticated or untrusted entities via public networks", not a property of the CVE's attack vector. It also shrinks the urgent queue instead of growing it: in an initial CISA analysis at one large civilian agency, as reported by Tenable, only 1% of vulnerability instances landed in the three-day tier and over 60% qualified for deferral to the next system upgrade.

How often is the KEV catalog updated?

About three days a week, in batches: the feed carries entries on 105 distinct dateAdded days against the 185 weekdays elapsed so far in 2026. Additions are announced as "CISA Adds N Known Exploited Vulnerabilities" alerts, and the JSON feed's catalogVersion carries the release date, 2026.09.16 at the time of writing. Of the entries in it, 360 (21%) are flagged as used in ransomware campaigns and 55 carry forensicTriage: Yes. Additions by year: 311 in 2021, 555 in 2022, then 187, 186 and 245, with 229 so far in 2026.

The catalog ships as JSON, as CSV, with a published JSON schema, and as a GitHub mirror if you would rather diff commits than poll a URL. Each entry carries cveID, vendorProject, product, vulnerabilityName, dateAdded, shortDescription, requiredAction, dueDate, knownRansomwareCampaignUse, forensicTriage, notes and cwes. Write a parser against the published schema and you silently drop forensicTriage: it is new under BOD 26-04 and still absent from that schema as of 2026-09-18 — and it is the flag that tells you an asset needs a compromise check, not just a patch.

# 1. Pull the feed, check what version you got
curl -s -o kev.json https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
jq -r '.catalogVersion, .count' kev.json
# 2026.09.16
# 1713

# 2. The three newest entries, with deadlines and both flags
jq -r '.vulnerabilities | sort_by(.dateAdded) | reverse | .[:3][] | "\(.dateAdded) \(.cveID) \(.vendorProject) \(.product) due=\(.dueDate) ransomware=\(.knownRansomwareCampaignUse) forensicTriage=\(.forensicTriage)"' kev.json
# 2026-09-16 CVE-2026-87886 Acronis Backup due=2026-09-19 ransomware=Unknown forensicTriage=Yes
# 2026-09-16 CVE-2026-76460 Cisco Identity Services Engine due=2026-09-19 ransomware=Unknown forensicTriage=Yes
# 2026-09-16 CVE-2026-58704 Google Pixel due=2026-09-19 ransomware=Unknown forensicTriage=Yes

# 3. How much of the catalog has ransomware behind it
jq '[.vulnerabilities[] | select(.knownRansomwareCampaignUse=="Known")] | length' kev.json
# 360

Membership is where the work starts, not where it ends. Bitsight, looking at 1.4 million organizations, measured a 174-day median time to fix a KEV entry, with more than 60% remediated after the CISA deadline had already passed.

EPSS vs CVSS vs CISA KEV: nine real CVEs where the signals disagree

When the three signals agree, prioritization is not the hard part. Log4Shell, CVE-2021-44228, is CVSS 10.0, was in KEV on the day it was published, and sits at EPSS 0.99999 today. Any rule catches that one. The rule exists for the nine rows below.

BandCVEWhat it isCVSS (source)EPSS (2026-09-16)In KEV
Low CVSS, confirmed exploitedCVE-2023-24880Windows SmartScreen bypass4.4 (Microsoft and NVD agree)0.782Yes, 2023-03-14, ransomware
Low CVSS, confirmed exploitedCVE-2024-55550Mitel MiCollab admin-only file read2.7 (NVD) / 4.4 (CISA-ADP)0.379Yes, 2025-01-07, ransomware
Low CVSS, confirmed exploitedCVE-2023-20867VMware Tools guest authentication bypass3.9 (VMware and NVD agree)0.135Yes, 2023-06-23
High CVSS, nobody exploitsCVE-2024-32911Android privilege escalation via misused crypto (Google CNA)9.8 (NVD) / 7.5 (CISA-ADP)0.00192No
High CVSS, nobody exploitsCVE-2024-38432Matrix Tafnit v8 file-upload flaw (CWE-646)9.8 (NVD) / 5.5 (CNA)0.00173No
High EPSS, no KEVCVE-2024-6387regreSSHion, OpenSSH signal-handler race8.1 (NVD)0.995No
High EPSS, no KEVCVE-2024-3094xz-utils backdoor10.0 (NVD)0.860No
EPSS lateCVE-2023-48365Qlik Sense incomplete-patch bypass9.9 (NVD) / 9.6 (MITRE)0.245Yes, 2025-01-13, ransomware
EPSS lateCVE-2023-34362MOVEit Transfer SQL injection9.8 (NVD)0.99934Yes, 2023-06-02

CVSS from the NVD record, with the CNA or CISA-ADP score shown where they differ; EPSS from the FIRST API on 2026-09-16, so re-pull before you act on it; KEV from catalogVersion 2026.09.16. CVE IDs named in the narratives below are context, not rows.

Low CVSS, confirmed exploited

SmartScreen is the familiar case. The other two teach more. Mitel CVE-2024-55550 is a file read that needs administrator rights, hence NVD's 2.7. It matters because watchTowr published a chain pairing it with the unauthenticated path traversal CVE-2024-41713 (9.1 on NVD); CISA added both on 2025-01-07 and flags the 2.7 as ransomware-linked. VMware Tools CVE-2023-20867 is 3.9 from vendor and NVD alike because AV:L/AC:H/PR:H assumes an attacker who still has to get somewhere. Mandiant documented UNC3886 already standing there, executing privileged commands into Windows, Linux and PhotonOS guests from a compromised ESXi host without authenticating guest credentials. The preconditions were the attacker's starting position, not an obstacle.

High CVSS, nobody exploits

Both rows carry an NVD 9.8 next to a much lower second opinion: CISA-ADP says 7.5 for the Android bug, the issuing CNA says 5.5 for the Tafnit one. EPSS has both under 0.003 and neither is in KEV more than two years on. The word Critical on a queue item is often one analyst's reading of one vector, not a consensus. Record which source produced the number you sorted on.

High EPSS, no KEV

regreSSHion at 0.995 and the xz-utils backdoor at 0.860 are the reverse case, and they need reading precisely: sensors see mass activity, and CISA has catalogued no confirmed exploitation with a remediation action for either. EPSS is evidence that exploitation was attempted, not that it succeeded, and its authors state the model cannot separate researcher and commercial scanning from malicious activity. xz is the odder of the two: the backdoor only answers to the implanter's key, so its score reflects detections and version scanning rather than attempts anyone else could land — it read 0.00046 on 2024-04-01. Read a high score with no KEV entry as "the internet is hammering on this", not as a breach report.

EPSS late

Arctic Wolf reported Cactus ransomware exploiting the Qlik Sense chain in November 2023. CISA added the sibling CVEs, CVE-2023-41265 and CVE-2023-41266, on 2023-12-07 with the ransomware flag set, one month later. The bypass, CVE-2023-48365, waited until 2025-01-13, thirteen months behind its siblings. KEV caught the product quickly. A team joining feeds by CVE ID alone, whose scanner had attached the bypass ID, saw nothing for the fourteen months between Arctic Wolf's report and that listing. EPSS saw nothing at all: 0.011 for CVE-2023-41265 and 0.0024 for CVE-2023-41266 the day after their KEV listing, 0.00082 for the bypass on 2023-12-15 and still 0.00082 on 2024-09-01. MOVEit is the same failure in fast-forward. KEV listed it on 2023-06-02, the day NVD published it; eight days later EPSS still read 0.019, two weeks into a Cl0p campaign that began on 2023-05-27, and it did not reach 0.94 until 2023-07-10. Emsisoft's tally of that campaign, to June 2024: 2,773 organizations and 95,788,491 individuals. The rules that fall out: join on product and version as well as CVE ID, and never wait for EPSS to confirm what KEV already says.

Where EPSS, CISA KEV and CVSS each fail

EPSS is a model of what sensors see

Its authors are explicit about where those sensors sit. The EPSS v3 paper states that detection "will be biased toward detecting network-based attacks", that the sensors live on "public-facing perimeter internet devices", and that the model is "not able to distinguish between exploit activity generated by researchers or commercial entities, versus actual malicious exploit activity". Targeted, hands-on-keyboard exploitation does not trip a honeypot. That is why 417 of the 1,712 KEV entries carrying an EPSS score — 24.4% — sit below 0.10 on a join of the two feeds, and 53 (3.1%) below 0.01.

The blind spot shows up live. The TeleMessage archiving backend CVE-2025-47729 (KEV 2025-05-12) has drifted from 0.069 in the week it was listed down to 0.0043, and the AD FS zero-day CVE-2026-56155 (KEV 2026-07-14) reads 0.0035, both on the scores published 2026-09-16. Both are catalogued as exploited; neither is visible to the model. CISA gave the AD FS bug 14 days, not 3, and the feed shows forensicTriage: No — under BOD 26-04 that is the KEV row where CISA judged the bug not automatable and the technical impact partial. The deadline came from those decision points, not from the CVSS vector; exposure there is a property of the asset, which is why an AV:L bug on a public-facing asset can still land in an exposed row. Model releases also move scores with nothing changing in the bug: curl's CVE-2023-38545 went from 0.0066 to 0.90 across the v4 boundary, and from 0.26 to 0.78 across v5. FIRST's own framing is the one to keep: "A low EPSS score on a KEV-listed vulnerability is not a contradiction."

KEV is a record, and records are late and narrow

An entry needs a clear remediation action, and the directive behind the due dates binds federal civilian agencies, not you. VulnCheck counted 495 newly exploited CVEs in the first half of 2026, 23.43% of them showing exploitation on or before the day the CVE was published. Of the 61 it observed being exploited against its own canary hosts, only 12 had been added to CISA KEV — that is 12 of 61, not 12 of 495. CISA added 245 entries in all of 2025. Absence from KEV is not evidence of non-exploitation. It is the absence of a catalogued report.

CVSS is an opinion, and often two

Of the same 1,712 KEV CVEs, 1,356 carry both an nvd@nist.gov score and at least one other assessor's, pooling v3.x and v4.0 metrics. Counting a record as disagreeing when any other assessor's score differs from NVD's, 314 disagree (23.2%) and 143 (10.5%) disagree far enough to land in different severity bands; restricted to v3.x-only pairs the disagreement rate is 18.4%. The spreads are not rounding errors: the NVD record for Zerologon, CVE-2020-1472, carries 10.0 from NVD and 5.5 from Microsoft. The 2026 Computers & Security paper "Fragmentation of CVSS scores in the NVD" puts the disagreement rate across all dual-assessed CVEs at roughly a third — the same effect at population scale.

The deeper problem is structural. The Base score models an unprivileged attacker starting from outside, which is why the VMware Tools bypass above scores 3.9 for an attacker who already owned the hypervisor. And since April 2026, NIST enriches only KEV-listed, federal-use and EO 14028 CVEs and no longer routinely adds its own severity score where the CNA supplied one. Your scanner's CVSS column is increasingly the vendor's number, or blank.

None of the three replaces another. KEV is a fact, EPSS is a forecast, CVSS is an impact estimate, and they stack in that order because certainty beats probability beats opinion. EPSS republishes every day and CISA adds to KEV most weeks, which is why the queue has to be refreshed continuously rather than exported once a quarter.

The decision rule: KEV, then EPSS, then CVSS, multiplied by exposure

Five tiers, asked in order. The first tier that matches is the one the finding gets. It is the four-factor framework with the feeds wired in.

One entry rule comes before the tiers, because two of the three signals only exist for CVEs. A finding with no CVE enters at the tier its impact and reachability justify, never at the bottom by default: reachable plus total control — an unauthenticated datastore, an exposed admin panel, a DNS record someone else can claim — is tier 1, the same SSVC row (publicly exposed, automatable, total impact) that gives a CVE three days.

  1. In KEV and reachable from the internet. Fix or mitigate within 72 hours, whatever CVSS says. Mitigation counts only if it removes reachability: block the port at the edge, disable the vulnerable feature, or take the service offline. If the entry carries forensicTriage: "Yes", or its dateAdded equals the CVE's publication date, treat it as an incident first: check the asset for prior compromise and rotate whatever the bug exposed. Patching closes the path; it does not revoke what was already taken.
  2. In KEV, not reachable. An entry added in the last 30 days gets tier 1 timing whether or not it is reachable — recent exploitation is the strongest signal either feed produces. For older entries on unreachable assets, 14 days, or accept with a written reason, evidence of unreachability and an expiry date. Never an open-ended waiver.
  3. Not in KEV, EPSS at or above 0.10, reachable. Seven days.
  4. EPSS at or above 0.10 but not reachable; or, on a reachable asset, CVSS 9.0 and above with a network attack vector, or CVSS 7.0 and above that is unauthenticated and needs no user interaction (AV:N/PR:N/UI:N). 30 days.
  5. Everything else. Sort by CVSS, batch into maintenance windows or fix on upgrade, re-score weekly. Both feeds move under you.

The same rule as a vulnerability remediation SLA you can paste into a ticket template:

TierConditionDeadlineNote
0No CVE: reachable and gives total control — unauthenticated datastore, exposed admin panel, takeover-able DNS record72 hoursPlaced by impact and reachability, because no feed scores it
1In KEV, internet-reachable, or any KEV entry added in the last 30 days72 hoursZero-day or forensicTriage: "Yes" adds a compromise check and rotation
2In KEV, not reachable, added more than 30 days ago14 daysOr accept, in writing, with evidence and an expiry date
3Not in KEV, EPSS >= 0.10, reachable7 daysProbability, not percentile
4EPSS >= 0.10 unreachable, or reachable with CVSS >= 9.0 (AV:N) or CVSS >= 7.0 (AV:N/PR:N/UI:N)30 daysBackstop for what both feeds missed
5Everything elseMaintenance window or fix on upgradeRe-score weekly

Tier 2 gets 14 days rather than 72 hours because FIRST says the KEV signal "degrades over time" — an old entry drifts back toward whatever its current features predict. That justifies a fortnight for an unreachable 2021 entry. It does not justify skipping it, and it says nothing about an entry added last week, which is why recency pulls a KEV item back into tier 1.

Seven days for tier 3 is not an arbitrary week. Mandiant measured an average time-to-exploit of five days across the 138 vulnerabilities it tracked as exploited in 2023 — 47 days with the outliers left in — and M-Trends 2026 puts the mean at "an estimated -7 days, meaning exploitation is routinely occurring before a patch is even released." A fortnight is longer than the attacker's head start. A week is roughly the length of it.

Tier 4 is the backstop for a join that misses. Qlik Sense is the case: the ID the scanner had attached was the bypass, and that ID sat at EPSS 0.00082 for thirteen months after KEV had already caught the product through its siblings. A 9.9 on an internet-facing asset was the only signal left.

Why 0.10

FIRST tells you to pick a threshold your team can clear, and gives two calibration points: EPSS 0.04, about the 90th percentile, costs roughly what a CVSS-Critical-only program costs, and 0.008 costs roughly what CVSS High-and-above costs. On today's feed 0.04 selects 37,457 of 375,610 scored CVEs (9.97%) and 0.10 selects 17,384 (4.63%). Those are selectivity figures, not workloads — effort is a property of your own inventory, not of the global corpus. Applied to your own findings, 0.10 cuts the EPSS-driven queue to roughly half what 0.04 produces. Pick the one whose output your team clears in a week; if capacity allows, drop to 0.04 — that is the lever, not the deadline. Re-check the counts after each model release.

When the CVSS number is disputed or missing

When a CVE carries two scores, use the higher and record both in the ticket, so the tier does not depend on which source your scanner read first. When there is no NVD score at all, fall back in order: the CISA-ADP vector if the record has one; then CISA's SSVC decision points, where Automatable: yes with Technical Impact: total on a reachable asset is CISA's own 3-day row, so treat it as tier 1 even with no CVSS at all. Anything short of that pair drops to tier 4, and a record with neither goes to tier 5 with a note that it needs a human look. Never let a blank CVSS field silently mean low.

The exposure multiplier

This is the input no feed supplies. FIRST's checks are presence, reachability and consequence; BOD 26-04's four variables are KEV status plus exposure, automatability and technical impact. Both put exposure first, which is the entire premise of external attack surface management. CISA gives you automatability and technical impact for many CVEs as SSVC decision points in the CVE record's CISA-ADP container; the command is in the next section. Nobody gives you the first one. You have to know which of your services answer from the public internet — that is asset discovery, and it is step zero below. Without it, every tier collapses into "everything".

Modifiers and the two things never to do

One modifier up: move an item one tier up if the asset holds customer data, credentials or signing material, or if it controls other systems — hypervisor, identity provider, backup server, CI/CD runner, management plane. The VMware Tools row above is why that second clause exists: a hypervisor holds none of the three, and whoever owns it owns every guest on it. One modifier down: an item drops a tier only with evidence it is unreachable — an external scan showing the port closed, the vulnerable feature disabled by config — never because "it's behind a WAF" or "EPSS is low". Log the reason so the next reader sees why a 9.8 sits in tier 5.

Never downgrade a KEV item because its EPSS is low. FIRST's own guidance is that "a recent KEV addition is reasonable grounds to treat a vulnerability as high priority regardless of its EPSS score". And never multiply EPSS by CVSS into a composite risk score. The same page is blunt about it: "Do not multiply an EPSS score by an ordinal (such as CVSS) and think that produces a combined 'risk score.'" One is a calibrated probability, the other an ordinal with no empirical calibration; the product has no units and no meaning.

Then there are the findings with no CVE at all. An unauthenticated Redis or MongoDB reachable from the internet, an exposed admin panel, a dangling subdomain on a deprovisioned SaaS tenant: no identifier, no CVSS vector, no EPSS score, no KEV entry. The entry rule above places them, by the same two questions. What does it give an attacker, and can an attacker reach it. An open Redis or MongoDB port is full compromise with or without a CVE. There is no exploit probability for a missing password, and severity should be set by impact and exposure, not by a lab score.

Wiring it up: exposure first, then a daily join in three lines

Step zero is the inventory, and it comes before the feeds: enumerate what is reachable, fingerprint the product and version, then map those strings to CVE IDs.

sudo nmap -sV -Pn -p- --open --min-rate 1000 -oX scan.xml 203.0.113.0/24
sudo nmap -sU -sV -Pn -p U:53,123,161,500,623,1900,5353,11211 --open -oX scan-udp.xml 203.0.113.0/24

shodan search --fields ip_str,port,product,version 'ssl.cert.subject.cn:"example.com"'

curl -s -X POST https://api.osv.dev/v1/query \
  -d '{"package":{"name":"org.apache.logging.log4j:log4j-core","ecosystem":"Maven"},"version":"2.14.1"}'

-p- covers 65,535 TCP ports and no UDP at all, which is where DNS, NTP, SNMP, IKE, IPMI, SSDP and mDNS answer — hence the second line, which needs root and names its ports rather than trusting --top-ports (memcached's UDP port ranks below 18,000 in nmap's frequency table; its TCP 11211 is what the first line catches). Both scans want -sV, because the product and version strings in the XML are what you match against advisories. The OSV query turns a package name and version straight into advisories with their CVE IDs; Maven packages are groupId:artifactId there, while npm, PyPI and Go take the bare package name. An empty {} means no match on the name you sent, not no advisories — check the name before you trust the silence. The output of step zero is one file, my_cves.txt, one CVE ID per line, restricted to services that actually answer from the public internet. If you cannot produce that file, stop here — nothing downstream works. The full Shodan walkthrough is how to find your company on Shodan, and version fingerprinting on public hosts is also where an end-of-life runtime turns a KEV entry into a reachable one.

With that file in hand, KEV is three lines.

curl -s -o kev.json https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
jq -r '.vulnerabilities[].cveID' kev.json | LC_ALL=C sort > kev_ids.txt
LC_ALL=C sort -u my_cves.txt | LC_ALL=C comm -12 - kev_ids.txt

That prints only the CVEs you have that CISA lists as actively exploited in the wild. No interpretation required: those lines are tiers 1 and 2. The LC_ALL=C prefixes are not decoration — comm compares in whatever collation each side was sorted with, and a cron job that inherits a different locale than your shell drops matches silently. Cron it daily after 14:00 UTC — EPSS publishes shortly after 13:30 UTC, so a single run covers both feeds — and page on any new line.

EPSS is two.

curl -sL -o epss.csv.gz https://epss.empiricalsecurity.com/epss_scores-current.csv.gz
zcat epss.csv.gz | grep -v '^#' | awk -F, 'NR>1 && $2+0>=0.10' | grep -F -w -f my_cves.txt

Note the NR>1 and the +0. The cve,epss,percentile header is not a comment line, so it survives the grep, and a bare string comparison lets it through the filter as well; the row index drops it and the arithmetic coercion keeps the test numeric. Note the -w too: without whole-word matching, CVE-2021-4428 happily matches CVE-2021-44280 and you inherit a stranger's score. The surviving rows are your tier 3 and 4 candidates. Pull the bulk file once a day for the join; keep the API for spot checks and history.

For the two SSVC variables that sharpen the middle tiers — Automatable and Technical Impact — CISA's ADP container carries them for many CVEs, though coverage is partial, so some records return nothing.

curl -s https://cveawg.mitre.org/api/cve/CVE-2023-48365 \
  | jq -c '.containers.adp[]? | select(.providerMetadata.shortName=="CISA-ADP") | .metrics[]?.other? | select(.type=="ssvc") | .content.options'

[{"Exploitation":"active"},{"Automatable":"no"},{"Technical Impact":"total"}]

Vulnrichment is the public repository behind that container. Automatable: yes with Technical Impact: total sharpens tiers 1 and 4, and it is the fallback worth having when a CVE arrives with no usable CVSS at all. On tooling: Grype v0.92.0 and later adds EPSS metrics to results and shows an indication of CISA known-exploited vulnerabilities, so a Grype scan already carries both signals — but its default table sorts by a computed risk column that multiplies EPSS by CVSS, exactly the composite FIRST tells you not to build, so run it with --sort-by kev or --sort-by epss and read the two signals separately. Trivy's maintainers declined native EPSS support as out of scope, so post-process its JSON with a plugin or with the join above.

Common questions about EPSS, CVSS and KEV

Does CISA KEV replace CVSS?

No. KEV tells you a bug has been exploited in the wild. It says nothing about how bad exploitation would be, and it covers 1,713 of the 375,610 scored CVEs. CVSS stays in the rule as the impact estimate and the tie-breaker for the other 99.5% of your queue, where KEV has nothing to say.

Is EPSS better than CVSS?

They answer different questions, so the comparison only means something per task. For deciding what to fix first, EPSS wins on measured effort and coverage. For deciding how much damage a fix prevents, CVSS is the only one of the three that even tries. Use EPSS to rank, CVSS to size.

What is the difference between a CVE and a KEV?

A CVE is an identifier for a vulnerability. A KEV entry is a CVE that CISA has catalogued on reliable evidence of active exploitation in the wild, with a clear remediation action and a due date attached. Every KEV entry is a CVE. About one in 220 scored CVEs is in KEV.

Who produces the EPSS scores?

Empirical Security generates them and FIRST republishes them daily, free and without registration, for every published CVE. The model is trained on exploitation activity observed by FIRST's data partners, which is why the scores reflect what perimeter sensors see rather than what any single organization experiences.

What percentage of CVEs are actually exploited in the wild?

Cyentia's EPSS study puts it at about 6% ever observed exploited, and FIRST's own telemetry sees activity against 2.5-3% of published CVEs in any 30-day window. CISA's catalogue of confirmed cases is smaller still, 0.46% of scored CVEs. The other side of that arithmetic — the 94% with no observed exploitation at all — is where a CVSS-sorted queue spends most of its year.

What do I do with this?

Eight steps, in order. The whole list is one week's work for a small team. The deadlines live in the table above; these are the commands and the artifacts they leave behind.

  1. Build my_cves.txt from the CVE IDs on your internet-facing services only, with the step-zero commands above. If you cannot yet say which services are internet-facing, that inventory is the whole week's work — do it first, because every tier multiplies by it.
  2. Run the KEV join today. Its output is tiers 1 and 2. Flag every hit carrying forensicTriage=Yes, every entry added in the last 30 days, and every entry whose dateAdded equals its CVE's publication date: those get a compromise check and a credential rotation, not just a patch.
  3. Match KEV on vendorProject and product too, not only by CVE ID, then check the vendor advisory for the affected version range against what your inventory says you are running. The Qlik Sense chain is what that step is for.
  4. Run the EPSS join at 0.10. Its output is tiers 3 and 4.
  5. Pull CVSS from both the CNA and NVD, use the higher, record both. No NVD score? Take the CISA-ADP vector. No vector either? Read the SSVC decision points — Automatable: yes plus Technical Impact: total on a reachable asset is tier 1. Nothing at all? Tier 5, flagged for a human.
  6. Add the two modifier fields to your ticket template: a flag for sensitive or controlling assets, and unreachability evidence with an expiry date.
  7. Cron both pulls daily after 14:00 UTC and alert on any new KEV intersection with your inventory.
  8. Merge the exposures that have no CVE into the same queue by impact: open datastores, exposed admin panels, dangling DNS records. Re-score everything weekly, and re-check your EPSS threshold after each model release.

One honest note on tooling. FortWatch does not ingest EPSS or the CISA KEV catalog. It supplies the other half of the rule: which of your services answer from the public internet, the CVE IDs and CVSS for what it fingerprints on them (via OSV, so the number is the advisory source's own, unaffected by NVD's enrichment stop), and severity set by impact and exposure rather than a lab score. Each issue comes with an AI explanation and a due date by severity, which you can set to match the tiers above. Pull KEV and EPSS with the commands in this post, join them on the CVE IDs already on your findings, and the queue is yours. If you want the exposure half handled for you, that is FortWatch vulnerability scanning.

Share this post
Get started

Ready to secure your infrastructure?

Try for free — scan your entire attack surface in under 5 minutes. No credit card required.

  • No credit card required

  • 14-Day free trial

Ready to secure your stack?

Secure your entire stack today

Start scanning in under 5 minutes. No credit card required. 14-day free trial included.