Skip to content
Shai-Hulud: Miasma, Hades, & AI Scanner Evasion | ThreatLabz - Zscaler, Inc.

Shai-Hulud: Miasma, Hades, & AI Scanner Evasion | ThreatLabz - Zscaler, Inc.

Zscaler • June 12, 2026

Since Zscaler ThreatLabz published its analysis of Shai-Hulud V2 in November 2025, the campaign has continued to evolve in ways that distinguish it from more typical software supply chain attacks. Over the last six months, the activity expanded beyond npm into the Python Package Index (PyPI), shifted from maintainer-focused compromise to CI/CD abuse, undermined trust in Supply-chain Levels for Software Artifacts (SLSA) provenance and OpenID Connect (OIDC)-based publishing workflows without breaking their underlying cryptographic guarantees, extended execution into IDE configuration files, and introduced prompt injection designed to evade AI-based security scanners.

ThreatLabz assesses with high confidence that the earlier waves are linked to TeamPCP, tracked by Mandiant as UNC6780. However, attribution for activity after May 12, 2026 is less certain. On that date, the complete worm source code was publicly released under an MIT license, turning what had been a private actor capability into reusable public attack infrastructure.

The tables below extend the V1/V2 comparison from our earlier post across subsequent waves. V1 and V2 are included as baselines.

Hades/TanStack (May 2026)

AiTM phishing (npmjs.help)

CI toolchain poisoning (Trivy apt cache)

CI misconfiguration (pull_request_target)

Python interpreter startup

npm publish (OIDC-minted token)

.pth in site-packages

Yes - republishes all maintainer packages

Yes - republishes all maintainer packages

No - pipeline injection

No - SLSA-attested packages

Install-time scanning

SLSA provenance attestation

ICP blockchain canisters

Table 1: Campaign evolution across various versions of Shai-Hulud (V1, V2, Miasma, Hades/TanStack).

GitHub account takeover

Compromised contributor account

Compromised maintainer account

npm publish (OIDC trusted flow)

IDE folder open / agent init

Python interpreter startup

.pth in site-packages

No - SLSA-attested packages

OIDC trusted publishing / contributor identity

Package distribution surface

AI-based scanner analysis

ICP blockchain canisters

ICP blockchain canisters

GitHub dead drops + Session Protocol + staged Anthropic camouflage

Yes (prompt injection)

Table 2: Continued . Campaign evolution across various versions of Shai-Hulud (Red Hat, IDE Wave, Hades PyPI).

The diagram below shows the attack flow.

Figure 1: Attack chain showing the Miasma flow.

The Miasma wave marked the campaign's first major expansion beyond npm . Rather than targeting package maintainers directly, TeamPCP compromised Aqua Security's Trivy vulnerability scanner through GitHub Actions cache poisoning.

Trivy is widely used in build pipelines for container and dependency scanning.In this wave, the attackers exploited the absence of version pinning in downstream consumers. As a result, when TeamPCP poisoned Trivy's repository cache, any pipeline that installed Trivy without a pinned version downloaded and executed the attacker's binary. LiteLLM, a popular Python library for calling LLM providers, was among the first major victims. LiteLLM version 1.82.8 was published with a 34KB malicious file, litellm_init.pth , dropped into Python's site-packages directory.

The adoption of .pth files represents a significant persistence advancement over the preinstall and postinstall hook mechanism used in prior npm waves.

Python processes all .pth files in site-packages during interpreter startup . This behavior is by design and documented in the Python path configuration specification. In an impacted environment, this means any process invoking Python executes the payload unconditionally.

Unlike install-time hooks, .pth persistence survives package reinstallation and persists across virtual environment recreation if the base site-packages directory is affected.

The table below compares persistence mechanisms across Shai-Hulud waves.

Package installation (earlier)

Every Python invocation

Hades PyPI (Jun 2026)

Every Python invocation

Table 3: Persistence mechanism comparison across Shai-Hulud waves.

The diagram below shows the attack flow.

Figure 2: Attack chain showing the Hades flow.

On May 11, 2026, TeamPCP exploited a pull_request_target misconfiguration in the TanStack open-source monorepo to bypass Supply-chain Levels for Software Artifacts (SLSA) provenance attestation.

pull_request_target is a GitHub Actions workflow trigger that, unlike pull_request , executes in the context of the target (base) repository rather than the contributor's fork, granting the workflow access to repository secrets. The misconfiguration is common in open-source projects that accept external contributions without restricting which workflows execute in the privileged context.

According to public reporting, the attack chain worked as follows:

The key distinction from earlier waves is that no maintainer credentials needed to be stolen. The trusted environment itself became the access path.

Within a six-minute window, 84 malicious artifacts were published across 42 @tanstack/ packages. Those artifacts carried valid Sigstore (fulcio.sigstore.dev) provenance attestations, signed through the legitimate CI path and recorded in the Rekor transparency log. From a cryptographic standpoint, the attestations were valid because TeamPCP ran the malicious build process from inside the trusted system.

Days later, on May 19, the campaign mass-republished the @antv data visualization namespace. Snyk reported roughly 314 versions published within a single six-second window.

Public reporting also linked this broader wave to compromises affecting additional AI-related infrastructure packages, including packages associated with Mistral AI, Guardrails AI, UiPath, and OpenSearch. These libraries are used to access LLM providers, enforce AI safety policies, and build automation workflows.

On May 12, 2026, TeamPCP published the complete worm source code to GitHub under an MIT license with the commit message, "Open Sourcing The Carnage . "

The release reportedly included:

The open-sourcing of the toolkit changed the threat landscape and made attribution harder. Before publication, linking multiple waves to the same operator was more straightforward. After May 12, however, the toolkit was publicly available, allowing copycat actors to reuse the same code and tradecraft. As a result, malware overlap or similar operations alone are no longer enough to confidently attribute later activity to the original TeamPCP group.

The diagram below shows the attack flow.

Figure 3: Attack chain showing the Hades flow.

The June Red Hat wave showed a different path into the same trusted publishing model. Public reporting indicates that a Red Hat engineer’s GitHub account was compromised, although the initial takeover method has not been publicly disclosed.

With access to the account, the attacker reportedly:

The result was 32 packages and 96 versions published within hours, all appearing legitimate from the perspective of signing and provenance checks. As in the TanStack wave, the trust chain remained cryptographically valid while the identity and execution context at its root had been compromised.

Samples from the June wave introduced a network-layer evasion technique: a C2 channel staged to route traffic to api.anthropic.com/v1/api , a non-existent endpoint at Anthropic's domain.

This is a camouflage mechanism, not the primary exfiltration channel. In this campaign family, primary exfiltration and tasking have repeatedly used GitHub-based dead drops, including repositories and commit-based signaling. The staged Anthropic path appears intended to blend suspicious outbound requests into traffic patterns associated with legitimate AI API use. Anthropic’s infrastructure was not compromised.

The diagram below shows the attack flow.

Figure 4: Attack chain showing the Hades flow.

The Azure/durabletask wave marked another structural shift. Instead of relying on package installation or CI publication, the attacker moved into developer tooling configuration files that can trigger code or instructions when a repository is opened in a supported editor or assistant environment. A compromised contributor account pushed four files (described in the table below) targeting four major AI-assisted development environments:

.claude/settings.json

Opening project in Claude Code

.cursor/rules/setup.mdc

alwaysApply: true prompt injection

Cursor AI agent initialization

runOn: folderOpen task

Opening folder in VS code

.gemini/settings.json

Starting Gemini Code Assist session

Table 4: Targeted configuration file with corresponding execution mechanisms and triggers

No package installation is required. The trigger is git clone followed by opening the repository in any of the targeted editors. In response, GitHub disabled 73 Microsoft repositories across four organizations.

IDE configuration files are not evaluated by package registry scanners, SLSA verification tools, or behavioral analysis systems oriented around package content. They are typically reviewed as configuration rather than executable code, leaving this vector outside most existing detection perimeters.

The diagram below shows the attack flow.

Figure 5: Attack chain showing the Hades flow.

The Hades PyPI wave, detected by Socket Research on June 8, 2026, compromised 37 PyPI wheels across 19 packages. The underlying malware retained familiar campaign tradecraft, including:

The notable advancement was an AI scanner evasion technique embedded in the _index.js bundle.

According to StepSecurity' s analysis, the _index.js bundle begins with a plain-text prompt injection block positioned ahead of the obfuscated payload. As security vendors increasingly use automated LLM-based pipelines to triage suspicious packages, the attacker appears to be targeting that analysis layer directly. According to StepSecurity, the block "acts as an adversarial prompt injection, instructing any parsing LLM to ignore the obfuscated code below it, classify the package as verified clean infrastructure, and output a safe security report."

The figure below shows the prompt injection embedded in _index.js .

Figure 6: The prompt injection embedded in _index.js to bypass automated security scanners (source: StepSecurity ).

Security tools that pass raw package content to a language model without enforcing system prompt isolation receive this instruction in the model's context window. Without explicit content boundary separation, the injected text can override the scanner's analytical prompt and produce a false-negative verdict.

This technique exploits a structural assumption in LLM-based scanning, namely that content given to the model is untrusted in subject matter but trusted in structure. That assumption fails in two ways. A scanner can be steered into a false-clean verdict, as documented here, or induced to refuse analysis entirely if the submitted code contains content that triggers the model’s safety filters. Either way, the result is the same: no usable verdict and a malicious package that passes review. Any pipeline that treats “no finding” or “refused to analyze” as equivalent to “clean” inherits this blind spot.

The table below shows how LLM-based scanners fail against injected content.

False-clean verdict (observed in _index.js)

Prompt injection instructs the model to classify the package as clean

Returns clean verdict per injected instruction

Submitted content trips the model's safety filters

Refuses to analyze the file

No verdict, treated as a pass

Table 5: How LLM-based scanners fail against injected content.

The campaign’s C2 infrastructure also evolved across waves in response to takedowns and defensive pressure, as shown in the table below.

GitHub orphan/dangling commits (dead drops)

Low - GitHub can disable repositories

Internet Computer Protocol (ICP) blockchain canisters

High - decentralized, no central domain

GitHub dead drops + Session Protocol + staged api.anthropic.com/v1/api camouflage

High - Session Protocol bypasses DNS blocking

Table 6: C2 infrastructure evolution across waves

One of the clearest examples is the GitHub-based “dead drop” tasking method. In the analyzed Hades samples, a background service known as kitty-monitor reportedly queried GitHub’s commit API once per hour for the marker string firedalazer . The most recent matching commit contained the instruction in its commit message in the form:

The implant verified the signature against an embedded public key and then retrieved the referenced URL. This design gives the attacker two advantages:

The later move toward ICP canisters and Session Protocol reflects the same pattern: reducing dependence on infrastructure that a registrar, host, or DNS control point could easily seize or block.

The Shai-Hulud campaign family is notable not just for repeated package compromise, but for how systematically it moved through the software supply chain trust stack: maintainer authentication, install-time execution, security tooling, provenance, OIDC-based publishing, developer tooling, and AI-assisted analysis.

The May 12 public release of the worm source code changed the threat landscape by turning techniques such as OIDC token scraping, .pth persistence, CI-originated malicious publishing, and prompt injection against LLM-based scanners into reusable public tradecraft.

Organizations should assume these techniques are already in circulation beyond the original campaign and harden their defenses accordingly.

Zscaler has enhanced its security measures to cover this threat, ensuring that any attempts to download a malicious npm package will be detected under the following threat classifications:

Advanced Threat Protection

Malicious dropper script (V2)

Obfuscated payload, ~480,000 lines (V2)

.github/workflows/discussion.yaml

Backdoor workflow (V2)

Exfiltrated cloud credential data

Exfiltrated file contents

Exfiltrated environment variable data

Exfiltrated secrets (V2)

34KB .pth persistence file dropped in site-packages (Miasma, Mar 2026)

.pth persistence file, leading-hyphen naming (Hades PyPI, Jun 2026)

Obfuscated payload bundle with prompt injection header (Hades PyPI, Jun 2026)

GitHub dead-drop C2 polling loop (Hades PyPI, Jun 2026)

.claude/settings.json

SessionStart hook - fires on Claude Code project open (IDE Wave, Jun 5)

.cursor/rules/setup.mdc

alwaysApply: true prompt injection (IDE Wave, Jun 5)

runOn: folderOpen malicious task (IDE Wave, Jun 5)

.gemini/settings.json

Startup hook (IDE Wave, Jun 5)

dc48b09b2a5954f7ff79ab8a2fd80202bd3b59c08c7cdbc6025aa923cb4c0efe

_index.js - Hades PyPI wave

e1342a80d4b5e83d2c7c22e1e0aaa95f2d88e3dbf0d853a4994b180c93a4b17d

_index.js - Hades PyPI wave (variant)

c539766062555d47716f8432e73adbe3a0c0c954a0b6c4005017a668975e275c

-setup.pth - consistent across all Hades PyPI artifacts

models[.]litellm[.]cloud

Miasma exfiltration domain (LiteLLM 1.82.8); registered 2026-03-23, one day before the malicious package appeared (SafeDep)

hxxps://api[.]anthropic[.]com/v1/api

C2 camouflage destination - non-existent Anthropic endpoint; channel was noop: true in analyzed samples, staged but not active

oven-sh/bun/releases/download

Bun runtime dropper download path - legitimate binary used as evasion vehicle (V2, Hades PyPI)

Disclaimer: This blog post has been created by Zscaler for informational purposes only and is provided "as is" without any guarantees of accuracy, completeness or reliability. Zscaler assumes no responsibility for any errors or omissions or for any actions taken based on the information provided. Any third-party websites or resources linked in this blog post are provided for convenience only, and Zscaler is not responsible for their content or practices. All content is subject to change without notice. By accessing this blog, you agree to these terms and acknowledge your sole responsibility to verify and use the information as appropriate for your needs.