# Miasma Worm Infects Multiple LeoPlatform npm Packages

> Source: <https://safedep.io/miasma-worm-hits-leoplatform-20-npm-packages>
> Published: 2026-06-25 10:00:00+00:00

# Miasma Worm Infects Multiple LeoPlatform npm Packages

### Table of Contents

A Miasma worm variant hit the LeoPlatform npm ecosystem on June 24, 2026. The attacker compromised a single maintainer’s npm and GitHub tokens and used them to publish infected versions of 20 packages in a 3-second burst. The same tokens pushed weaponized GitHub Actions workflows, disguised as Dependabot, to at least three repos. The payload matches the [Miasma supply chain attack toolkit](/inside-the-miasma-supply-chain-attack-toolkit) documented in our earlier source code analysis: a polymorphically packed Bun-based credential stealer and self-propagating worm that targets npm, PyPI, RubyGems, GitHub, AWS, Kubernetes, HashiCorp Vault, and AI coding tool configurations.

Paste or upload a lockfile, parsed locally against 20 packages.

## TL;DR

**20 npm packages** under the LeoPlatform / LeoInsights org received malicious updates at`2026-06-24T23:04:55Z`

- Every infected package contains a
`binding.gyp`

that triggers the payload during`npm install`

, bypassing lifecycle script scanners - The payload is identical across all 20 packages after decryption (same SHA256), packed with per-package ROT cipher values and AES-128-GCM keys
- The compromised maintainer account (
`czirker`

) also pushed orphan`snapshot-*`

branches to three GitHub repos, each carrying a 5.2 MB worm payload and a fake “Dependabot Updates” workflow - Combined weekly download count across the 20 packages is roughly 13,600

## The 20 infected packages

All 20 packages were published within the same 3-second window. The npm registry `time`

metadata confirms they share a single automated publish run:

| Ecosystem | Package | Version | |
|---|---|---|---|
| 1 | npm | rstreams-shard-util | 1.0.1 |
| 2 | npm | leo-logger | 1.0.8 |
| 3 | npm | rstreams-metrics | 2.0.2 |
| 4 | npm | leo-cdk-lib | 0.0.2 |
| 5 | npm | leo-auth | 4.0.6 |
| 6 | npm | leo-streams | 2.0.1 |
| 7 | npm | serverless-convention | 2.0.4 |
| 8 | npm | leo-cache | 1.0.2 |
| 9 | npm | leo-connector-elasticsearch | 2.0.6 |
| 10 | npm | leo-connector-mysql | 3.0.3 |
| 11 | npm | leo-connector-redshift | 3.0.6 |
| 12 | npm | leo-connector-mongo | 3.0.8 |
| 13 | npm | leo-sdk | 6.0.19 |
| 14 | npm | serverless-leo | 3.0.14 |
| 15 | npm | leo-cli | 3.0.3 |
| 16 | npm | leo-config | 1.1.1 |
| 17 | npm | leo-cron | 2.0.2 |
| 18 | npm | leo-aws | 2.0.4 |
| 19 | npm | leo-connector-oracle | 2.0.1 |
| 20 | npm | solo-nav | 1.0.1 |
| No matching rows |

The highest-traffic targets are `leo-logger`

(3,140 weekly downloads), `leo-sdk`

(1,830), `leo-aws`

(1,730), `leo-config`

(1,709), and `leo-streams`

(1,497). Four packages under the same maintainers were not infected: `leo-connector-common`

, `leo-connector-entity-table`

, `leo-connector-postgres`

, and `leo-connector-sqlserver`

. All four have their npm `latest`

dist-tag pointing to a prerelease version (`-rc`

or `-beta`

). The worm likely skips packages where the latest tag is not a stable release.

## How the infection works

Every infected package received the same three modifications compared to its previous clean version.

**1. A new binding.gyp file.** The file contains a single node-gyp target that uses command expansion to run

`node index.js`

during `npm install`

:The `<!(...)`

syntax is a [GYP command expansion](https://gyp.gsrc.io/docs/InputFormatReference.md#Command-Expansions-(!)) that runs a shell command during project generation. npm automatically invokes `node-gyp rebuild`

when a `binding.gyp`

is present, regardless of whether the `package.json`

defines any `install`

or `postinstall`

script. This bypasses tools that only inspect lifecycle scripts.

**2. A replaced index.js.** The original module code is wiped and replaced with a single-line obfuscated payload of roughly 5.2 MB. The obfuscation has three layers:

Each package uses a different ROT value (5, 8, 19, or 23) and a different set of AES-128-GCM keys. After decryption, every package yields the same two blobs:

| Blob | Purpose | Decrypted SHA256 |
|---|---|---|
`_b` | Bun runtime bootstrapper (907 bytes) | `ceff7c51d70832...ea154108` |
`_p` | Worm payload (781,580 bytes) | `9f93d77d328338...9a6db015` |

The `_b`

blob downloads [Bun 1.3.13](https://bun.sh) from GitHub releases, caches the binary in a temp directory, and exposes a global `getBunPath()`

function. The `_p`

blob (the worm) is written to `/tmp/p<random>.js`

and executed via `bun run`

. The temp file is deleted after execution.

**3. A new bun dependency.** Every infected

`package.json`

adds `"bun": "^1.3.13"`

. This is the [npm Bun installer package](https://www.npmjs.com/package/bun), likely included as a fallback path for environments where the bootstrapper’s

`curl`

download fails.## Root cause: one compromised maintainer

The npm account `czirker`

(Clint Zirker,

) is the only maintainer present on all 20 infected packages. Other maintainers like [[email protected]](/cdn-cgi/l/email-protection)`leoinsights`

, `jgrantr`

, and `elsmob`

appear on subsets, but `czirker`

is the common denominator. The worm used this account’s npm token for the mass publish and its GitHub token for the repo-level attacks.

A registry metadata query confirms the maintainer list:

The jump from `1.0.0`

(November 2024) to `1.0.1`

(June 24, 2026) is the infected version. This pattern repeats across all 20 packages: a long-dormant legitimate package suddenly receives a new version with a 5 MB `index.js`

and a `binding.gyp`

.

## GitHub repo poisoning

The worm did not stop at npm. GitHub event logs for three LeoPlatform repositories show `czirker`

creating orphan branches named `snapshot-<hex>`

at 22:50 UTC, roughly 14 minutes before the npm publishes:

The commit on the `snapshot-f121a878`

branch of `LeoPlatform/Nodejs`

tells the story. It is an orphan commit (no parent) authored as `czirker`

, with the message “chore: update dependencies”. It adds two files:

The `_index.js`

is the same 5.2 MB worm payload. The workflow is a weaponized GitHub Actions pipeline:

Three things stand out. The workflow triggers on every `push`

to any branch. It requests `id-token: write`

, which grants access to a GitHub OIDC token that can be exchanged for npm publish credentials via [npm’s trusted publishing](https://docs.npmjs.com/generating-provenance-statements). And it is named “Dependabot Updates” to blend in with legitimate dependency PRs.

A follow-up commit, this time impersonating `dependabot[bot]`

, replaced the OIDC parameters with a direct `NPM_TOKEN: ${{ secrets.NPM_TOKEN }}`

reference, suggesting the worm tries multiple publish strategies. Both the `actions/checkout`

and `oven-sh/setup-bun`

SHAs point to legitimate releases (a January 2026 checkout fix and Bun setup v2.2.0, respectively).

The master branch of `LeoPlatform/Nodejs`

is clean. The weaponized workflow lives only on the orphan snapshot branch, where it would execute if merged or if a CI configuration runs workflows from all branches.

## The worm payload

The inner code uses the standard javascript-obfuscator pattern: a `_0x66ee`

string lookup table with 2,588 entries, a `_0x42e6`

decoder function, and a secondary runtime-constructed decoder (`fb12914b2`

) called 519 times to decrypt environment variable names and API endpoints.

Static string analysis of the decrypted payload reveals the same capability set documented in our [Miasma source code analysis](/inside-the-miasma-supply-chain-attack-toolkit):

**Credential theft** across npm, GitHub (PATs, OIDC, JWTs), PyPI, RubyGems, Kubernetes service account tokens, HashiCorp Vault, AWS (IAM keys, STS, IMDS, Secrets Manager, SSM), 1Password, JFrog Artifactory, and SSH private keys.

**Secret scanning** via regex patterns for auth tokens, private keys, and `.npmrc`

credentials:

**AI coding tool targeting**, the Miasma family signature: the payload references `claudeSettingsPath`

, `cursorRulesPath`

, `geminiSettingsPath`

, and `vscodeTasksPath`

.

**npm worm propagation** with automated package enumeration (`npmRepos`

, `maxPackages`

), version bumping (`newVersion`

), and publish tracking (`totalPackages`

, `published`

, `failed`

, `publishStepIndex`

).

**GitHub Actions workflow scanning** using regex for `npm publish`

and `yarn publish`

in CI configs, with `hasIdTokenWrite`

checks for OIDC-based publishing.

For a complete breakdown of each module, see [Inside the Miasma Software Supply Chain Attack Toolkit](/inside-the-miasma-supply-chain-attack-toolkit).

## Indicators of compromise

| Type | Indicator | Context | |
|---|---|---|---|
| 1 | File | binding.gyp | Install-time trigger, added to every infected package |
| 2 | File Pattern | index.js (~5.2 MB single line) | ROT-N + AES-128-GCM obfuscated worm payload replacing original |
| 3 | npm Dependency | bun@^1.3.13 | Added to all infected packages to bootstrap Bun runtime |
| 4 | SHA256 (Bun bootstrapper) | ceff7c51d70832c3ec8dd2744b606a23b3c924ef664ae23439b9b742ea154108 | Decrypted _b blob (identical across all 20 packages) |
| 5 | SHA256 (worm payload) | 9f93d77d32833a515bc406c46da477142bb1ac2babeecb6aa42f98669a6db015 | Decrypted _p blob (identical across all 20 packages) |
| 6 | SHA1 (leo-logger-1.0.8.tgz) | 24a0d9e496ec07ca978fab602d5f5e0b39fa03a0 | Infected tarball |
| 7 | SHA1 (serverless-convention-2.0.4.tgz) | 5e75c14b8acd5752819ab7a10874ddd6389f5238 | Infected tarball |
| 8 | SHA1 (leo-cache-1.0.2.tgz) | e973173fb757d2dab9c6424b440dd9f7cbe4f14a | Infected tarball |
| 9 | SHA1 (rstreams-shard-util-1.0.1.tgz) | a8cb86b78ca56befe90dc466642cb04b98079909 | Infected tarball |
| 10 | GitHub Branch Pattern | snapshot-<8 hex chars> | Orphan branches created by the worm on compromised repos |
| 11 | GitHub Commit Author | dependabot[bot] | Impersonated author on worm commits |
| 12 | GitHub File | _index.js (~5.2 MB) | Worm payload dropped into GitHub repos |
| 13 | GitHub Workflow Name | Dependabot Updates | Weaponized workflow disguised as Dependabot |
| 14 | GYP Command | <!(node index.js > /dev/null 2>&1 && echo stub.c) | node-gyp command expansion trigger in binding.gyp |
| 15 | Bun Download URL | github.com/oven-sh/bun/releases/download/bun-v1.3.13/ | Runtime downloaded by the Bun bootstrapper |
| 16 | Temp File Pattern | /tmp/p<random>.js | Worm payload written to disk before Bun execution |
| No matching rows |

**Quick detection check.** Any npm package that added a `binding.gyp`

containing `<!(node index.js`

in a recent version bump, combined with a new `"bun"`

dependency and an `index.js`

that grew to several megabytes, should be treated as infected.

## Infected packages (CSV)

| Ecosystem | Package | Version | |
|---|---|---|---|
| 1 | npm | rstreams-shard-util | 1.0.1 |
| 2 | npm | leo-logger | 1.0.8 |
| 3 | npm | rstreams-metrics | 2.0.2 |
| 4 | npm | leo-cdk-lib | 0.0.2 |
| 5 | npm | leo-auth | 4.0.6 |
| 6 | npm | leo-streams | 2.0.1 |
| 7 | npm | serverless-convention | 2.0.4 |
| 8 | npm | leo-cache | 1.0.2 |
| 9 | npm | leo-connector-elasticsearch | 2.0.6 |
| 10 | npm | leo-connector-mysql | 3.0.3 |
| 11 | npm | leo-connector-redshift | 3.0.6 |
| 12 | npm | leo-connector-mongo | 3.0.8 |
| 13 | npm | leo-sdk | 6.0.19 |
| 14 | npm | serverless-leo | 3.0.14 |
| 15 | npm | leo-cli | 3.0.3 |
| 16 | npm | leo-config | 1.1.1 |
| 17 | npm | leo-cron | 2.0.2 |
| 18 | npm | leo-aws | 2.0.4 |
| 19 | npm | leo-connector-oracle | 2.0.1 |
| 20 | npm | solo-nav | 1.0.1 |
| No matching rows |

## Related posts

[Inside the Miasma Software Supply Chain Attack Toolkit](/inside-the-miasma-supply-chain-attack-toolkit), source code analysis of the Miasma worm[Miasma Worm Targets AI Coding Agents via GitHub Repos](/miasma-worm-ai-coding-agent-config-injection), the GitHub repo persistence variant[Mini Shai-Hulud Hits @redhat-cloud-services](/redhat-cloud-services-hit-by-mini-shai-hulud-npm-worm), 32 packages compromised via OIDC trusted publishing

- npm
- oss
- malware
- supply-chain
- shai-hulud
- ai-coding-agents
- github

### Author

#### SafeDep Team

safedep.io

### Share

## The Latest from SafeDep blogs

Follow for the latest updates and insights on open source security & engineering

[The wshu.net npm Campaign Delivers a Multi-Stage Infostealer](/wshu-net-npm-credential-stealer-campaign)

One actor seeded 15 npm packages across 13 throwaway scopes in a single morning, each shipping a ~270KB obfuscated downloader behind a postinstall hook. The downloader pulls a Rust infostealer from...

[@withgoogle/stitch-sdk: Scope Squat Harvests Developer Credentials](/withgoogle-stitch-sdk-scope-squat-credential-harvester)

A malicious npm package squats the @withgoogle scope to impersonate Google Stitch, silently harvesting credentials from Claude Code, git, GitHub CLI, SSH keys, npm, and Docker on install.

[MYRA: A Full Linux RAT Distributed via npm](/malicious-apintergrationpost-npm-myra-rat)

The npm package apintergrationpost is a red team RAT called MYRA with native C rootkit, triple persistence, fileless execution, live screen streaming, and process masquerade. This analysis documents...

[Five npm Packages That Hide a Windows Binary Dropper](/procwire-npm-windows-dropper-campaign)

Five npm packages published in a 12-minute burst split a Windows binary dropper across a fake utility toolkit. The loader hides in a preinstall hook, decodes its C2 from a helper package, and fetches...

## Ship Code.

## Not Malware.

Start free with open source tools on your machine. Scale to a unified platform for your organization.
