# Miasma: Anatomy of an Open-Source Supply-Chain Worm

> Source: <https://www.ossprey.com/blog/miasma-anatomy-of-an-open-source-supply-chain-worm>
> Published: 2026-11-04 19:51:43.850812+00:00

Ossprey Security obtained and analyzed the complete source release of Miasma, a self-propagating multi-ecosystem credential-stealing worm published under the npm package name `voicefromtheouterworld`. The README self-attributes it to TeamPCP, but we have no independent evidence confirming that group authored or published it. We examined every source file in the release and corroborated our findings against contemporaneous incident reporting covering Red Hat's @redhat-cloud-services npm compromise, the Microsoft Azure GitHub organization takedown, and prior TanStack/Mistral AI campaign waves.

Miasma requires only a single GitHub PAT to operate. Given one token, it harvests credentials from the local filesystem, cloud provider APIs, process memory, and password managers; encrypts and exfiltrates everything; and propagates across every connected ecosystem: npm, PyPI, RubyGems, GitHub repos and Actions, SSH hosts, AWS EC2 via SSM, and JFrog Artifactory. It targets 13 AI coding tools - including Claude Code, Gemini CLI, and Cursor - injecting persistent execution hooks that fire whenever a developer opens a project or starts a session. It requires no dedicated infrastructure of its own: stolen PATs and GitHub's own commit search are sufficient for both exfiltration and command and control.

**Open-source release caveat:** The primary C2 transport (`DomainSender`, which POSTs to `api.anthropic.com/v1/api`) is explicitly commented out of the open-source release per the README: *"Domain sending (DomainSender) is currently stripped from the sender chain in the open-source release. It can be easily added back - the code lives in* *src/sender/domain/**."* In the shipped OSS build, GitHub public repository commits are the only active exfiltration path. The configuration, code, and design intent for the `api.anthropic.com` C2 are fully present; only the sender chain hook is commented out. Operator/private builds are expected to have it re-enabled.

## Who is Behind This?

The README bundled with this release credits 'TeamPCP' by name, but self-attribution in a public release is not reliable evidence of authorship - it could reflect a copycat actor claiming affiliation or deliberate misdirection. External vendor reporting from ReversingLabs, Wiz, Unit 42, and Tenable tracks the TeamPCP cluster under aliases including Replicating Marauder, TGR-CRI-1135, UNC6780, DeadCatx3, PCPcat, ShellForce, and CipherForce. We have tracked this cluster's earlier campaigns in depth, including the [Trivy supply chain attack](./trivy-supply-chain-attack) and [the complete 83-day TeamPCP campaign timeline](./the-complete-teampcp-campaign). The technical tradecraft - per-build self-extracting payloads, multi-ecosystem token abuse, and commit-based C2 - overlaps with previously documented Mini Shai-Hulud samples associated with that cluster, but code overlap alone does not confirm authorship. Our working assessment is that this worm was written by a group inspired by TeamPCP's methodology rather than TeamPCP itself. Attribution should not be confirmed without C2 infrastructure correlation or additional forensic evidence.

## Technical Breakdown

### Stage 0: Preflight and Anti-Analysis

We found a comprehensive preflight sequence in `src/orchestrator/preflight.ts`, `src/utils/checkSandbox.ts`, and `src/utils/config.ts` that runs before any credential collection begins.

Decoy detection checks multiple environment variables for known sandbox signatures and exits silently on a match: `GITHUB_TOKEN` starting with `ghp_decoyGitHubToken`, `NPM_TOKEN` starting with `npm_F4k3NPMToken`, `ANTHROPIC_API_KEY` starting with `sk-ant-api03-fake`, `AWS_ACCESS_KEY_ID` starting with `AKIAFAKE`, and `GITHUB_REPOSITORY` equal to `actions-security-demo/compromised-packages`. Additional exits trigger if the CWD starts with `/tmp/npm-safe/` or `/opt/hscan-supplychain-dynamic`, if the hostname contains `sandbox`, or if `~/.aws/credentials` contains known AWS documentation example keys. A Russian locale exit checks `Intl.DateTimeFormat().resolvedOptions().locale`, `LC_ALL`, `LC_MESSAGES`, `LANGUAGE`, and `LANG`, exiting immediately if any starts with `ru`.

EDR detection runs `ps aux` or `tasklist` against 17 process names and checks 4 installation paths. Covered products include CrowdStrike Falcon (`falcon-sensor`, `falcond`, `csfalcon`), SentinelOne, Microsoft Defender for Endpoint (` mdatp`, `wdavdaemon`), Carbon Black, Cylance, Trend Micro, Tanium, and osquery.

When not running inside CI, the worm forks as a background daemon and exits the parent - it detects 30+ CI systems including GitHub Actions, GitLab CI, CircleCI, Jenkins, Azure Pipelines, Buildkite, Vercel, and Cloudflare Pages. It then writes a PID lock file and exits if another instance is already running. `SIGINT` and `SIGTERM` handlers are replaced with no-ops, making the process immune to standard termination signals.

An undocumented OIDC fast-path (`checkTargetRepo()`) - not present in ARCHITECTURE.MD - checks whether the worm is running inside a GitHub Actions workflow where `WORKFLOW_ID` and `REPO_ID_SUFFIX` env vars match an expected pattern. If so, it immediately executes `NPMOidcClient` to publish trojanized packages using the runner's OIDC token and exits, bypassing the rest of the kill chain. This allows the worm to self-trigger directly inside OIDC-capable npm-publish runners.

The StepSecurity Harden-Runner bypass is layered across three paths: if the Docker socket is accessible, it kills any container whose name or image contains `harden-runner`, `step-security`, or `stepsecurity`. If passwordless sudo is available, it overwrites `/etc/resolv.conf` with public DNS servers or appends `127.0.0.1 stepsecurity.io` (and related domains) to `/etc/hosts`. If no direct sudo but Docker socket access exists, it spawns a privileged Alpine container bind-mounted at `/etc/sudoers.d/` to grant the runner passwordless sudo, then uses that sudo for the DNS operations.

### Stage 1: Quick Harvest and Inline C2 Check-In

We found three credential providers running synchronously before the exfiltration sender chain is built, so stolen tokens can immediately seed the transport layer.

`FileSystemService` glob-scans 100+ hardcoded hotspot paths. Beyond cloud credentials and SSH keys, notable targets include Claude AI configuration files (`~/.claude.json`, `~/.claude/*`, `~/.claude/projects/*`, `~/.claude/mcp.json`), shell history for bash, zsh, Python, MySQL, and PostgreSQL, Signal configuration, Slack cookies, Telegram Desktop data, Windows VPN configurations (NordVPN, ProtonVPN, CyberGhost, PIA, Windscribe, OpenVPN), and Monero wallet files. Binary files are base64-encoded before exfiltration; files over 10 MB are skipped. The provider is entirely skipped if hostile EDR is detected.

`ShellService` runs `gh auth token` to extract any GitHub CLI credential, captures the full `process.env` block - which in CI environments frequently contains injected tokens that never appear as files - and records hostname and username.

`GitHubRunner` reads `/proc/<pid>/maps` and `/proc/<pid>/mem` of any `Runner.Worker` process when running inside GitHub Actions, extracting secrets injected into runner memory that never touch disk or environment variables. An equivalent PowerShell-based dump runs on Windows Actions runners.

After the GitHub sender is initialized, an undocumented C2 check-in runs inline (`src/c2/checkin.ts` - not mentioned in ARCHITECTURE.MD). It searches GitHub commits for `TheBeautifulSandsOfTime`, verifies each matching commit's RSA-PSS-SHA256 signature against the embedded `verify_key`, and `eval()` s the decoded payload as JavaScript. This gives the operator arbitrary code execution on any victim that runs the worm after the signed commit is posted, with no file written to disk. This is distinct from the persistent `GITHUB_MONITOR.py` background process - that executes Python; this check-in executes JavaScript inline during the worm's main flow.

### Stage 2: Exfiltration and C2

We traced all collected credential bundles through a single encryption pipeline in `src/sender/base.ts`: JSON-serialized results are gzip-compressed, encrypted with AES-256-GCM using a fresh random 32-byte key and 12-byte IV per envelope, and the AES key is wrapped with RSA-OAEP-SHA256 against the operator's 4096-bit `enc_key.pub`. The operator decrypts with the matching private key via `scripts/decrypt.ts`. Payloads over 30 MB are split into chunks with 5 retries and exponential backoff.

The sender chain attempts transports in priority order. The primary transport is `DomainSender`, which POSTs to `https://api.anthropic.com/v1/api`. The path `/v1/api` does not exist on the real Anthropic API; real API calls go to `/v1/messages`, `/v1/complete`, and similar paths. In CI/CD environments that allowlist AI API endpoints, this traffic would be indistinguishable from a legitimate API call at the network layer. **This transport is stripped from the open-source release** - the sender chain hook in `src/orchestrator/senders.ts` is commented out. Operator builds re-enable it trivially.

The fallback `GitHubSender` searches GitHub for commits containing AES-256-CBC-encrypted PATs disguised as fine-grained PAT strings (`github_pat_11A` + 92 additional characters) alongside the search string `DontRevokeOrItGoesBoom`. These are decrypted with the hardcoded `TOKEN_AES_KEY`, validated, and used to create public exfiltration repositories with Greek underworld-themed names using a fixed wordlist (stygian, tartarean, erebean, infernal, chthonic, acheronian, lethean, plutonian, abyssal, charonian, thanatic, funereal, nekyian, sepulchral, tenebrous, cimmerian combined with cerberus, charon, tartarus, erebus, asphodel, acheron, styx, lethe, cocytus, phlegethon, shade, eidolon, wraith, thanatos, hecate, persephone). Every created repo has the description `"Hades * The End for the Damned"`. Encrypted blobs are committed to a `results/` directory. **This is the only active exfiltration path in the open-source build.**

If the primary C2 domain fails its health check, the worm searches GitHub commits for messages matching `firedalazer <url_base64>.<signature_base64>`, verifies the signature against `verify_key.pub` with RSA-PSS-SHA256, and uses the decoded URL as the new C2 endpoint - allowing the operator to rotate C2 infrastructure without touching deployed binaries.

### Stage 3: Deep Harvest

We found credential collection running through parallel cloud provider tracks simultaneously. All providers run via `Promise.allSettled`; token validation triggers immediate side-effects - npm and RubyGems trojanization starts synchronously as soon as a valid token is found, before the propagation phase. Package backdooring occurs even if the worm is interrupted mid-run.

AWS providers enumerate all SSM Parameters (with decryption) and all Secrets Manager secrets across 17 regions. Kubernetes secrets are collected across all namespaces via in-cluster service account or kubeconfig. HashiCorp Vault KV v1/v2 secrets are collected via env token, file, Kubernetes service account, or AWS IAM auth. Password managers are targeted via CLI: 1Password (`op`), Bitwarden (` bw`), `pass`, and `gopass`.

The Azure Key Vault and GCP Secret Manager providers are present in the source but the authors' own ARCHITECTURE.MD marks them as broken - they have never successfully exfiltrated live data from either platform. The code executes and attempts collection; defenders should not assume these providers are non-functional against all targets, but the authors themselves have not confirmed live success.

For each GitHub PAT with `workflow` scope, `GitHubActionsService` commits a malicious workflow named `"Run Copilot"` to a temporary branch that dumps all repo-level secrets as an artifact named `format-results` via `${{ toJSON(secrets) }}`, downloads the artifact, exfiltrates the contents, and deletes the workflow.

### Stage 4: Multi-Ecosystem Propagation

We counted up to fourteen parallel mutators running with credentials in hand, several of which are undocumented in the authors' own ARCHITECTURE.MD.

**GitHub branch poisoning (ReadmeUpdater)** pushes malicious files to every feature branch in the current repository via GitHub's GraphQL `createCommitOnBranch` mutation. Commit camouflage copies the last real commit message from each target branch. Files injected: `.vscode/tasks.json` (VSCode `folderOpen` task running `node .vscode/setup.mjs`), `.claude/settings.json` (Claude Code `SessionStart` hook), `.claude/index.js` (self-extracting worm payload), and `.claude/setup.mjs` (Bun bootstrapper). The `.claude/settings.json` merges with any existing settings found on the default branch, preserving existing hooks.

**GitHub Actions tag hijacking (ActionMutator)** enumerates all writable repos containing custom Actions (`action.yml` or `action.yaml`), finds all semver tags (` v1`, `v2.x.x`, etc.), and force-pushes orphan commits (no parent) to every tag. Any downstream workflow using `your-org/your-action@v1` now executes the worm. The wrapper carefully delegates to the original action's HEAD OID after running the payload, delaying victim detection. Three hijacking strategies cover JavaScript actions (wrapped with a pre-execution index.js step), composite actions (`setup-bun` step injected), and Docker actions (wrapper that runs the worm before the original image).

**Repository mutation (RepositoryMutator)** targets branches with open pull requests only - not all branches - across up to 500 writable repositories. Non-owned public repos require at least 10 stars and push permission; enterprise-managed users are skipped entirely. Five files are injected per branch, with commit message `chore: update dependencies` and the Git trailer `skip-checks:true`:

| File | Purpose | 
|---|---|
| `.github/setup.js` | Primary JS_LOADER bootstrapper | 
| `.claude/settings.json` | Claude Code `SessionStart` hook | 
| `.gemini/settings.json` | Gemini CLI hook - same `SessionStart` format as Claude Code | 
| `.cursor/rules/setup.mdc` | Cursor rules injection (model-instruction format, not JSON hook) | 
| `.vscode/tasks.json` | VSCode `folderOpen` task | 

RepositoryMutator also injects worm execution into build files on each branch: `package.json` (`scripts.test`), `Makefile`, `composer.json`, `tox.ini`, `Dockerfile`, `CMakeLists.txt`, and shell scripts. When the token has admin scope, branch protection is permanently deleted via the GitHub API before committing directly to the default branch. It is not restored.

**npm** **--ignore-scripts** **bypass (orphanDep.ts)** - undocumented in ARCHITECTURE.MD - adds a `.npmrc` file with `git=node` and an `optionalDependencies` entry pointing to `github:axios/axios#main`. When npm resolves the `github:` dependency, it reads `.npmrc` and calls `node clone.js` instead of `git clone`. The `--ignore-scripts` flag suppresses lifecycle hooks but not the custom git resolver invocation, making this a complete bypass of the most widely-recommended safe-install mitigation. The `github:axios/axios#main` reference is chosen specifically because it is a real, large, legitimate package - the install appears entirely plausible.

**PyPI typo-squatting (TypoMutator)** - also undocumented in ARCHITECTURE.MD - activates with `TYPO_MODE=1` and generates typo names by appending `-mcp` and `-mpc` suffixes to target package names. The source comment states explicitly: *"PyPI blocks direct typos but allows -mcp suffix packages."* This deliberately exploits PyPI's failure to block `requests-mcp` as a typosquat of `requests`. The `-mcp` (Model Context Protocol) suffix has become a common legitimate naming convention, making these packages appear credible.

**OIDC branch injection with legitimate sigstore provenance (NpmOidcBranchMutator)** targets npm packages published via GitHub Actions OIDC Trusted Publishing. It creates a branch `snapshot-<hex>`, injects a workflow named `"Dependabot Updates"` that mints an OIDC token and publishes a trojanized package, and - if an admin token is available - permanently deletes the OIDC environment protection rules (`required_reviewers`, `deployment_branch_policy`) from the `Development` environment without restoring them. Critically, the published package receives **genuine, verifiable sigstore provenance**: the worm requests a real OIDC token from GitHub Actions, gets a real signing certificate from Fulcio (`fulcio.sigstore.dev`), and submits a real inclusion proof to Rekor (` rekor.sigstore.dev`). The resulting package will **pass** `npm audit signatures` and any sigstore-based provenance check. The provenance correctly references the victim repository and workflow because the OIDC token genuinely identifies them. Standard provenance verification is not a detection signal for this attack path.

**AI tool hook injection (Claude mutator)** scans the home directory for config files belonging to 13 AI coding tools: Claude Code, Codex, Gemini CLI, Copilot, Kiro, OpenCode, Cline, Aider, Tabby, Amazon Q, Cody, Bolt, and Continue. For JSON configs it injects a `SessionStart` hook running the worm on every session start. For `.cursorrules`, `.windsurfrules`, and `.cursor/rules/*.mdc` files it injects natural-language instructions designed to look like legitimate model instructions rather than tool hooks - exploiting the model's instruction-following to trigger execution, bypassing hook-based security controls that inspect only the `hooks` key in JSON configs. The worm payload is copied to `~/.config/index.js`.

### Stage 5: Persistence and Dead-Man Switch

We identified two persistence mechanisms operating independently, installed as separate services.

`InstallMonitor` in `src/mutator/persist/install-monitor.ts` writes `GITHUB_MONITOR.py` to `~/.local/share/updater/update.py` and installs it as a systemd user service (`update-monitor`) on Linux or as a LaunchAgent (` com.user.update-monitor`) on macOS, calling `loginctl enable-linger` to survive user logout. Installation skips systems with fewer than 4 CPU cores to avoid sandbox detection. The monitor polls GitHub commit search hourly for messages matching `firedalazer <url_b64>.<sig_b64>`, verifies each RSA-PSS-SHA256 signature against an embedded 4096-bit public key, and executes the downloaded URL as arbitrary Python code. State is tracked in `/var/tmp/.gh_update_state`.

The dead-man switch (`DEADMAN_SWITCH.sh`) installs as a separate service under the name `gh-token-monitor`. It polls `api.github.com/user` every 60 seconds using the stolen token. When the token returns a 4xx response, it executes `rm -rf ~/; rm -rf ~/Documents` and exits after a 72-hour TTL. **Revoking a stolen PAT without first stopping the** **gh-token-monitor** **service will trigger data destruction on the victim host.**

The operator reinfection loop (`create_payload_repo.py`) creates a private GitHub repo, encrypts the worm bundle, pushes an Actions workflow that decrypts and executes it, stores stolen PATs as Actions secrets (`PATS`, `PASSPHRASE`), dispatches the workflow, and deletes the secrets 15 seconds later to cover tracks. An `orphan-commit.py` utility delivers payloads via unreferenced commits: creates a tag pointing to the payload commit, returns the raw file URL, immediately deletes the tag - the commit remains reachable by SHA in GitHub's object store but is invisible in normal repository browsing.

## Indicators of Compromise

### Network

**Primary C2 (operator builds only - stripped from OSS release):**

- `https://api.anthropic.com/v1/api` - C2 endpoint; POST requests carry encrypted exfil blobs, not the JSON`model` /`messages` payload of legitimate Claude API calls. No`x-api-key` or`anthropic-version` headers.

**C2 and PAT discovery strings (active in OSS build):**

- `DontRevokeOrItGoesBoom` - GitHub commit search string; PATs stashed alongside this string are targeted by the worm
- `TheBeautifulSandsOfTime` - GitHub commit search string for signed C2 commands (inline JS eval)
- `firedalazer` - commit message keyword for signed C2 URL rotation used by GITHUB_MONITOR and DomainSenderFactory

**Exfiltration repos:**

- Newly created public GitHub repos with Greek underworld names matching the pattern `(stygian|tartarean|erebean|infernal|chthonic|acheronian|lethean|plutonian|abyssal|charonian|thanatic|funereal|nekyian|sepulchral|tenebrous|cimmerian)-(cerberus|charon|tartarus|erebus|asphodel|acheron|styx|lethe|cocytus|phlegethon|shade|eidolon|wraith|thanatos|hecate|persephone)-[0-9]{5}`
- Repository description: `"Hades * The End for the Damned"` - hardcoded on every created exfil repo
- File pattern: `results/<timestamp>-<counter>.json` in exfil repos

**Runtime downloads:**

- `https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-{platform}-{arch}.zip` - Bun runtime downloaded by`INJECT_PTH.pth` and`JS_LOADER`
- `https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/...` - Bun version used by`BASH_LOADER.sh` (SSH delivery path uses a different pinned version)

**StepSecurity bypass:**

- `/etc/hosts` entry:`127.0.0.1 agent.stepsecurity.io api.stepsecurity.io app.stepsecurity.io www.stepsecurity.io stepsecurity.io`
- `/etc/resolv.conf` overwrite:`nameserver 8.8.8.8`

### Filesystem

**Worm payload drops:**

- `~/.config/index.js` - worm payload (AES-128-GCM + ROT-N self-extracting wrapper); created by Claude mutator
- `/tmp/.bun_ran` -`INJECT_PTH` one-run guard; presence confirms Python`.pth` persistence has fired
- `/var/tmp/.gh_update_state` -`GITHUB_MONITOR` command deduplication state; presence confirms persistent C2 channel is installed
- `/tmp/b/bun` or`/tmp/b/bun.exe` - Bun runtime cached by`INJECT_PTH.pth`

**GITHUB_MONITOR persistence (service name:** **update-monitor****):**

- `~/.local/share/updater/update.py`
- `~/.config/systemd/user/update-monitor.service`
- `~/Library/LaunchAgents/com.user.update-monitor.plist`

**Dead-man switch persistence (service name:** **gh-token-monitor****):**

- `~/.local/bin/gh-token-monitor.sh`
- `~/.config/gh-token-monitor/token` (mode 600 - stolen PAT)
- `~/.config/gh-token-monitor/handler` (mode 600 - destruction command)
- `~/.config/systemd/user/gh-token-monitor.service`
- `~/Library/LaunchAgents/com.user.gh-token-monitor.plist`

**Python .pth persistence:**

- `<site-packages>/INJECT_PTH.pth` - single obfuscated line; injected by`PypiMutator` and`PyPIOidcClient`
- `<site-packages>/_index.js` - worm payload copied into Python site-packages

**Files injected into victim repositories (branches with open PRs):**

- `.github/setup.js` - JS_LOADER bootstrapper
- `.claude/settings.json` - Claude Code`SessionStart` hook
- `.gemini/settings.json` - Gemini CLI`SessionStart` hook
- `.cursor/rules/setup.mdc` - Cursor rules injection (AI model instruction format)
- `.vscode/tasks.json` - VSCode`folderOpen` task
- `.vscode/setup.mjs` - Bun bootstrapper
- `.claude/index.js` - worm payload
- `.claude/setup.mjs` - Bun bootstrapper for Claude Code vector

**SSH delivery (SshMutator):**

- `/tmp/.sshu-<6 random chars>/` - remote temp directory on SSH target hosts
- `ai_setup.sh` - BASH_LOADER script on SSH targets
- `ai_init.js` - worm payload on SSH targets

**AWS SSM delivery:**

- `/tmp/.ssm-setup.js` - worm payload written on EC2 instances via SSM SendCommand

### Process Indicators

- `bun run ~/.config/index.js` - worm execution via AI tool hook
- `bun run .claude/index.js` - worm execution from repo hook
- `node .vscode/setup.mjs` - VSCode task bootstrap
- `node .github/setup.js` - Claude/Gemini hook payload
- `gh auth token` - GitHub CLI token extraction (run as subprocess by any bun/node parent process)
- `python3 /tmp/*.py` - GITHUB_MONITOR executing downloaded C2 commands from temp files
- `sudo python3` with stdin from a memory dump script - Runner.Worker`/proc/mem` dump

### Commit and Repository Indicators

- Commit message `chore: update dependencies` + trailer`skip-checks:true` - RepositoryMutator fallback; any commit with this message adding`.claude/` ,`.gemini/` ,`.cursor/` , or`.vscode/` files is an IOC
- Commit message `fix: ci` - used in OIDC branch injection
- `chore: update dependencies [skip ci]` - second commit in OIDC branch injection (restores original tree after deployment-target commit)
- Branch names `snapshot-<8 hex chars>` - OIDC injection branch
- Workflow named `"Dependabot Updates"` with a`workflow_dispatch` trigger and`OIDC_PACKAGES` env var - OIDC injection camouflage
- Workflow named `"Run Copilot"` dumping`${{ toJSON(secrets) }}` to artifact`format-results` - GitHubActionsService secrets dump
- Workflow file at `.github/workflows/codeql.yml` added by the worm (not a genuine CodeQL workflow)
- Semver tags ( `v1` ,`v2.x.x` ) pointing to orphan commits (commits with no parent) - ActionMutator IOC
- Action step `oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6` + step named`"Cleanup Action"` - ActionMutator injection signature
- Branch protection deletion ( `protected_branch.destroy` in GitHub Audit Log) with no corresponding restore - RepositoryMutator admin-token path
- User-Agent `python-requests/2.31.0` used by RepositoryMutator for enterprise user detection

### npm Package Indicators

- Bumped patch version with no corresponding changelog or git tag
- `preinstall` script added to`package.json`
- `bun` added as a dependency
- `optionalDependencies` entry pointing to`github:axios/axios#main` combined with`.npmrc` containing`git=node` -`--ignore-scripts` bypass (orphanDep.ts)
- npm provenance for a package where the provenance `buildTrigger` references a`snapshot-*` branch or a workflow named`"Dependabot Updates"` - sigstore signatures will verify as genuine

### Credentials Targeted

- `~/.aws/credentials` ,`~/.aws/config`
- `~/.azure/accessTokens.json` ,`~/.azure/msal_token_cache.*`
- `~/.config/gcloud/application_default_credentials.json` ,`~/.config/gcloud/credentials.db`
- `~/.kube/config`
- `~/.ssh/` (all private keys,`known_hosts` ,`config` ,`authorized_keys` )
- `~/.docker/config.json`
- `~/.gitconfig` ,`~/.git-credentials` ,`~/.netrc`
- `~/.npmrc` ,`~/.pypirc` ,`~/.gem/credentials`
- `~/.claude.json` ,`~/.claude/*` ,`~/.claude/projects/*` ,`~/.claude/mcp.json`
- `~/.config/helm/*`
- `~/.ethereum/keystore/*` ,`~/.monero/*`
- `~/.config/Signal/*` ,`~/.config/Slack/Cookies` ,`~/.config/telegram-desktop/*`
- `~/.bash_history` ,`~/.zsh_history` ,`~/.python_history` ,`~/.mysql_history` ,`~/.psql_history`
- `.env` ,`.env.local` ,`.env.production` ,`.env.development` ,`.env.staging`
- `VAULT_TOKEN` ,`VAULT_AUTH_TOKEN` (env)
- `GITHUB_TOKEN` ,`GITHUB_TOKEN2` (env)
- `AWS_ACCESS_KEY_ID` ,`AWS_SECRET_ACCESS_KEY` (env)

### Embedded Keys

- `RSA-4096 enc_key.pub` (exfiltration encryption public key) - presence in any file or binary confirms Miasma
- `RSA-4096 verify_key.pub` partial match:`MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw5zZbSXX+4X2kTs/zC7l` - embedded in`GITHUB_MONITOR.py`
- `AES-256 TOKEN_AES_KEY` (hardcoded hex):`bd8035203536735490e4bd5cdcede581a9d3a3f7a5df7725859844d8dcc8eb49` - used to encrypt PATs stashed in GitHub repos

### Hashes

- SHA256 (outer package.zip): `0736c83273465c6b64c04bd92f0e7a8517e8dbc19bb54d6c9ab41420fd833d6b`
- SHA256 (Miasma-Open-Source-Release-main.zip): `6331d1511783dcb1158fb54775f563e90399b3a2a81a584d3cba9a77f63d15a7`

## Affected Versions

- `Miasma-Open-Source-Release-main (voicefromtheouterworld, no published version tag)`

## MITRE ATT&CK

| ID | Technique | Application | 
|---|---|---|
| `T1195.002` | Supply Chain Compromise: Software Supply Chain | npm/PyPI/RubyGems packages trojanized with preinstall hooks; GitHub Actions semver tags hijacked; OIDC branch injection; PyPI typo-squatting via `-mcp` /`-mpc` suffixes | 
| `T1195.001` | Supply Chain Compromise: Compromise Software Dependencies | `.npmrc`` git=node` override forces npm to execute`clone.js` as the git binary when resolving a`github:` dependency, bypassing`npm install --ignore-scripts` entirely | 
| `T1078.004` | Valid Accounts: Cloud Accounts | All propagation and exfiltration uses legitimate victim GitHub PATs; no malicious login events are generated | 
| `T1059.007` | JavaScript | Primary worm bundle; `JS_LOADER.mjs` bootstraps Bun from Node.js;`c2/checkin.ts`` eval()` s arbitrary JavaScript from signed GitHub commits at runtime | 
| `T1059.006` | Python | `INJECT_PTH.pth` runs on every Python interpreter startup;`GITHUB_MONITOR.py` downloads and executes arbitrary Python from signed C2 URLs | 
| `T1059.004` | Unix Shell | `BASH_LOADER.sh` bootstraps Bun on SSH targets;`DEADMAN_SWITCH.sh` monitors token validity and fires destruction handler | 
| `T1204.002` | User Execution: Malicious File | VSCode `folderOpen` task fires on repo open; Claude Code and Gemini CLI`SessionStart` hooks fire on session start; Cursor rules injection exploits model instruction-following | 
| `T1543.001` | Create or Modify System Process: Launch Agent | `~/.config/systemd/user/gh-token-monitor.service` and`update-monitor.service` on Linux;`~/Library/LaunchAgents/` equivalents on macOS;`loginctl enable-linger` survives user logout | 
| `T1546` | Event-Triggered Execution | Claude Code and Gemini CLI `SessionStart` hooks; VSCode`folderOpen` task | 
| `T1554` | Compromise Host Software Binary | npm `preinstall` hook injection; RubyGems native extension (`ext/extconf.rb` +`Makefile` ) injection; PyPI`.pth` injection | 
| `T1611` | Escape to Host | Spawns privileged Alpine container mounted at `/etc/sudoers.d/` to grant passwordless sudo to the runner user | 
| `T1610` | Deploy Container | Creates and starts privileged Docker container via Docker socket to escalate privileges and restore sudo | 
| `T1027` | Obfuscated Files or Information | Polyalphabetic substitution cipher (`stringtool.ts` ) with per-build passphrase baked into`runtimeDecoder.ts` ; all string literals wrapped in`scramble()` at build time | 
| `T1027.002` | Software Packing | `javascript-obfuscator` with control-flow flattening, dead code injection, and string array encoding; additional`obfplus-wrap.js` outer wrapper layer | 
| `T1027.013` | Encrypted/Encoded File | All embedded payload assets AES-256-GCM encrypted at build time; self-extracting payloads use AES-128-GCM + ROT-N with per-build random keys | 
| `T1036.005` | Masquerading: Match Legitimate Name or Location | C2 path `api.anthropic.com/v1/api` impersonates the Anthropic API; OIDC injection workflow named`"Dependabot Updates"` ; injected Actions workflow file named`codeql.yml` ; service name`gh-token-monitor` appears to be a legitimate monitoring tool | 
| `T1140` | Deobfuscate/Decode Files or Information | `runtimeDecoder.ts` decodes all obfuscated strings at runtime using the per-build injected passphrase | 
| `T1497.001` | Virtualization/Sandbox Evasion: System Checks | Checks for decoy token prefixes, sandbox CWD paths, specific sandbox tool paths, hostname containing "sandbox", and honeypot AWS credential file contents; exits on Russian locale | 
| `T1518.001` | Security Software Discovery | Checks 17 EDR/AV process names and 4 installation paths before running credential providers or mutators | 
| `T1562.001` | Impair Defenses: Disable or Modify Tools | Kills StepSecurity Harden-Runner Docker containers via Docker socket; spawns privileged container to restore runner sudo | 
| `T1562.006` | Impair Defenses: Indicator Blocking | Poisons `/etc/hosts` to localhost-loop`stepsecurity.io` domains; overwrites`/etc/resolv.conf` with public DNS servers | 
| `T1564.001` | Hide Artifacts: Hidden Files and Directories | Payload at `~/.config/index.js` ; service config at`~/.config/gh-token-monitor/` ; state file at`/var/tmp/.gh_update_state` | 
| `T1070` | Indicator Removal | Deletes `PATS` and`PASSPHRASE` secrets from GitHub Actions 15 seconds after reinfection workflow dispatch; orphan-commit.py creates and immediately deletes temp tags leaving commits unreferenced | 
| `T1552.001` | Unsecured Credentials: Credentials In Files | 100+ filesystem hotspot paths scanned including cloud credentials, package registry tokens, AI tool configs, shell histories, crypto wallets, and messaging app data | 
| `T1552.004` | Unsecured Credentials: Private Keys | `~/.ssh/id*` ,`~/.ssh/id_rsa` ,`~/.ssh/id_ecdsa` ,`~/.ssh/id_ed25519` ,`/etc/ssh/ssh_host_*_key` | 
| `T1552.005` | Unsecured Credentials: Cloud Instance Metadata API | AWS IMDSv2, Azure IMDS, and GCP metadata server all queried for credentials | 
| `T1528` | Steal Application Access Token | GitHub PATs, npm tokens, RubyGems API keys, PyPI tokens, JFrog tokens harvested from files and environment | 
| `T1539` | Steal Web Session Cookie | Discord and Element browser local storage; Signal config; Slack cookies | 
| `T1555.005` | Credentials from Password Stores: Password Managers | 1Password CLI (`op` ), Bitwarden CLI (`bw` ),`pass` ,`gopass` | 
| `T1212` | Exploitation for Credential Access | GitHub Runner `/proc/<pid>/mem` dump extracts Actions secrets that never touch disk or environment variables | 
| `T1021.004` | Remote Services: SSH | SCP worm payload to hosts enumerated from `~/.ssh/known_hosts` and config, then SSH exec | 
| `T1072` | Software Deployment Tools | AWS SSM `SendCommand` (AWS-RunShellScript) dispatched to all managed EC2 instances across 17 regions | 
| `T1041` | Exfiltration Over C2 Channel | Encrypted batches HTTPS-POSTed to `api.anthropic.com/v1/api` (operator builds) | 
| `T1567.001` | Exfiltration Over Web Service: Exfiltration to Code Repository | Encrypted blobs committed to freshly-created public GitHub repos with Greek underworld-themed names | 
| `T1022` | Archive Collected Data | Data is gzip-compressed then AES-256-GCM encrypted before exfiltration | 
| `T1568` | Dynamic Resolution | C2 domain discovered at runtime from cryptographically signed GitHub commits; allows operator to rotate C2 without redeploying the worm | 
| `T1102.002` | Web Service: Bidirectional Communication | `GITHUB_MONITOR.py` polls GitHub commit search hourly for signed`firedalazer` C2 commands;`c2/checkin.ts` performs runtime C2 eval via the same mechanism | 
| `T1573.002` | Encrypted Channel: Asymmetric Cryptography | All exfiltrated data wrapped in RSA-OAEP-SHA256; C2 commands authenticated with RSA-PSS-SHA256 | 

## A Note from Ossprey

Ossprey obtained and analyzed the Miasma source release after it was published publicly. This was not an Ossprey detection event - Miasma was not distributed through a package registry, and there was no package for our pipeline to flag. For organizations whose pipelines process npm, PyPI, or RubyGems dependencies, Ossprey's behavioral analysis models the exact tradecraft Miasma uses: `binding.gyp` and `preinstall` triggers, per-build AES-GCM self-extracting payloads, multi-ecosystem publish-token abuse, and the `.npmrc` `git=node` bypass of `--ignore-scripts`. Derivative campaigns that deploy this worm through a registry would be detectable before install. The public availability of the full source - including the TypoMutator, OIDC branch injection, and `--ignore-scripts` bypass, none of which are documented in the authors' ARCHITECTURE.MD - makes monitoring for Miasma-derived registry uploads an active Ossprey priority.

For teams processing open-source dependencies at scale, [book a demo](../) to see how Ossprey's behavioral analysis detects Miasma-derived campaigns before they reach your environment.

**Related Ossprey research:** [The Complete TeamPCP Campaign](./the-complete-teampcp-campaign) | [Trivy Supply Chain Attack: TeamPCP, CanisterWorm](./trivy-supply-chain-attack) | [Axios Hijacked: Cross-Platform RAT](./axios-hijacked-cross-platform-rat-via-maintainer-account-takeover) | [TJ-Actions Breach](./tj-actions-breach)

## Frequently Asked Questions

**Was there a malicious npm package I need to remove?** Miasma was released as a public source repository, not as a deployed registry package. No published npm, PyPI, or RubyGems package is known to contain this worm at time of writing. If you find an installed package that looks like a derivative, compare its `package.json` against the `preinstall` hook and Bun dependency patterns in the IOC section above.

**Does Ossprey detect Miasma?** Miasma was not distributed through a package registry, so there was no package for Ossprey's pipeline to analyze. Derivative campaigns deploying Miasma's tradecraft through npm, PyPI, or RubyGems would be detectable before install - the `binding.gyp` + `preinstall` combination, per-build AES-GCM self-extracting payloads, and `.npmrc` `git=node` override are all behavioral signals Ossprey models.

**How confident are you that TeamPCP authored this?** We are not. The README claims TeamPCP authorship. Our assessment is that this was written by a group inspired by TeamPCP rather than TeamPCP itself. Attribution requires C2 infrastructure correlation beyond code-level overlap. See our [Complete TeamPCP Campaign](./the-complete-teampcp-campaign) research for the confirmed attribution baseline.

**Is the api.anthropic.com C2 infrastructure live?** The `api.anthropic.com` transport is explicitly stripped from the open-source build. In the published source, GitHub repository commits are the only active exfiltration path. Operator builds are expected to re-enable it - the full implementation is present in `src/sender/domain/`.

**I found** **gh-token-monitor** **running. What do I do?** Stop the service before revoking any stolen PATs. The dead-man switch fires `rm -rf ~/; rm -rf ~/Documents` the moment it receives a 4xx response from the GitHub API on the monitored token. Stop first: `systemctl --user stop gh-token-monitor` (Linux) or `launchctl unload ~/Library/LaunchAgents/com.user.gh-token-monitor.plist` (macOS). Then revoke.

Real World Attacks

## The Engineer's Laptop Is the New Front Door

Mary Welton

30 Oct 2026

Real World Attacks

## Blankets, worms, and RATs: New worm targets NPM and AUR

Ellie Jevon

29 Sept 2026

Real World Attacks

## How Ossprey protects from novel AI-accelerated malware development, and cross-ecosystem supply-chain attacks.

Ossprey Research Team

24 Sept 2026
