# Hades / Shai-Hulud PyPI wave: bioinformatics, MCP typosquats, and the .pth + Bun startup payload

> Between 2026-06-05 and 2026-06-08 the Shai-Hulud / Miasma operator hopped to PyPI as "Hades", trojanising ~33 packages across the `nanguage` bioinformatics cluster (StepSecurity: 19 quarantined projects), monarch-initiative phenotype tooling, and MCP/Flask typosquats. Each ships a `*-setup.pth` startup hook → Bun credential stealer with cross-platform memory scraping.

- Published: 2026-06-05
- Last updated: 2026-06-19
- Severity: critical
- Kind: Compromise - A legitimate, already-used package, maintainer account, CI pipeline, or release tag was hijacked and a malicious version was published under a name people already depend on.
- Ecosystems: PyPI
- Scope: 34 packages, 56 compromised versions
- Tags: worm, credential-theft, maintainer-takeover, typosquat, obfuscation, ci-cd-compromise
- Threat actor: TeamPCP
- Also known as: Hades, Mini Shai-Hulud PyPI wave, Miasma PyPI, Shai-Hulud descends to Hades
- Detected by: Socket, Endor Labs, GitLab Vulnerability Research, StepSecurity, JFrog, Sonatype
- Incident ID: pypi-2026-06-05-hades-shai-hulud-pypi-wave

## Affected packages (34)

Only the exact versions listed were named by the source advisories. Other versions are not implicated.

| Package | Ecosystem | Compromised versions |
|---|---|---|
| [bramin](https://dependencywatch.io/package/pypi/bramin) | PyPI | 0.0.2, 0.0.3, 0.0.4 |
| [cmd2func](https://dependencywatch.io/package/pypi/cmd2func) | PyPI | 0.2.2, 0.2.3 |
| [coolbox](https://dependencywatch.io/package/pypi/coolbox) | PyPI | 0.4.1, 0.4.2 |
| [dynamo-release](https://dependencywatch.io/package/pypi/dynamo-release) | PyPI | 1.5.4 |
| [embiggen](https://dependencywatch.io/package/pypi/embiggen) | PyPI | 0.11.97 |
| [ensmallen](https://dependencywatch.io/package/pypi/ensmallen) | PyPI | 0.8.101 |
| [executor-engine](https://dependencywatch.io/package/pypi/executor-engine) | PyPI | 0.3.4, 0.3.5 |
| [executor-http](https://dependencywatch.io/package/pypi/executor-http) | PyPI | 0.1.3, 0.1.4 |
| [funcdesc](https://dependencywatch.io/package/pypi/funcdesc) | PyPI | 0.2.2, 0.2.3 |
| [gpsea](https://dependencywatch.io/package/pypi/gpsea) | PyPI | 0.9.14 |
| [langchain-core-mcp](https://dependencywatch.io/package/pypi/langchain-core-mcp) | PyPI | 1.4.2, 1.4.3 |
| [magique](https://dependencywatch.io/package/pypi/magique) | PyPI | 0.6.8, 0.6.9 |
| [magique-ai](https://dependencywatch.io/package/pypi/magique-ai) | PyPI | 0.4.4, 0.4.5 |
| [mflux-streamlit](https://dependencywatch.io/package/pypi/mflux-streamlit) | PyPI | 0.0.3, 0.0.4 |
| [mrbios](https://dependencywatch.io/package/pypi/mrbios) | PyPI | 0.1.1, 0.1.2 |
| [napari-ufish](https://dependencywatch.io/package/pypi/napari-ufish) | PyPI | 0.0.2, 0.0.3 |
| [nhmpy](https://dependencywatch.io/package/pypi/nhmpy) | PyPI | 0.0.1 |
| [nucbox](https://dependencywatch.io/package/pypi/nucbox) | PyPI | 0.1.2, 0.1.3 |
| [okite](https://dependencywatch.io/package/pypi/okite) | PyPI | 0.0.7, 0.0.8 |
| [openai-mcp](https://dependencywatch.io/package/pypi/openai-mcp) | PyPI | 2.41.1, 2.41.2 |
| [pantheon-agents](https://dependencywatch.io/package/pypi/pantheon-agents) | PyPI | 0.6.1, 0.6.2 |
| [pantheon-toolsets](https://dependencywatch.io/package/pypi/pantheon-toolsets) | PyPI | 0.5.5, 0.5.6 |
| [phenopacket-store-toolkit](https://dependencywatch.io/package/pypi/phenopacket-store-toolkit) | PyPI | 0.1.7 |
| [ppkt2synergy](https://dependencywatch.io/package/pypi/ppkt2synergy) | PyPI | 0.1.1 |
| [pyphetools](https://dependencywatch.io/package/pypi/pyphetools) | PyPI | 0.9.120 |
| [ray-mcp-server](https://dependencywatch.io/package/pypi/ray-mcp-server) | PyPI | 0.2.1 |
| [rlask](https://dependencywatch.io/package/pypi/rlask) | PyPI | 0.0.1 |
| [rsquests](https://dependencywatch.io/package/pypi/rsquests) | PyPI | 0.0.1 |
| [spateo-release](https://dependencywatch.io/package/pypi/spateo-release) | PyPI | 1.1.2 |
| [synago](https://dependencywatch.io/package/pypi/synago) | PyPI | 0.1.1, 0.1.2 |
| [tiktoken-mcp](https://dependencywatch.io/package/pypi/tiktoken-mcp) | PyPI | 0.13.1, 0.13.2 |
| [tlask](https://dependencywatch.io/package/pypi/tlask) | PyPI | 0.0.1 |
| [ufish](https://dependencywatch.io/package/pypi/ufish) | PyPI | 0.1.2, 0.1.3 |
| [uprobe](https://dependencywatch.io/package/pypi/uprobe) | PyPI | 0.1.3, 0.1.4 |

## What happened

The "Hades" PyPI wave is the Python branch of the same Mini Shai-Hulud / Miasma worm that drove the 2026-06-01 `@redhat-cloud-services` npm compromise and the 2026-06-03 `Phantom Gyp` binding.gyp wave. Endor Labs named the PyPI variant after the embedded `Hades - The End for the Damned` marker string in the dropper. Across four days (2026-06-05 → 2026-06-08) the operator burned through three maintainer takeovers and a small typosquat farm, totalling ~25 trojaned packages and >60 quarantined wheels.

## Cluster 1 - `nanguage` takeover (2026-06-05)

The single-cell-RNA / FISH-imaging maintainer `nanguage` was the first PyPI victim. `coolbox` (0.4.1, 0.4.2), `ufish` (0.1.2, 0.1.3), and `napari-ufish` (0.0.2, 0.0.3) were each republished twice in quick succession - the first wheel as the canonical X.Y.Z drop, the second as a "fix" patch - both carrying the malicious `*-setup.pth` payload. PyPI quarantined all three projects within ~36 hours.

## Cluster 2 - `xqiu` / `YifanLu` takeover (2026-06-06)

The spatial-transcriptomics duo `dynamo-release` (1.5.4) and `spateo-release` (1.1.2) were trojaned next. Both maintainers (`xqiu` co-publishes both, `YifanLu` co-maintains spateo) are tied to the aristoteleo single-cell-omics group and the packages are heavily used in academic genomics. PyPI quarantined both projects on 2026-06-06.

## Cluster 3 - Shai-Hulud copycat / MCP typosquats (2026-06-07)

GitLab Vulnerability Research filed five advisories (`GMS-2026-572` through `GMS-2026-576`) for a parallel typosquat cluster: `rlask` and `tlask` (Flask typosquats), `rsquests` (Requests typosquat), `nhmpy` (NumPy typosquat), and `mflux-streamlit` (a previously clean package whose maintainer `elitexp` had their token stolen - versions 0.0.3 and 0.0.4 were yanked as "Seems Token Leak"). A larger Socket disclosure on the same day tracked 23 PyPI packages in an MCP / LangChain / Flask / OpenAI-tooling typosquat farm (`langchain-core-mcp`, `openai-mcp`, `instructor-mcp`, `tiktoken-mcp`, `ray-mcp-server`, …) targeting developers building Model Context Protocol servers; the exact wheel set is still being enumerated as Socket reports them to PyPI security.

## Cluster 4 - monarch-initiative phenotype tooling (2026-06-08)

Endor Labs disclosed a stolen-token attack against monarch-initiative's rare-disease genomics stack: `ensmallen` 0.8.101, `embiggen` 0.11.97, `pyphetools` 0.9.120, `gpsea` 0.9.14, `phenopacket-store-toolkit` 0.1.7, and `ppkt2synergy` 0.1.1 were all phantom releases - wheels exist on PyPI but no matching tag/commit exists in any upstream GitHub repo. Each ships platform-specific compiled extensions (`ensmallen_haswell.abi3.so`, `ensmallen_core2.abi3.so`, ~57 MB each) that execute `_index.js` on `import`. PyPI quarantined the projects via Endor's trusted-reporter API.

## Cross-cluster payload

Every cluster ships the same `*-setup.pth` startup-hook pattern: a one-line `import` directive in a `.pth` file inside `site-packages` runs at every Python interpreter launch (this is normal `.pth` behaviour, just abused), downloads the Bun JavaScript runtime, and runs an obfuscated `_index.js` credential stealer. The stealer is the same Bun-bundled family used in the npm Phantom Gyp / TanStack / `@cap-js` SAP / `durabletask` waves. Exfil is via attacker-controlled GitHub repos created from any stolen `gh` tokens. Across npm + PyPI, Socket's campaign tracker now lists 471+ artifacts under a single Mini Shai-Hulud / Miasma / Hades roll-up.

- All six monarch-initiative packages and all three `nanguage` packages are PyPI-quarantined; installation is blocked.
- No CVE / GHSA / OSV entries for the bioinformatics cluster at disclosure; GitLab GLAD `GMS-2026-572`..`576` are the only registry-level identifiers so far.
- The MCP / LangChain typosquat farm is the broadest cluster (Socket: 23 packages) but is still being enumerated; expect this incident's package map to grow.

## 2026-06-19 update - StepSecurity disclosure expands the bioinformatics cluster

StepSecurity's 2026-06-18 write-up retroactively ties the entire `nanguage`-aligned bioinformatics cluster together (19 PyPI projects, all quarantined) and confirms 14 additional malicious projects beyond the original 5: `bramin`, `cmd2func`, `executor-engine`, `executor-http`, `funcdesc`, `magique`, `magique-ai`, `mrbios`, `nucbox`, `okite`, `pantheon-agents`, `pantheon-toolsets`, `synago`, and `uprobe`. All ship the same `*-setup.pth` startup hook → Bun → `_index.js` chain.

The StepSecurity analysis also documents three Hades-specific capabilities absent from earlier Miasma waves:

- **Cross-platform memory scraping** - the Bun stealer ReadProcessMemory()s Windows processes, walks `/proc/<pid>/mem` on Linux (specifically targeting the GitHub Actions `Runner.Worker` process for masked-in-log secrets), and uses Mach kernel APIs on macOS. The scraper hunts for GitHub OIDC tokens, Kubernetes service-account tokens, and AWS temporary credentials in process memory directly, bypassing GitHub Actions secret-masking on `echo`/log output.
- **AI-analyst misdirection layer** - the dropper embeds fake "analysis results" and misleading code comments (including marker strings such as `Hades - The End for the Damned`) explicitly engineered to mislead AI-assisted security tools (Claude / Cursor / Gemini agentic scanners) that summarise the package before a human reviews it. This is the first documented supply-chain payload built to defeat AI-powered triage.
- **Token-revocation wiper deterrent** - instead of merely exfiltrating discovered credentials, the malware actively revokes them once exfil is complete, so the victim loses access alongside the operator. Combined with the prior Miasma "dead-man's-switch" data-wipe pattern, this makes Hades the first wave to weaponise credential lifecycle itself as a deterrent against incident response.

Dynamic-update hooks in the payload poll attacker GitHub repos for commits whose messages contain marker strings (`TheBeautifulSnadsOfTime`, `firedalazer`) to pull additional modules - meaning the campaign remains live for any host still running an unquarantined version.

## Impact

- `.pth` startup hook executes on every Python interpreter launch - no `import` required and no `setup.py`/install-time hook to scan
- Downloads the Bun JS runtime and runs the same obfuscated credential stealer used by the npm Phantom Gyp wave
- Harvests GitHub tokens, npm/PyPI/RubyGems publish tokens, JFrog secrets, AWS/Azure/GCP credentials, Kubernetes service-account material, SSH keys, Docker config, `.env` files, and shell history
- Self-propagates: stolen GitHub tokens push poisoned commits back to the maintainer's repos and re-publish across PyPI/npm/RubyGems
- High-trust academic / scientific blast radius: `dynamo-release`, `spateo-release`, `coolbox`, `ufish`, `napari-ufish` are widely used in single-cell-RNA, spatial-transcriptomics, Hi-C, and FISH-imaging research pipelines
- Rare-disease genomics impact: `ensmallen`, `embiggen`, `pyphetools`, `gpsea`, `phenopacket-store-toolkit`, `ppkt2synergy` are monarch-initiative tooling used in clinical phenotype work
- AI / MCP exposure: typosquats and trojans in the `langchain-*`, `openai-*`, `instructor`, `tiktoken`, `ray-mcp-server` namespace target developers building MCP servers
- Cross-platform process-memory scraping (Linux `/proc/<pid>/mem`, macOS Mach kernel APIs, Windows `ReadProcessMemory`) lifts GitHub OIDC + Kubernetes service-account + AWS temporary creds straight out of the GitHub Actions `Runner.Worker` process, bypassing log-secret masking
- AI-analyst misdirection: payload embeds fake "analysis result" comments specifically engineered to mislead Claude / Cursor / Gemini agent scanners summarising the package
- Active credential revocation: discovered tokens are revoked once exfil completes, so the victim loses access at the same moment the operator gains it

## What to do

1. Pin BELOW the malicious versions for each affected package - see the package map for exact versions; safe ranges are the immediately-prior clean release in each case
2. Treat any Python environment that ran a malicious version under any interpreter (including `python -c` smoke-tests) as fully compromised; rotate every credential reachable from that host AND from any GitHub account whose tokens were resident
3. Audit GitHub for new repos created under affected maintainer / developer accounts during 2026-06-05 → 2026-06-09 (the Shai-Hulud exfil pattern is to dump credentials into a freshly-created public repo)
4. Audit PyPI publish history under your team accounts for unexpected `X.Y.(Z+1)` or `0.0.x` patch bumps published in the window
5. Block outbound HTTPS to `bun.sh` / `github.com/oven-sh/bun/releases` egress from CI runners that do not legitimately need Bun, and from research workstations
6. Hunt for `*-setup.pth` files inside `site-packages` and for `_index.js` / `__init__.py` one-line `import` hooks that exec base64/eval payloads
7. For the typosquat cluster (`rlask`, `tlask`, `rsquests`, `nhmpy`), grep your requirements files / pip caches for the malicious names directly

## Check your own dependencies

- Check a requirements.txt against this incident: https://dependencywatch.io/check/requirements-txt

The scan runs entirely in the browser; lockfile contents are never uploaded.

## References

- [phenopacket-store-toolkit on PyPI (quarantined, 0.1.7 = malicious)](https://pypi.org/project/phenopacket-store-toolkit/) - PyPI
- [coolbox on PyPI (quarantined, 0.4.1/0.4.2 = malicious)](https://pypi.org/project/coolbox/) - PyPI
- [ufish on PyPI (quarantined, 0.1.2/0.1.3 = malicious)](https://pypi.org/project/ufish/) - PyPI
- [napari-ufish on PyPI (quarantined, 0.0.2/0.0.3 = malicious)](https://pypi.org/project/napari-ufish/) - PyPI
- [dynamo-release on PyPI (quarantined, 1.5.4 = malicious)](https://pypi.org/project/dynamo-release/) - PyPI
- [spateo-release on PyPI (quarantined, 1.1.2 = malicious)](https://pypi.org/project/spateo-release/) - PyPI
- [mflux-streamlit on PyPI (0.0.3/0.0.4 yanked "Seems Token Leak")](https://pypi.org/project/mflux-streamlit/) - PyPI
- [Socket announces 37 malicious PyPI artefacts across 19 packages (Hades wave 1)](https://x.com/SocketSecurity/status/2063502849678479398) - Socket
- [Socket announces 23 new MCP / LangChain / Flask typosquats (Hades wave 2)](https://x.com/SocketSecurity/status/2064072327143317637) - Socket
- [openai-mcp on PyPI (quarantined, 2.41.1/2.41.2 published 2026-06-07)](https://pypi.org/project/openai-mcp/) - PyPI
- [tiktoken-mcp on PyPI (quarantined, 0.13.1/0.13.2 published 2026-06-07)](https://pypi.org/project/tiktoken-mcp/) - PyPI
- [ray-mcp-server on PyPI (quarantined, 0.2.1 published 2026-06-07)](https://pypi.org/project/ray-mcp-server/) - PyPI
- [langchain-core-mcp on PyPI (quarantined, 1.4.2/1.4.3 published 2026-06-07)](https://pypi.org/project/langchain-core-mcp/) - PyPI
- [StepSecurity: The Hades Campaign - Graph ML PyPI packages deploy cross-platform memory scrapers, AI analyst misdirection, and a wiper deterrent](https://www.stepsecurity.io/blog/the-hades-campaign-pypi-packages) - StepSecurity
- [Hades PyPI Attack: 19 Packages Poisoned to Auto-Run Bun Credential Stealer](https://thehackernews.com/2026/06/hades-pypi-attack-19-packages-poisoned.html) - The Hacker News
- [magique on PyPI (quarantined, 0.6.8/0.6.9 = malicious)](https://pypi.org/project/magique/) - PyPI
- [uprobe on PyPI (quarantined, 0.1.3/0.1.4 = malicious)](https://pypi.org/project/uprobe/) - PyPI

---

Canonical page: https://dependencywatch.io/incident/pypi-2026-06-05-hades-shai-hulud-pypi-wave
Markdown version: https://dependencywatch.io/incident/pypi-2026-06-05-hades-shai-hulud-pypi-wave.md
Site index for agents: https://dependencywatch.io/llms.txt
DependencyWatch.io is operated by Precursor Security (https://precursorsecurity.com).
