Prioritization
How to check your scan results against CISA KEV from the command line
Find which CVEs in a Nessus, OpenVAS, Qualys or any other scan export are on CISA's Known Exploited Vulnerabilities list, then rank the rest by EPSS, with grep, comm, curl and jq.
By Brandon Sooknanan, Founder, Varaxon Technologies · · 5 min read
A scan report can list hundreds of CVEs. CISA's Known Exploited Vulnerabilities catalog tells you which of them attackers are actually using, and FIRST's EPSS score estimates how likely the rest are to be exploited soon. This guide shows how to check your own scan export against both, step by step. You need curl and jq 1.6 or later. The other commands come with Linux and macOS.
We tested every command on 6 November 2026 against the live KEV feed and a sample Nessus CSV export. The example output below comes from that sample.
1. Pull the CVE IDs out of your export
This works on a Nessus CSV or .nessus file, an OpenVAS or Greenbone XML report, a Qualys or Rapid7 CSV, or a JSON export. It doesn't need to understand the format. It just finds every string that looks like a CVE ID.
| What it does | Command |
|---|---|
| Save every unique CVE ID to scan-cves.txt | grep -ohE 'CVE-[0-9]{4}-[0-9]{4,}' scan-export.csv | LC_ALL=C sort -u > scan-cves.txt |
| Count them | wc -l < scan-cves.txt |
To check several exports at once, list them all after grep, or use *.csv. LC_ALL=C makes every sort in this guide use the same order, which comm needs in step 3.
2. Download the KEV catalog
| What it does | Command |
|---|---|
| Save the catalog as kev.json | curl -s -o kev.json https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json |
| Show the catalog version and size | jq -r '.catalogVersion, .count' kev.json |
| Save every KEV CVE ID to kev-cves.txt | jq -r '.vulnerabilities[].cveID' kev.json | LC_ALL=C sort -u > kev-cves.txt |
On 6 November 2026 the catalog was version 2026.11.04 with 1,768 entries. CISA adds entries several times a week, so download a fresh copy each time you check.
3. Find the CVEs that are on KEV
comm compares two sorted lists. With -12 it prints only the lines found in both, which here means the CVEs in your scan that are known to be exploited.
| What it does | Command |
|---|---|
| Save the matches to on-kev.txt | LC_ALL=C comm -12 scan-cves.txt kev-cves.txt > on-kev.txt |
| Show KEV details: CVE, vendor, product, date added, due date, ransomware use | jq -r --rawfile ids on-kev.txt '($ids | split("\n") | map(select(length > 0))) as $l | .vulnerabilities[] | select(.cveID | IN($l[])) | [.cveID, .vendorProject, .product, .dateAdded, .dueDate, .knownRansomwareCampaignUse] | @tsv' kev.json |
Our sample export had 6 unique CVEs, and 3 of them were on KEV: CVE-2024-3400 (Palo Alto PAN-OS), CVE-2021-44228 (Log4j) and CVE-2023-4863 (Chromium WebP). The first two are marked Known for ransomware use. The output is tab-separated, so it pastes straight into a spreadsheet.
4. See which hosts have them
| What it does | Command |
|---|---|
| Every row in the export that mentions a KEV CVE | grep -F -f on-kev.txt scan-export.csv |
| Just CVE and host, using Nessus's default CSV columns | grep -F -f on-kev.txt scan-export.csv | cut -d, -f2,5 | LC_ALL=C sort -u |
In a default Nessus CSV, the CVE is column 2 and the host is column 5. If you changed the columns, or your scanner uses a different layout, check the header row and change the numbers to match. In our sample, Log4j showed up on two hosts and the other two KEV CVEs on one host each.
5. Rank everything by EPSS
FIRST's EPSS API takes up to 100 CVEs per request and needs no key. The commands below split your list into groups of 100, so they work for a scan of any size.
| What it does | Command |
|---|---|
| Split the list into files of 100 | split -l 100 scan-cves.txt chunk_ |
| Fetch scores and list them: CVE, EPSS, percentile | for f in chunk_*; do curl -s "https://api.first.org/data/v1/epss?cve=$(paste -sd, "$f")&limit=100"; done | jq -rs '[.[].data[]] | sort_by(-(.epss | tonumber)) | .[] | [.cve, .epss, .percentile] | @tsv' |
| List the CVEs that are not on KEV, to work through next | LC_ALL=C comm -23 scan-cves.txt kev-cves.txt |
Scores change daily, so note the date. In our sample, the three KEV entries all scored above 0.99. Of the rest, the SSH Terrapin issue (CVE-2023-48795) scored 0.94, well above the two OpenSSL CVEs at 0.04 and 0.02. That puts Terrapin next in line, even though its CVSS score was the lowest of the three.
Putting it to use
- Treat KEV's
dueDateas your target for anything internet-facing. US federal agencies get anywhere from a few days to three weeks, depending on the entry. - Re-run steps 2 and 3 when CISA adds entries. A CVE that was safe to defer last week can be on KEV today.
- Keep the TSV output with the scan. A dated list of exploited CVEs and the hosts that had them is useful evidence for audits and client reviews.
- Findings with no CVE, such as weak TLS settings or default credentials, won't appear here. Review those in the report itself.
For a single CVE, the free CVE checker shows its KEV status, EPSS and CVSS without a terminal. To follow what CISA adds each month, see how to track KEV additions with curl and jq. We built Varaxon Scan Hub to run these KEV and EPSS checks on every finding when you upload a scan export, and to put the exploited ones at the top of the client's fix plan.
Common questions
Does this work with OpenVAS, Qualys or Rapid7 exports?
Yes. Step 1 finds CVE IDs as text, so any CSV, XML or JSON export that includes them works. Only the column numbers in step 4 depend on the scanner.
Why does comm say a file is not in sorted order?
The two lists were sorted with different locale settings. Re-create both with LC_ALL=C sort -u, as shown above, and run comm with LC_ALL=C too.
Why does jq say IN or --rawfile is not defined?
Your jq is older than 1.6. Install a current version from your package manager or the jq releases page.
Turn your next scan export into a client-ready report.
Run a free pre-flight check