What actually predicts vulnerability exploitation?
Almost every organization prioritizes vulnerabilities by CVSS severity. Almost every organization is doing it wrong. Here is the evidence, measured over a corpus of ~330,000 CVEs, using CISA KEV (the Known Exploited Vulnerabilities catalog) as ground truth for "actually exploited in the wild."
Base rate to keep in mind: only 0.489% of CVEs are in KEV, roughly 1 in 200. Any useful signal has to beat that base rate by a lot. Most don't. A few do, dramatically.
Key findings (the short version)
- EPSS is ~10× better than CVSS at predicting exploitation. CVEs with EPSS ≥ 0.5 are exploited at 15.2% (a 31× lift over base rate). Critical CVSS (9.0–10.0) sits at just 1.4%.
- CVSS is nearly redundant once you have EPSS. Combining them barely beats EPSS alone (15.5% vs 15.2%).
- An exploit-tagged reference is a near-perfect filter. 99.6% of exploited CVEs have one; among CVEs without one, exploitation is 0.003%.
- Exploitation has a CWE fingerprint. Type-confusion (CWE-843) is exploited at 11.8× base rate; deserialization, auth-bypass, and OS-command-injection cluster at ~4–6×.
- And an ecosystem fingerprint. NuGet/.NET and Maven/Java packages are the most weaponized; Rust (crates.io) the least, a ~30× spread.
- Time-to-exploit has collapsed. Median days from CVE publication to KEV listing fell from ~951 days (2019) to ~2–8 days (2023–2026). The patch window is now a week.
- One intuitive hypothesis was false: "low-EPSS memory-safety bugs are a hidden exploited blind spot." The data says no.
The rest of this page shows the work.
EPSS vs CVSS: it's not close
EPSS (the Exploit Prediction Scoring System, from FIRST) estimates the probability a CVE will be exploited in the next 30 days. CVSS estimates severity, how bad it would be if exploited. People routinely use CVSS as a stand-in for "should I worry," and the data says that's a mistake.
Exploitation rate by EPSS band:
| EPSS score | CVEs | Exploited (KEV) | Lift vs base rate |
|---|---|---|---|
| ≥ 0.50 | 6,083 | 15.2% | 31× |
| 0.10–0.50 | 12,824 | 2.27% | 4.6× |
| 0.01–0.10 | 43,750 | 0.58% | 1.2× |
| 0.001–0.01 | 155,000 | 0.08% | 0.16× |
| < 0.001 | 89,030 | 0.02% | 0.04× |
Clean, monotonic, and steep: an EPSS ≥ 0.5 CVE is roughly 760× more likely to be exploited than an EPSS < 0.001 one.
Exploitation rate by CVSS band:
| CVSS base | CVEs | Exploited (KEV) | Lift vs base rate |
|---|---|---|---|
| 9.0–10.0 (Critical) | 38,504 | 1.43% | 2.9× |
| 7.0–9.0 (High) | 112,706 | 0.76% | 1.6× |
| 4.0–7.0 (Medium) | 141,289 | 0.14% | 0.28× |
| < 4.0 (Low) | 12,217 | 0.07% | 0.13× |
Critical CVSS gives you a 2.9× lift. EPSS ≥ 0.5 gives you 31×. That is the entire argument against CVSS-driven patching in one comparison.
And CVSS adds almost nothing on top of EPSS. Among CVEs that are both EPSS ≥ 0.5 and CVSS ≥ 9, the exploitation rate is 15.47%, barely above EPSS ≥ 0.5 alone (15.22%). Once you know the exploitation probability, the severity score is close to redundant for the "what's likely to be hit" question. (It still matters for impact, see prioritization.)
The single best filter: is there a public exploit reference?
NVD tags references on a CVE. One tag, Exploit, turns out to be the strongest single signal in the corpus, used as a filter:
- Of the 1,611 exploited (KEV) CVEs, 1,604 (99.6%) have an exploit-tagged reference.
- Among the ~246,000 CVEs without one, the exploitation rate is 0.003%, 7 CVEs total.
In other words: no public exploit reference ≈ not exploited. This shrinks the haystack from 330k to ~83k while keeping 99.6% of the real threats. It's a necessary-condition filter you can apply before anything else.
(Caveat, stated honestly: this signal is partly contemporaneous with exploitation becoming known, NVD often adds the tag as the exploit surfaces, so it's powerful for triaging the existing backlog, weaker as a pure leading indicator for a brand-new CVE. We tested whether NVD's edit history leads exploitation; it doesn't.)
Exploitation has a CWE fingerprint
Some weakness classes get weaponized far more than others. Ranked by exploitation rate (minimum 300 CVEs in class):
| CWE | Weakness | Exploited rate | Lift |
|---|---|---|---|
| CWE-843 | Type confusion | 5.78% | 11.8× |
| CWE-288 | Auth bypass via alternate path | 2.77% | 5.7× |
| CWE-502 | Deserialization of untrusted data | 2.57% | 5.3× |
| CWE-78 | OS command injection | 2.06% | 4.2× |
| CWE-306 | Missing authentication | 1.91% | 3.9× |
| CWE-416 | Use-after-free | 1.49% | 3.1× |
| CWE-94 | Code injection | 1.34% | 2.7× |
| CWE-787 | Out-of-bounds write | 1.30% | 2.7× |
Memory-corruption primitives (type confusion, UAF, OOB write) and injection/auth classes dominate. This is a usable prior: a missing-auth or deserialization bug on a reachable surface deserves more of your attention than its CVSS might suggest.
And an ecosystem fingerprint
Exploitation rate by package ecosystem (OSV data, minimum 200 CVEs):
| Ecosystem | Exploited rate |
|---|---|
| NuGet (.NET) | 1.55% |
| Maven (Java) | 0.79% |
| Packagist (PHP) | 0.39% |
| RubyGems | 0.38% |
| PyPI (Python) | 0.22% |
| npm (JavaScript) | 0.21% |
| Go | 0.20% |
| crates.io (Rust) | 0.05% |
A ~30× spread from .NET/Java to Rust. Enterprise, compiled, long-lived stacks get weaponized; memory-safe Rust sits at the bottom. Useful context when you're triaging a polyglot SBOM.
The big one: time-to-exploit has collapsed
This is the finding with the most operational weight. Median number of days from a CVE's publication to its KEV listing, by publication year:
| Publish year | Median days to KEV |
|---|---|
| 2019 | 951 |
| 2020 | 572 |
| 2021 | 203 |
| 2022 | 23 |
| 2023 | 5 |
| 2024 | 8 |
| 2025 | 7 |
| 2026 | 2 |
In 2019, you had ~2.5 years between disclosure and known exploitation. By 2023–2026, the median is a single-digit number of days. (Honest caveat: recent years are right-censored, CVEs that will be exploited later aren't in KEV yet, but the fast tail is undeniable, and 203 CVEs were KEV-listed before NVD even finished publishing them.)
The implication is blunt: a vulnerability feed that is a week stale is now structurally too slow. Freshness stopped being hygiene and became the product.
The hypothesis the data killed
It's worth showing a negative result, because it's the kind of plausible idea that ships as a feature without anyone checking it.
Hypothesis: "EPSS under-rates memory-safety bugs. A low-EPSS but memory-corruption CVE is a hidden exploited blind spot worth flagging."
Reality: Low-EPSS (< 0.1) + memory-safety CWE (OOB write, UAF, type confusion, etc.) has an exploitation rate of 0.38%, below the 0.489% base rate (0.8× lift). Memory-safety CWEs only dominate the low-EPSS-exploited set by raw count (there are simply a lot of them), not by rate. The apparent lift, when we dug in, came entirely from the exploit-tag, not the CWE class.
So "flag low-EPSS memory-corruption bugs" would have produced noise dressed up as insight. Worth knowing before building it. (This proof-first reflex, measure the premise cheaply before you build, runs through all our experiments.)
A practical signal hierarchy
Putting it together, here's the order of operations the data supports for "should I care about this CVE?":
- Filter on a public exploit reference. No exploit reference → almost certainly not exploited. (Cuts 330k → ~83k.)
- Rank by EPSS, not CVSS. EPSS ≥ 0.5 is your 31×-lift shortlist.
- Layer in KEV / SSVC "active" as ground-truth confirmation. If CISA says it's exploited, it's exploited.
- Use CWE and ecosystem as priors for the un-scored or freshly-published long tail (where EPSS hasn't warmed up).
- Weight recency hard. Given the time-to-exploit collapse, a fresh high-EPSS CVE in your stack is a this-week problem, not a this-quarter one.
- Use CVSS for impact, not likelihood. It answers "how bad if," not "how likely."
FAQ
Is EPSS better than CVSS for prioritization? For exploitation likelihood, decisively yes, about 10× better in this data (EPSS ≥ 0.5 → 15.2% exploited vs critical CVSS → 1.4%). CVSS still matters for impact. The right model uses EPSS (and KEV/SSVC) for likelihood and CVSS for severity, not CVSS for both.
Does a public proof-of-concept mean a CVE will be exploited? Not on its own, but its absence is highly predictive of non-exploitation: 99.6% of in-the-wild-exploited CVEs have an exploit-tagged reference, and CVEs without one are exploited only 0.003% of the time. Treat "has a public exploit" as a necessary-condition filter.
Which vulnerability types are most exploited? By rate: type confusion (CWE-843) leads at ~12× base rate, followed by auth-bypass (CWE-288), deserialization (CWE-502), OS command injection (CWE-78), and missing authentication (CWE-306) at ~4–6×.
How fast are vulnerabilities exploited after disclosure? The median has collapsed from ~951 days (2019 CVEs) to ~2–8 days (2023–2026 CVEs). Plan for a patch window of days, not months.
What is CISA KEV? The Known Exploited Vulnerabilities catalog, CISA's authoritative public list of CVEs confirmed exploited in the wild. It's the ground truth used throughout this analysis (~1,611 entries at time of writing).
Next: how to turn these signals into a priority ranking that beats CVSS, and whether an LLM can reason over this corpus better than a base model.