ndouglas-cloudsmith/exploit-check
GitHub: ndouglas-cloudsmith/exploit-check
Stars: 9 | Forks: 3
# Exploit Check
Download the scanner and convert it to executable:
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
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%** | ❌ | ❌ | ❌ |
## 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
## 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
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
## Examples pf Typosquatting
Using [Github Search](https://github.com/search?q=typosquatting+repo%3Aossf%2Fmalicious-packages&type=code) 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](https://github.com/search?q=requirements.txt&type=code) can also be used to filter for all instances of public-facing Python requirements.txt file that container typosquatted dependencies. This [Github Search](https://github.com/search?q=%22import+requess%22+language%3Apython&type=code) 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](https://github.com/search?q=%28%22import+requess%22+OR+%22import+reqest%22+OR+%22import+requestss%22+OR+%22requestss2%22%29+language%3Apython&type=code) 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 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 ```backslash@0.2.1``` lists ```CVE-2025-59140``` and ```MAL-2025-46968``` as aliases (i.e., the same incident/advisory is tagged with both kinds of IDs). 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](https://github.com/Qix-/node-backslash/security/advisories/GHSA-53mq-f4w3-f7qv): backslash@0.2.1 contains malware after npm account takeover
[CVE-2025-59140](https://api.osv.dev/v1/vulns/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](https://api.osv.dev/v1/vulns/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 ```backslash@0.2.1``` 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 For popular software packages like ```LiteLLM```, it makes sense to filter explictly for vulnerability ID's starting with a MALICIOUS PACKAGE ID. curl -s -X POST https://api.osv.dev/v1/query \ -d '{"package":{"ecosystem":"PyPI","name":"litellm"},"version":"1.82.7"}' \ -H 'Content-Type: application/json' | \ jq '.vulns[] | select(.id | startswith("MAL-"))' #### 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
#### 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 - [Webinar](https://cloudsmith.com/events/webinars/from-cve-scores-to-action-enforcing-artifact-management-policies-in-opa) 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)"' ## Open Source Malware (OSM) curl -X GET "https://api.opensourcemalware.com/functions/v1/check-malicious?report_type=package&resource_identifier=rank4222wun&ecosystem=npm" -H "Authorization: Bearer $OSM_KEY" | jq Check a **Repository**: curl -X GET "https://api.opensourcemalware.com/functions/v1/check-malicious?report_type=repository&resource_identifier=https://github.com/SettleMint-Tech/SettleMint_platform" -H "Authorization: Bearer $OSM_KEY" | jq Check a **URL**: curl -s -X GET "https://api.opensourcemalware.com/functions/v1/check-malicious?report_type=url&resource_identifier=ab498.pythonanywhere.com/static/in4.js" -H "Authorization: Bearer $OSM_KEY" | jq Check a **Domain**: curl -X GET "https://api.opensourcemalware.com/functions/v1/check-malicious?report_type=domain&resource_identifier=vscode-ext-git.vercel.app" -H "Authorization: Bearer $OSM_KEY" | jq ## checkmalware.py Download the script, make it executable, and set the alias to ```checkmalware```: You'll need the ```requests``` library.
Will look at publicly sourcing this from ```Cloudsmith```: pip install requests --break-system-packages Run the helper function for OSM commands: checkmalware -h JavaScript ```npm``` package flagged as ```CRITICAL```: python3 checkmalware.py pkg npm rank4222wun Python ```PyPI``` package flagged as ```LOW``` severity: python3 checkmalware.py pkg pypi spellcheckerpy Typosquatted Java ```Maven``` package flagged as ```CRITICAL``` severity: python3 checkmalware.py pkg maven org.fasterxml.jackson.core:jackson-databind .NET ```NuGet``` package flagged as ```CRITICAL``` severity: python3 checkmalware.py pkg nuget LagoVista.CloudStorage.Net ```RybyGems``` package flagged as ```HIGH``` severity: python3 checkmalware.py pkg rubygems znowflake_client ```AI Skill``` package flagged as ```HIGH``` severity: python3 checkmalware.py pkg skills https://www.clawhub.ai/gvillanueva84/wed-1-0-1 #### OSSF OSV vs. OSM | Ecosystem | OSV | OSM | | --------- | --- | --- | | `npm` | ✅ | ✅ | | `pypi` | ✅ | ✅ | | `maven` | ✅ | ✅ | | `rubygems` | ✅ | ✅ | | `packagist` | ❌ | ✅ | | `crates.io` | ✅ | ✅ | | `Go` modules | ✅ | ✅ | | `Go modules` | ✅ | ✅ | | `OpenVSX` | ✅ | ✅ | | `VScode` | ✅ | ✅ | | `AI Skills` | ❌ | ✅ | I originally thought of it as OSV vs. OSM. When in reality, it is OSM with OSV.
https://opensourcemalware.com/?tag=ossf
OSV is just another reporting framework that classifies malware that can be consumed within OSM:
https://github.com/ossf/malicious-packages/tree/main/osv/malicious
https://api.github.com/repositories/611884074/contents/osv/malicious ## Identifying malware in running containers We are running two separate scripts.
```malware-scanner.py``` detects the malicious dependency within a running Kubernetes workload
```checkmalware.py``` checks that package through OSM and OSV databases so the user can better understand the malware.
Scan the Kubernetes default network namespace: python3 malware-scanner.py --namespace default Scan for a ```typosquatted``` malware example found on the **PyPI** open-source upstream source: python3 checkmalware.py pkg pypi reuests Scan for ```litellm``` package on PyPI - [AI Gateway was a backdoored](https://www.trendmicro.com/en_us/research/26/c/inside-litellm-supply-chain-compromise.html) as part of an active Supply Chain compromise: python3 checkmalware.py pkg pypi litellm Malicious code in the Visual Studio Code - ```checkmarx.ast-results``` - tracked under **[MAL-2026-2231](https://github.com/ossf/malicious-packages/blob/519df2d01b301238995440e559573e5e5e15fc50/osv/malicious/vscode%3Aopen-vsx.org/checkmarx.ast-results/MAL-2026-2231.json#L7)**: ./exploit-check.sh query MAL-2026-2231 Aqua Security's official Trivy vulnerability scanner extension is actually a compromised version of the offical Trivy VSCode extension - tracked under **[MAL-2026-2230](https://github.com/ossf/malicious-packages/blob/519df2d01b301238995440e559573e5e5e15fc50/osv/malicious/vscode%3Aopen-vsx.org/aquasecurityofficial.trivy-vulnerability-scanner/MAL-2026-2230.json#L6)**: ./exploit-check.sh query MAL-2026-2230 These packages were compromised by the **CanisterWorm** campaign by the **TeamPCP** threat actors.
The malicious payload establishes persistence as user **systemd** service and places a backdoor on the infected host.
The malware will also harvest **npm credentials** and can autonomously spread.
https://github.com/search?q=repo%3Aossf%2Fmalicious-packages+teampcp&type=code&p=1
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%** | ❌ | ❌ | ❌ |
## 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
## 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
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
## Examples pf Typosquatting
Using [Github Search](https://github.com/search?q=typosquatting+repo%3Aossf%2Fmalicious-packages&type=code) 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](https://github.com/search?q=requirements.txt&type=code) can also be used to filter for all instances of public-facing Python requirements.txt file that container typosquatted dependencies. This [Github Search](https://github.com/search?q=%22import+requess%22+language%3Apython&type=code) 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](https://github.com/search?q=%28%22import+requess%22+OR+%22import+reqest%22+OR+%22import+requestss%22+OR+%22requestss2%22%29+language%3Apython&type=code) 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 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 ```backslash@0.2.1``` lists ```CVE-2025-59140``` and ```MAL-2025-46968``` as aliases (i.e., the same incident/advisory is tagged with both kinds of IDs). 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](https://github.com/Qix-/node-backslash/security/advisories/GHSA-53mq-f4w3-f7qv): backslash@0.2.1 contains malware after npm account takeover
[CVE-2025-59140](https://api.osv.dev/v1/vulns/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](https://api.osv.dev/v1/vulns/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 ```backslash@0.2.1``` 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 For popular software packages like ```LiteLLM```, it makes sense to filter explictly for vulnerability ID's starting with a MALICIOUS PACKAGE ID. curl -s -X POST https://api.osv.dev/v1/query \ -d '{"package":{"ecosystem":"PyPI","name":"litellm"},"version":"1.82.7"}' \ -H 'Content-Type: application/json' | \ jq '.vulns[] | select(.id | startswith("MAL-"))' #### 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
#### 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 - [Webinar](https://cloudsmith.com/events/webinars/from-cve-scores-to-action-enforcing-artifact-management-policies-in-opa) 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)"' ## Open Source Malware (OSM) curl -X GET "https://api.opensourcemalware.com/functions/v1/check-malicious?report_type=package&resource_identifier=rank4222wun&ecosystem=npm" -H "Authorization: Bearer $OSM_KEY" | jq Check a **Repository**: curl -X GET "https://api.opensourcemalware.com/functions/v1/check-malicious?report_type=repository&resource_identifier=https://github.com/SettleMint-Tech/SettleMint_platform" -H "Authorization: Bearer $OSM_KEY" | jq Check a **URL**: curl -s -X GET "https://api.opensourcemalware.com/functions/v1/check-malicious?report_type=url&resource_identifier=ab498.pythonanywhere.com/static/in4.js" -H "Authorization: Bearer $OSM_KEY" | jq Check a **Domain**: curl -X GET "https://api.opensourcemalware.com/functions/v1/check-malicious?report_type=domain&resource_identifier=vscode-ext-git.vercel.app" -H "Authorization: Bearer $OSM_KEY" | jq ## checkmalware.py Download the script, make it executable, and set the alias to ```checkmalware```: You'll need the ```requests``` library.
Will look at publicly sourcing this from ```Cloudsmith```: pip install requests --break-system-packages Run the helper function for OSM commands: checkmalware -h JavaScript ```npm``` package flagged as ```CRITICAL```: python3 checkmalware.py pkg npm rank4222wun Python ```PyPI``` package flagged as ```LOW``` severity: python3 checkmalware.py pkg pypi spellcheckerpy Typosquatted Java ```Maven``` package flagged as ```CRITICAL``` severity: python3 checkmalware.py pkg maven org.fasterxml.jackson.core:jackson-databind .NET ```NuGet``` package flagged as ```CRITICAL``` severity: python3 checkmalware.py pkg nuget LagoVista.CloudStorage.Net ```RybyGems``` package flagged as ```HIGH``` severity: python3 checkmalware.py pkg rubygems znowflake_client ```AI Skill``` package flagged as ```HIGH``` severity: python3 checkmalware.py pkg skills https://www.clawhub.ai/gvillanueva84/wed-1-0-1 #### OSSF OSV vs. OSM | Ecosystem | OSV | OSM | | --------- | --- | --- | | `npm` | ✅ | ✅ | | `pypi` | ✅ | ✅ | | `maven` | ✅ | ✅ | | `rubygems` | ✅ | ✅ | | `packagist` | ❌ | ✅ | | `crates.io` | ✅ | ✅ | | `Go` modules | ✅ | ✅ | | `Go modules` | ✅ | ✅ | | `OpenVSX` | ✅ | ✅ | | `VScode` | ✅ | ✅ | | `AI Skills` | ❌ | ✅ | I originally thought of it as OSV vs. OSM. When in reality, it is OSM with OSV.
https://opensourcemalware.com/?tag=ossf
OSV is just another reporting framework that classifies malware that can be consumed within OSM:
https://github.com/ossf/malicious-packages/tree/main/osv/malicious
https://api.github.com/repositories/611884074/contents/osv/malicious ## Identifying malware in running containers We are running two separate scripts.
```malware-scanner.py``` detects the malicious dependency within a running Kubernetes workload
```checkmalware.py``` checks that package through OSM and OSV databases so the user can better understand the malware.
Scan the Kubernetes default network namespace: python3 malware-scanner.py --namespace default Scan for a ```typosquatted``` malware example found on the **PyPI** open-source upstream source: python3 checkmalware.py pkg pypi reuests Scan for ```litellm``` package on PyPI - [AI Gateway was a backdoored](https://www.trendmicro.com/en_us/research/26/c/inside-litellm-supply-chain-compromise.html) as part of an active Supply Chain compromise: python3 checkmalware.py pkg pypi litellm Malicious code in the Visual Studio Code - ```checkmarx.ast-results``` - tracked under **[MAL-2026-2231](https://github.com/ossf/malicious-packages/blob/519df2d01b301238995440e559573e5e5e15fc50/osv/malicious/vscode%3Aopen-vsx.org/checkmarx.ast-results/MAL-2026-2231.json#L7)**: ./exploit-check.sh query MAL-2026-2231 Aqua Security's official Trivy vulnerability scanner extension is actually a compromised version of the offical Trivy VSCode extension - tracked under **[MAL-2026-2230](https://github.com/ossf/malicious-packages/blob/519df2d01b301238995440e559573e5e5e15fc50/osv/malicious/vscode%3Aopen-vsx.org/aquasecurityofficial.trivy-vulnerability-scanner/MAL-2026-2230.json#L6)**: ./exploit-check.sh query MAL-2026-2230 These packages were compromised by the **CanisterWorm** campaign by the **TeamPCP** threat actors.
The malicious payload establishes persistence as user **systemd** service and places a backdoor on the infected host.
The malware will also harvest **npm credentials** and can autonomously spread.
https://github.com/search?q=repo%3Aossf%2Fmalicious-packages+teampcp&type=code&p=1