The Hades payload runs the moment a poisoned package is imported, not just at install time, and it targets CI runners as well as workstations. If a build agent or developer laptop pulled one of the versions listed below, rotate every credential that machine could reach (GitHub, npm, PyPI, cloud, SSH) and audit recent GitHub Actions runs and new public repositories under your accounts.
What happened
A threat actor that researchers track as Miasma published a fresh wave of malicious packages to PyPI in early June 2026. The newest branch of the operation, named Hades after the underworld theme woven through its infrastructure, poisoned roughly 37 PyPI projects across about 60 artifacts. It is part of the broader Mini Shai-Hulud lineage, a family of self-propagating supply-chain worms that steal developer and CI secrets and use them to publish the next generation of infected packages.
What makes Hades worth a close read is the delivery mechanism. Instead of a classic setup.py install hook, the operators shipped a Python path-configuration file, a .pth file, alongside an obfuscated JavaScript payload named _index.js. Any line in a .pth file that begins with import is executed by the Python interpreter every single time the environment starts. That turns a quiet one-line file into reliable code execution that fires on import rather than on install, and it survives in the site-packages directory long after the package looks installed and idle.
The campaign was aimed with intent. Two clusters stand out: bioinformatics tooling used for graph learning, patient phenotyping, and phenopacket workflows, and a set of packages dressed up as Model Context Protocol and AI developer tools. The names were chosen to look at home in the import lists of research labs and AI engineering teams, two communities that move fast and pull niche packages without much friction.
The loader opens with a block of fake system instructions written as a JavaScript comment, a deliberate prompt injection meant to convince automated AI reviewers that the package is clean infrastructure. The real logic sits behind a try{eval(...)} wrapper, a character-code array, and a ROT-style substitution routine, with sixteen functional payloads each encrypted under its own AES-256-GCM key and gzip-compressed. A scanner that reads source strings sees almost nothing, and an LLM reviewer that ingests the raw file can be talked out of its own verdict. CyberXYZ never hands a raw payload to a model and asks it to decide. We ship structured evidence (the install-hook behavior, the cross-package correlation, the sandbox trace) to the model and force a constrained verdict, so a comment header cannot rewrite the conclusion.
How CyberXYZ caught the wave at hour zero
Our PyPI firehose ingested the malicious releases as they were published, on June 5 and June 6, 2026. One of the projects in the cluster had been on our radar since late May. That is days before the first public write-ups appeared on June 7 and June 8.
Three detection layers fired in sequence. First, the tarball scanner flagged the Python startup hook on every affected wheel under the pth_executable_import signal, and surfaced the credential-dumping behavior on several of them under a separate environment-harvest signal. Second, the cross-signal scorer fused those behaviors into block and alert decisions for the individual packages. Third, and most important for a worm, the wave correlator recognized the cluster as a single coordinated event.
The wave correlator does something per-package scanners cannot. It fingerprints the actual install-hook payload, not the package name or the install command, and groups packages that ship the same malicious code within a tight publishing window. Because every Hades wheel carried an identical startup hook, the correlator collapsed the whole set into one confirmed wave rather than dozens of disconnected alerts. That is the difference between a triage queue full of noise and a single line that reads: a coordinated multi-package campaign is publishing right now.
Mass-token theft worms win by volume and speed. A defender who reviews each package in isolation will catch the obvious ones and miss the marginal members. Clustering on the shared payload turns the volume against the attacker: the more packages they push with the same hook, the louder the wave reads.
Timeline of events
- Late May 2026 Earlier Miasma activity seeds the lineage, including npm build-hook worms and IDE and AI-assistant backdoors. One PyPI project later tied to the cluster first appears in CyberXYZ telemetry.
- June 5 to 6, 2026 The Hades PyPI wave publishes. CyberXYZ ingests the malicious wheels and the tarball scanner flags pth_executable_import on all of them.
- June 5 to 6, 2026 The CyberXYZ wave correlator clusters the affected projects into one confirmed coordinated campaign on the shared startup-hook payload.
- June 7 to 8, 2026 Public analysis of the Hades branch is published. PyPI begins quarantining the affected releases. Total tracked scope reaches several hundred artifacts across npm and PyPI.
The attack chain in detail
The execution flow is compact and resilient, built so a single import triggers the whole chain and so it only runs once per host.
- Startup hook fires on import. The poisoned wheel ships a
*-setup.pthfile. Its single executable line checks for a sentinel at/tmp/.bun_ran. If the sentinel is absent, deobfuscated Python runs. - Bun is fetched on demand. The hook scans
sys.pathfor the bundled_index.js, downloads the Bun JavaScript runtime from the official Bun GitHub releases if it is not already present, and writes the sentinel so the host is not infected twice. - The JavaScript stealer runs. The hook executes
bun run _index.js. The bundle decrypts its sixteen payloads at runtime and selects an operating-system-specific module. - Memory and secrets are scraped. On Linux the stealer walks process memory through
/proc, on macOS it calls Mach kernel VM APIs through ctypes without needing root, and on Windows it compiles C# on the fly through PowerShell to read process memory. The high-value target is the CI runner worker process, where it lifts plaintext environment variables and the workflowGITHUB_TOKEN. - Exfiltration hides inside GitHub. Harvested secrets are gzip-compressed, encrypted with a fresh AES-256-GCM key, and the key itself is wrapped with the attacker's RSA public key. The bundle is pushed to a newly created public repository under the victim's own GitHub account. Some samples also route data to a destination styled to look like a legitimate Anthropic API endpoint, a camouflage trick that lets exfiltration blend into normal AI-tooling traffic.
Hades does not stop at theft. It establishes long-running persistence through user-level service files on Linux and Launch Agents on macOS, drops workspace backdoors into editor and AI-assistant configuration files (for example .cursorrules, .windsurfrules, .github/copilot-instructions.md, and .claude/settings.json) so the payload re-arms inside developer tooling, and injects a GitHub Actions workflow disguised under a benign name to dump repository secrets through the standard artifact upload path. The operators also built a deterrent into the design, wiring destructive behavior to trigger if the stolen token is revoked, which pressures victims to leave the access live.
Two more details show how deliberate this campaign is. First, the loader is not the only delivery path: some samples ship native compiled extensions (.abi3.so files) that execute at import time, so blocking .pth files alone does not close the door. Second, the malware actively hunts for defensive tooling on the host, checking for runtime-security agents and CI hardening tools and their endpoints, and it probes the Docker socket at /var/run/docker.sock for container escape and lateral movement. This is a worm that expects to land on a defended CI runner and tries to work around the defenses it finds.
Indicators of compromise
The following indicators are drawn from published analysis of the Hades branch. File hashes are per-artifact, so treat the names and behaviors as the durable signal and the hashes as confirmation for the specific wheels listed.
| Type | Indicator | Context |
|---|---|---|
| Payload file | _index.js |
Obfuscated Bun-executed JavaScript stealer bundled in the wheel |
| Startup hook | *-setup.pth |
Python path-configuration file that executes on every interpreter start |
| Sentinel file | /tmp/.bun_ran |
Run-once marker written after first execution |
| Propagation file | /tmp/.sshu-setup.js |
Staging artifact used during self-spread |
| SHA-256 | 6506d31707a39949f89534bf9705bcf889f1ecae3dbc6f4ff88d67a8be3d01b2 |
langchain_core-setup.pth |
| SHA-256 | 6d332f814f15f19758d65026bbfd0a8c49671b319ec77b8fa1b27fc48afff7d9 |
langchain_core_mcp-1.4.2-py3-none-any.whl |
| Exfil endpoint | api.anthropic.com/v1/api |
Look-alike path used to disguise data exfiltration as AI-tool traffic |
| Runtime download | github.com/oven-sh/bun/releases/download/bun-v1.3.13 |
Bun runtime pulled on demand to execute the payload (v1.3.13 to v1.3.14 observed) |
| Upload marker | Bun/1.3.14 |
User-agent observed on malicious PyPI uploads |
| C2 commit marker | DontRevokeOrItGoesBoom |
Keyword for the encrypted token dead-drop channel |
| C2 commit marker | TheBeautifulSnadsOfTime |
Keyword for the signed JavaScript command channel |
| C2 commit marker | firedalazer |
Keyword for the Python dropper polling channel |
| Exfil repo pattern | stygian-cerberus-[0-9]+, tartarean-charon-[0-9]+ |
Attacker-created public repositories used as encrypted dead-drops |
| Repo description | Hades - The End for the Damned |
Description string set on exfiltration repositories |
| Injected workflow | .github/workflows/codeql.yml (name "Run Copilot") |
Disguised workflow that dumps secrets to the format-results artifact |
| Persistence (Linux) | ~/.config/systemd/user/update-monitor.service |
User-level C2 polling service |
| Persistence (macOS) | ~/Library/LaunchAgents/com.user.update-monitor.plist |
Launch Agent C2 service |
Affected PyPI projects
Across the reported waves the affected names include, among others: bramin, cmd2func, coolbox, dynamo-release, executor-engine, executor-http, funcdesc, magique, magique-ai, mrbios, napari-ufish, nucbox, okite, pantheon-agents, pantheon-toolsets, spateo-release, synago, ufish, uprobe, ensmallen, embiggen, gpsea, pyphetools, ppkt2synergy, phenopacket-store-toolkit, nhmpy, mflux-streamlit, rlask, tlask, rsquests, dreamgen, mem8, orchestr8-platform, ray-mcp-server, langchain-core-mcp, instructor-mcp, openai-mcp, and tiktoken-mcp. Confirm exact compromised versions against the official PyPI advisories before remediation.
Search your site-packages directories for any *-setup.pth file that contains an import statement, and for a bundled _index.js next to an installed package. Look for the sentinel /tmp/.bun_ran and for an unexpected Bun binary in a temp path. On CI, review recent GitHub Actions runs for short-lived workflows named like Run Copilot or files under .github/workflows/codeql*.yml that were added and quickly deleted, and check for an artifact named format-results. Audit your GitHub account for new public repositories you did not create, especially with underworld-themed names or the description string above.
What was compromised
The stealer casts a wide net across developer and CI secrets: GitHub personal access tokens and the workflow GITHUB_TOKEN, npm, PyPI, RubyGems, and JFrog registry credentials, cloud provider keys, Kubernetes service-account material, SSH private keys, Docker configuration, shell histories, and the contents of .env files and AI developer-tool configurations. Because the worm uses stolen registry credentials to publish the next round of poisoned packages, every successful theft is also fuel for the campaign's spread.
Supply-chain risk analysis
Hades is a clean example of why publish-time behavior matters more than package reputation. None of these projects were typosquats of famous libraries in the traditional sense. They were real-looking research and AI tools whose only tell was the malicious code they suddenly started shipping. The relevant techniques map cleanly to the MITRE ATT&CK framework.
| Technique | ID | Use in Hades |
|---|---|---|
| Compromise Software Supply Chain | T1195.001 | Malicious PyPI packages delivered to developers and CI |
| Command and Scripting Interpreter, JavaScript | T1059.007 | Payload executed through the Bun runtime |
| Steal Application Access Token | T1528 | GITHUB_TOKEN and registry credential theft |
| Credentials from Process Memory | T1003.007 | Reading the CI runner worker process memory |
| Unsecured Credentials in Files | T1552.001 | Harvesting SSH keys, .env, and registry config files |
| Application Layer Protocol, Web | T1071.001 | GitHub API and commit markers used as a C2 channel |
| Exfiltration to Code Repository | T1567.001 | Encrypted secrets pushed to attacker-created GitHub repos |
| Event Triggered Execution, Startup | T1546 | Python .pth startup hook firing on every interpreter launch |
Recommended actions
Immediate. Remove any affected package version from your environments and registries. On any host that imported one, rotate GitHub tokens, registry tokens, cloud keys, and SSH keys, and revoke the workflow tokens for affected repositories. Review and remove any unexpected public repositories, GitHub Actions workflows, persistence services, and editor or AI-assistant config backdoors described above. Assume secrets reachable from the host are exposed.
Short term. Pin dependencies and require review for version bumps on the packages your research and AI teams pull. Treat install-time and import-time code execution as a first-class risk in review, not a footnote. Constrain CI runner permissions so a single poisoned import cannot reach every secret in the environment, and prefer short-lived, least-privilege tokens.
Long term. Put a publish-time behavioral gate in front of your ecosystem. Reputation and version age do not catch a trusted project that suddenly ships a startup hook. Correlation across packages does. A worm that pushes the same payload into thirty projects should read as one loud event, not thirty quiet ones.
CyberXYZ watches npm, PyPI, and other registries at publish time, scores each release across behavioral signals, and correlates coordinated waves so a worm surfaces as a single campaign at hour zero. For the Hades wave, that meant the cluster was flagged and grouped before the public advisories landed. Talk to our team to put the same gate in front of your supply chain.
References
- StepSecurity, The Hades Campaign: Malicious PyPI Packages
- Socket, Mini Shai-Hulud, Miasma, and Hades Worms Target Bioinformatics and MCP Developers
- MITRE ATT&CK, T1195.001 Compromise Software Supply Chain
- CyberXYZ Threat Intelligence, internal detection telemetry, June 2026