Data & Insight

Shai-Hulud Hit 19 PyPI Packages. CVE Scanners Saw Nothing.

RobertUpdated Jun 10, 20265 min read
Dark terminal-style feature image. White text reads: Shai-Hulud hit 19 PyPI packages. CVE scanners saw nothing. Below it in teal monospace: supply_chain.compromised: true. Attestd branding bottom left.

Shai-Hulud Hit 19 PyPI Packages. CVE Scanners Saw Nothing.#

On June 6, attackers compromised 37 wheel artifacts across 19 PyPI packages in the latest wave of the Shai-Hulud supply chain campaign. The affected packages include real, widely used bioinformatics tools: dynamo-release, coolbox, and ufish have cumulative download counts in the hundreds of thousands.

Every compromised version returns risk_state: "none". No CVEs. No CISA KEV entries. A CVE-only check would have passed all of them.


What the attack did#

Socket's research team identified the campaign on June 7. The malicious wheels shipped a *-setup.pth file alongside an obfuscated JavaScript payload named _index.js. Python's site module processes .pth files at interpreter startup, not at import time. That means the payload executes the next time Python starts — whether that is a test run, a notebook kernel, a CI job, or a pip command.

The loader bootstraps the Bun JavaScript runtime from GitHub, then runs _index.js. Once running, the payload targets a broad set of developer and CI/CD credentials: GitHub tokens, PyPI and npm publishing tokens, AWS, GCP, Azure, Kubernetes, and Vault credentials, SSH keys, Docker configs, shell histories, and Claude and MCP configuration files.

Exfiltration routes include automatically created GitHub repositories using Hades-themed markers, and a secondary HTTPS path pointed at a legitimate but non-existent Anthropic API endpoint, assessed by Socket as network-log camouflage rather than a live exfiltration channel.

Persistence is established via systemd services on Linux and LaunchAgents on macOS.


What the Attestd API returns#

bash
curl https://api.attestd.io/v1/check/coolbox/0.4.1 \
  -H "X-API-Key: $ATTESTD_API_KEY"
json
{
  "product": "coolbox",
  "version": "0.4.1",
  "risk_state": "none",
  "actively_exploited": false,
  "supply_chain": {
    "compromised": true,
    "sources": ["osv"],
    "malware_type": "malware",
    "description": "Malicious code in coolbox (PyPI)",
    "compromised_at": "2026-06-06T06:13:57Z",
    "removed_at": null
  }
}
bash
curl https://api.attestd.io/v1/check/ufish/0.1.2 \
  -H "X-API-Key: $ATTESTD_API_KEY"
json
{
  "product": "ufish",
  "version": "0.1.2",
  "risk_state": "none",
  "actively_exploited": false,
  "supply_chain": {
    "compromised": true,
    "sources": ["osv"],
    "malware_type": "malware",
    "description": "Malicious code in ufish (PyPI)",
    "compromised_at": "2026-06-06T06:13:57Z",
    "removed_at": null
  }
}
bash
curl https://api.attestd.io/v1/check/dynamo-release/1.5.4 \
  -H "X-API-Key: $ATTESTD_API_KEY"
json
{
  "product": "dynamo-release",
  "version": "1.5.4",
  "risk_state": "none",
  "actively_exploited": false,
  "supply_chain": {
    "compromised": true,
    "sources": ["osv"],
    "malware_type": "malware",
    "description": "Malicious code in dynamo-release (PyPI)",
    "compromised_at": "2026-06-06T06:13:57Z",
    "removed_at": null
  }
}

For contrast, a clean package from the same ecosystem:

bash
curl https://api.attestd.io/v1/check/langchain/0.3.0 \
  -H "X-API-Key: $ATTESTD_API_KEY"
json
{
  "product": "langchain",
  "version": "0.3.0",
  "risk_state": "none",
  "actively_exploited": false,
  "supply_chain": {
    "compromised": false,
    "sources": [],
    "malware_type": null,
    "description": null
  }
}

risk_state: "none" across all four. The supply chain field is the only signal that separates the malicious packages from the clean one.


Why CVE checks miss this class of attack#

CVE identifiers require a filing, a review, and publication. Supply chain compromises are operational: an attacker publishes a malicious version, it sits in the registry, and developers install it before any advisory exists.

The Shai-Hulud campaign has been running across npm and PyPI since September 2025. At the time Socket published its report, 448 artifacts were affected across 106 npm packages and 19 PyPI packages. None of them carry CVE identifiers. All of them would pass a CVE-only gate.

This is not an edge case. It is the default state for any active supply chain campaign during the window between publication and advisory. That window is measured in hours to days, which is exactly when install-time execution is doing its work.

The .pth execution vector is particularly difficult to catch via audit tooling because the malicious code does not run on import. It runs at interpreter startup. A developer who installs a compromised package, then starts a new Python process for an unrelated task, triggers the payload. The connection between the install and the execution is not obvious.


Two signals, one API call#

The Attestd response for a PyPI or npm package returns two independent signals: risk_state derived from NVD and CISA KEV data, and a supply_chain object derived from OSV and registry compromise data. They are independent. A package can be clean on one axis and compromised on the other.

For this campaign, every affected package is clean on the CVE axis and compromised on the supply chain axis. An agent or pipeline that gates only on risk_state passes the attack through. One that gates on both catches it.

python
result = client.check("coolbox", "0.4.1")
 
if result.risk_state in ("critical", "high"):
    raise SecurityError("CVE risk too high")
 
if result.supply_chain and result.supply_chain.compromised:
    raise SecurityError("Supply chain compromise detected")

Both checks are required. Neither is sufficient alone.


Immediate response if you installed affected versions#

Socket's full IOC list covers all 37 malicious artifacts. If any affected version was installed on a developer machine or CI runner, treat all credentials accessible from that environment as compromised and rotate immediately.

Priority order for rotation: GitHub personal access tokens, Actions secrets, and deploy keys; PyPI, npm, and other package publishing tokens; AWS, GCP, Azure, Kubernetes, and Vault credentials; SSH keys, Docker credentials, and cloud CLI profiles; Anthropic and Claude and MCP configuration tokens.

Search for the filesystem indicators Socket documented: /tmp/.bun_ran, _index.js inside site-packages, and *-setup.pth files in unexpected locations. Check GitHub organizations for repositories with the description "Hades - The End for the Damned" and artifacts named format-results.

Full IOC list and affected artifact inventory: socket.dev/supply-chain-attacks/miasma-mini-shai-hulud-supply-chain-attack


Coverage#

Attestd covers coolbox, ufish, and dynamo-release in the PyPI supply chain watchlist. Supply chain signals update on a 6-hour cycle from OSV and registry sources.

Get an API key at api.attestd.io/portal/login. Free tier, 1,000 calls a month, no credit card required.