Skip to content

This project was originally designed to compared CVSS scores with EPSS data via a simple command-line interface

Notifications You must be signed in to change notification settings

ndouglas-cloudsmith/exploit-check

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

79 Commits
 
 
 
 

Repository files navigation

Exploit Check

This project was originally designed to compared CVSS scores with EPSS data via a simple command-line interface

Download the scanner and convert it to executable:

wget https://raw.githubusercontent.com/ndouglas-cloudsmith/exploit-check/refs/heads/main/exploit-check.sh
chmod +x exploit-check.sh

If you need to update the scanner (EPSS records are refreshed daily), run the below command:

./exploit-check.sh update

To query a specific CVE (for example, CVE-2021-44228), run the below command:

./exploit-check.sh query CVE-2021-44228

For a full output of KEV findings, use the --full flag

./exploit-check.sh query CVE-2021-44228 --full
Screenshot 2025-09-11 at 14 41 41

List all KEV CVEs (short form):

./exploit-check.sh list

List all KEV CVEs with details:

./exploit-check.sh list --full

Comparing CVSS scores with EPSS percentiles and check for Known Exploits

CVE ID CVSS Severity CVSS Score EPSS Percentage KEV ExploitDB OSV
CVE-2021-45786 CRITICAL 9.8 0.41%
CVE-2024-0646 HIGH 7.0 0.02%
CVE-2024-25062 HIGH 7.5 0.11%
CVE-2021-44228 CRITICAL 10.0 94.47%
CVE-2024-38285 0.08%
CVE-2017-0144 HIGH 8.8 94.42%
CVE-2024-20024 MEDIUM 6.0 0.02%
CVE-2014-0160 HIGH 7.5 94.45%
CVE-2024-9482 MEDIUM 5.1 0.03%
CVE-2017-5638 CRITICAL 9.8 94.27% N/A
CVE-2024-28085 LOW 3.3 9.83%
CVE-2024-50302 MEDIUM 5.5 0.30%
CVE-2025-47273 HIGH 8.8 0.16%
CVE-2024-6345 4.36%
CVE-2016-5195 HIGH 7.0 94.18%
CVE-2022-48477 MEDIUM 4.1 0.00%
Screenshot 2025-09-12 at 12 02 22

Filtering based on CVE or by Package Name

If you get a CVE ID from Trivy or similar tooling, you get a short-hand response of all the above data for a CVE:

./exploit-check.sh query CVE-2024-6345

If you want to get the associated package-level insights from OSV.dev, you can add the --package-info flag:

./exploit-check.sh query CVE-2024-6345 --package-info

Querying package upstreams via OSV.dev

Ecosystem Name Description Example Package Name Exploit Check Query
pypi Python packages from the Python Package Index. requests ./exploit-check.sh pkg pypi requests
npm JavaScript/TypeScript packages from the Node Package Manager registry. react ./exploit-check.sh pkg npm react
maven Java and other JVM language packages. Use the GroupId:ArtifactId format. log4j ./exploit-check.sh pkg maven log4j
rubygems Ruby packages (gems). rails ./exploit-check.sh pkg rubygems rails
pub Dart and Flutter packages from the Pub repository. http ./exploit-check.sh pkg pub http
debian Debian Linux packages. Includes different versions (eg: debian:11). openssl ./exploit-check.sh pkg debian openssl
conancenter C/C++ packages from ConanCenter. openssl ./exploit-check.sh pkg conancenter openssl
hex Elixir and Erlang packages. phoenix ./exploit-check.sh pkg hex phoenix
alpine Alpine Linux packages. Includes different versions (eg: alpine:v3.16). curl ./exploit-check.sh pkg alpine curl

You can use the pkg package command to query the packages of a specific ecosystem like PyPi:

./exploit-check.sh pkg pypi requests

Finally, run the --follow-cves flag to find mapped CVEs found in OSV aliases for these records:

./exploit-check.sh pkg pypi requests --follow-cves

Simple workflow for the project

We can scan a public-facing package to understand if it contains a vulnerability:

./exploit-check.sh pkg Alpine:v3.22 setuptools
Screenshot 2025-09-18 at 11 26 11

Users can then scan the associated CVE ID in the same tooling to understand what the realistic risk is associated with that vulnerability:

./exploit-check.sh query CVE-2024-6345 --package-info
Screenshot 2025-09-18 at 11 29 39

Examples pf Typosquatting

Using Github Search I was able to search examples of packages published with the sole purpose of typosquatting within OpenSSF's Malicious Packages project:

Type: TYPOSQUATTING

./exploit-check.sh query MAL-2023-8358 

Type: NETWORK_ACTIVITY

./exploit-check.sh query MAL-2025-5829

Type: TYPOSQUATTING

./exploit-check.sh query MAL-2025-2549

Type: TYPOSQUATTING

./exploit-check.sh query MAL-2023-8360

Github Search can also be used to filter for all instances of public-facing Python requirements.txt file that container typosquatted dependencies. This Github Search at least returned one instance of a typosquatted dependency being used in a Python application. Problem is, this process is tedious. I instead need to define a list of typosquatted package names that are already listed in OSSF Malicious Packages project so that I can query those through Github Search to prove that organisations are being impacted by either typosquatting or hallucinated LLM slopsquatting.

Malware Findings (WIP)

./exploit-check.sh pkg npm eslint-plugin-react_editor

If you want to also run query_cve on any discovered CVEs (this will not run query_cve on MAL-* items because they are not CVEs), add --follow-cves:

./exploit-check.sh pkg npm eslint-plugin-react_editor --follow-cves

An explicit example where a single advisory record contains both a CVE and a MAL alias is shown in OSV / GitHub advisory data: the advisory for [email protected] lists CVE-2025-59140 and MAL-2025-46968 as aliases (i.e., the same incident/advisory is tagged with both kinds of IDs).

./exploit-check.sh pkg npm backslash --follow-cves

Why this happens: different authorities/databases label incidents differently — some tracks mark an event as “malicious package” (MAL-...) while vulnerability databases assign CVE identifiers for the same underlying problem (or for related issues discovered in the same package/advisory). OSV/GitHub advisory database often aggregates those aliases into one record when they refer to the same incident.

GHSA-53mq-f4w3-f7qv: [email protected] contains malware after npm account takeover
CVE-2025-59140: "Bug not found, but the following aliases were: GHSA-53mq-f4w3-f7qv GHSA-m2xf-jp99-f298 MAL-2025-46968"
CVE-2021-44228: "aliases": "GHSA-jfh8-c2jp-5v3q"

Users can now query a known advistory to better understand the relationship between the mapped identifiers (CVE-, GHSA-, MAL-) to better understand the risky packages:

./exploit-check.sh query GHSA-53mq-f4w3-f7qv  --package-info

Best (direct) way — use the OSV API POST /v1/query

OSV’s API accepts a JSON body with package and (optionally) version. If you supply version OSV will return only vulnerabilities that actually match that specific version (or none if it doesn’t match).

# replace 0.2.1 and backslash with whatever you need
curl -s -X POST https://api.osv.dev/v1/query -d '{
  "package": {
    "ecosystem":"npm",
    "name":"backslash"
  },
  "version":"0.2.1"
}' -H 'Content-Type: application/json' | jq .

Best (direct) way — use the OSV API POST /v1/query

Sometimes advisories list affected ranges rather than exact enumerated versions. In that case you can ask OSV for all vulnerabilities for the package and then check whether your version is contained in any affected range (tools/logic required). For example:

# get all vuln records for the package
curl -s -X POST https://api.osv.dev/v1/query -d '{
  "package": {
    "ecosystem":"npm",
    "name":"backslash"
  }
}' -H 'Content-Type: application/json' | jq .

Look at each affected[].ranges entry (they use semver ranges or git commit ranges). You can script a semver check (node, python semver library, or use OSV’s own matching logic) to test whether 0.2.1 falls into any range. OSV docs/discussions show this approach when matching exact versions is necessary.

Correct OSV query for an exact version:

curl -s -X POST https://api.osv.dev/v1/query -d '{"package":{"ecosystem":"npm","name":"backslash"},"version":"0.2.1"}' -H 'Content-Type: application/json'  | jq
  • If that returns nothing, query without version and inspect affected[].ranges to see whether 0.2.1 falls in any range.
  • Don’t pass [email protected] as the package name — split name and version separately. I'm working on this distinction within the code.

Or query the malware finding directly:

curl -s "https://api.osv.dev/v1/vulns/MAL-2023-8358" | jq

Searching the contents of EPSS by FIRST

Check a single CVE in FIRST's EPSS API & optional compare this against the exploit checker:

./exploit-check.sh query CVE-2022-48477 --full --package-info
curl -s "https://api.first.org/data/v1/epss?cve=CVE-2022-48477" | jq

or query multiple CVEs at the same time:

curl -s "https://api.first.org/data/v1/epss?cve=CVE-2022-48477,CVE-2024-9916" | jq

In-use high & critical vulnerability scanning

Using kubectl, we can create custom-columns to understand which container images are running in our Kubernetes cluster.

kubectl get pods -A -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,IMAGES:.spec.containers[*].image'

Haven't built a case for this yet, but eventually we will need to compare capabilities against the actually CVE conditions to be exploited:

kubectl get pods -A -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,IMAGES:.spec.containers[*].image,CAPS_ADD:.spec.containers[*].securityContext.capabilities.add,CAPS_DROP:.spec.containers[*].securityContext.capabilities.drop'

The goal is to automatically pipe those images into the Trivy scan so we can understand which HIGH or CRITICAL vulnerabilities are in-use.

kubectl get pods -A -o jsonpath='{.items[*].spec.containers[*].image}' \
  | tr ' ' '\n' \
  | sort -u \
  | xargs -n1 trivy image --scanners vuln --severity HIGH,CRITICAL

A much more efficient way to find all CVEs in an active cluster namespace is with the below command:

./exploit-check.sh kubernetes namespace default --severity high,critical

Note: This works on a per-namespace basis and requires Trivy as a prerequisite to perform this automated scan.
We can now find all the in-use container images within your Kubernetes with a simple plain-text search flag:

./exploit-check.sh images namespace -A --source cloudsmith
Screenshot 2025-09-26 at 23 46 30

Software Provenance & Identifying Typosquatting

Do all packages have a signed author? In short, NO. PyPI has:
No cryptographic verification of author/maintainer in metadata.

The only “real” source of truth is the PyPI account(s) listed as maintainers (visible on the web UI, not in the JSON API).
That’s why typosquatting works: the JSON metadata is weak, and the fields can be blank or misleading.

curl https://pypi.org/pypi/fabric/json | jq '.info.name, .info.author, .info.home_page'

You might also want to track the official docs/issues associated with a software package to confirm its validity.
Pro tip: If you do this a lot, you could wrap it into a shell function like:

pypi_urls () {
  curl -s https://pypi.org/pypi/$1/json | jq -r '..|strings?|select(test("\\.com|\\.io"))' | grep --color=always -E '\.com|\.io'
}

Then you can run:

pypi_urls requests

Cleanup Script

If you want/need to delete the local data sources, and remove the Gatekeeper admission controller, you can run the below command:

rm -v -- exploitdb_exploits.csv epss_scores-current.csv epss_scores-current.csv.gz known_exploited_vulnerabilities.json && kubectl delete -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.13/deploy/gatekeeper.yaml

Those sources are pulled down again when you run the update command:

./exploit-check.sh update

Upcoming Events

Demo: Just making kubectl outputs a bit more readable:

brew install kubecolor
alias kubectl=kubecolor

Kubernetes-specific CVEs

If you want a neat list of 2025 CVEs with key info:

curl -sL https://kubernetes.io/docs/reference/issues-security/official-cve-feed/index.json \
  | jq '.items 
        | map(select(.date_published | startswith("2025-"))) 
        | sort_by(.date_published) 
        | reverse 
        | map({id, date_published, summary})'

Or for a simple table output:

curl -sL https://kubernetes.io/docs/reference/issues-security/official-cve-feed/index.json \
  | jq -r '.items 
            | map(select(.date_published | startswith("2025-"))) 
            | sort_by(.date_published) 
            | reverse[] 
            | "\(.date_published)\t\(.id)\t\(.summary)"'

About

This project was originally designed to compared CVSS scores with EPSS data via a simple command-line interface

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages