How AI is changing the SOC operating model. Listen now →

close

How AI is changing the SOC operating model. Listen now →

close

BLOG

Mini Shai-Hulud Supply-Chain Compromise of @redhat-cloud-services npm Packages via GitHub Actions OIDC Abuse

Alessandra

Rizzo

Summary

On 2026-06-01, in a new wave of Mini Shai-Hulud, an attacker abused the @redhat-cloud-services GitHub Actions OIDC trusted publisher to ship malicious versions of packages across that npm scope. Each malicious version adds a preinstall hook that runs a multi-megabyte obfuscated index.js on every npm install, harvesting cloud, CI, and registry credentials and then self-propagating through the tokens it steals. The activity matches the Mini Shai-Hulud worm toolkit and is identified by the strings "Miasma: The Spreading Blight" and "niagA oG eW ereH :duluH-iahS", which the attacker uses as the description of the public GitHub repositories it exfiltrates to.

The important point for response is that no npm token was stolen and no maintainer changed. The packages were published by Red Hat's own automated release pipeline using its legitimate trusted identity, so two-factor authentication and routine token rotation alone do not contain this.

Attack Chain


Initial access: Minting for OIDC tokens

The root cause sits in the source repositories, not the packages. In RedHatInsights/frontend-components, commit 8bf0512 ("chore: update dependencies") is an orphan commit with zero parents, pushed as a history-less branch. It adds a GitHub Actions workflow and a roughly 4 MB _index.js. The committer name shown belongs to a real contributor, and the branch from which this commit ran points to a commit directly in the root repository rather than a fork.

The developer’s push access may have been harvested through a GitHub token, an SSH key or GitHub credentials. This was enough to publish the malicious GitHub Actions ci.yaml.

The workflow is therefore the engine: it triggers on a push to any branch, requests id-token: write to mint a GitHub OIDC token, then runs bun run _index.js with an OIDC_PACKAGES environment variable listing the scope's packages. Because the npm trusted-publisher binding accepted any workflow in the repository carrying id-token: write rather than a single pinned workflow on main, the malicious job was able to publish under Red Hat's trusted identity.

The same pattern reached javascript-clients and platform-frontend-ai-toolkit. The pinned actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd in that workflow is a confirmed worm fingerprint rather than a coincidence: SafeDep observed the same pinned SHA in the worm's own propagation commits.

Delivery and payload

Each malicious version sets "preinstall": "node index.js", so the code runs before any application code, including on CI runners. The index.js grows from a few kilobytes to roughly 4.3 MB and is a single statement that rebuilds a character-code array through String.fromCharCode, reverses a Caesar shift, and evals the result. The shift is randomized per build (we measured 12 and 5 on tsc-transform-imports, StepSecurity reported 21 on host-inventory-client, and SafeDep reported 9 on patch-client), so any signature keyed to one shift value will miss the others. The static harness we built auto-detects the shift for this reason.

The decoded layer is a small async loader that AES-128-GCM-decrypts two embedded blobs using hardcoded keys: a Bun-runtime downloader and the roughly 634 KB main implant. The loader fetches Bun v1.3.13 from the official GitHub releases mirror into a temporary directory, writes the implant to /tmp/p<random>.js, and runs it under Bun so it never touches the victim's Node installation. The implant itself is wrapped in an obfuscator.io string-array scheme layered over a bespoke PBKDF2-derived substitution cipher, which is why the campaign string and C2 details only assemble at runtime and resist static extraction.

What the payload does

Once running, the implant sweeps for credentials across AWS (including instance and ECS metadata, Secrets Manager, and SSM), Azure managed identity, GCP service accounts, HashiCorp Vault, Kubernetes service-account tokens, npm, GitHub, roughly forty CI providers, and password managers including Bitwarden and gopass. It reads ~/.npmrc, ~/.netrc, and shell and database history, and runs secret-scanning regexes over what it collects.

On GitHub Actions runners it reads /proc/<pid>/mem from the Runner.Worker process and uses ACTIONS_RUNTIME_TOKEN to target variables flagged isSecret, recovering masked secrets that never appear in workflow logs. Among the variables it reads is ANTHROPIC_API_KEY.

Exfiltration goes to attacker-created public GitHub repositories whose description is "Miasma: The Spreading Blight", routed through api.github.com with a spoofed python-requests/2.31.0 user agent so it blends with ordinary automation. The committed data is hybrid-encrypted to the attacker, not readable plaintext, as detailed under GitHub Exfiltration below.

The worm propagates two ways. It republishes tampered npm tarballs after exchanging OIDC tokens for publish rights and signs them through Sigstore so they carry valid provenance, and it commits a .github/workflows/codeql.yml file on a chore/add-codeql-static-analysis branch via the GraphQL createCommitOnBranch mutation so the change appears as a signed, verified CodeQL improvement that maintainers are likely to wave through.

Beyond theft, it attempts a Docker-socket container escape to give the runner passwordless sudo, probes for CrowdStrike, SentinelOne, Carbon Black, and StepSecurity Harden-Runner before acting, and stays quiet if anti-analysis variables such as __FAKE_PLATFORM__, __IS_DAEMON, or SKIP_DOMAIN are present.

For persistence that survives npm uninstall, it injects a SessionStart hook into Claude Code's ~/.claude/settings.json and a folderOpen task into VS Code's .vscode/tasks.json.

GitHub Exfiltration

At time of writing we have identified the GitHub compromised account letsgo0 as the potential dead-drop hub account for the exfiltrated data. Just today, it committed to over 400 repositories linked to the Mini Shai-Hulud campaign.

The repositories are named by a generator that combines an adjective, a mythological creature, and a number, for example relentless-thunderbolt-87930 and stygian-onslaught-86745, interspersed with Dune terms such as kralizec, ghola, mentat, fedaykin, and heighliner. Repository descriptions carry campaign banners, including "Miasma: The Spreading Blight" and "Shai-Hulud: Here We Go Again” stored reversed, as in niagA oG eW ereH :duluH-iahS, an evasion that defeats a plaintext GitHub description search, so any count based on searching for the forward string undercounts the hub.

Each repository holds exfil records at paths like /results/results-<numbers>-0.json, structured as an { envelope, key } pair. This is hybrid encryption: the stolen blob is encrypted with a random symmetric key (envelope), and that key is encrypted to a hardcoded attacker public key (key). So although the repositories are public, the data inside is not readable plaintext, and only the attacker's private key can open it. Cracking the envelope is not feasible without that key and is not a useful avenue. Scoping which organizations were affected therefore has to come from install telemetry and from CI and GitHub audit logs rather than from reading the dumps. Because the attacker can decrypt, the exposure is real, so takedown of the hub and rotation by affected parties remain the priorities.

Indicators of compromise

Note that exfiltrated data is committed via GitHub API to a new repository with the description "Miasma: The Spreading Blight". AES keys are regenerated per build, so each package version's keys are distinct indicators.

Type

Indicator

Commit

8bf051251ec3b973e39a313547e53421a2f8d2f6 (orphan, in frontend-components)

Publish workflow

release workflow, on push branches ['*'], id-token: write, step bun run _index.js, env OIDC_PACKAGES / WORKFLOW_ID / REPO_ID_SUFFIX

Worm propagation

branch chore/add-codeql-static-analysis, injected .github/workflows/codeql.yml, pinned actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd

Exfil hub account

letsgo0 (single GitHub account used as the dead-drop collection point; likely a compromised real account)

Exfil repo banners

descriptions Miasma: The Spreading Blight and Shai-Hulud: Here We Go Again (the latter often stored reversed, e.g. niagA oG eW ereH :duluH-iahS)

Exfil repo naming

generated <adjective>-<creature>-<number> plus Dune terms (kralizec, ghola, mentat, fedaykin, heighliner)

Exfil record format

results/results/results-<numbers>-0.json containing { envelope, key }, hybrid-encrypted to an attacker public key

Exfil transport

commits via api.github.com with spoofed UA python-requests/2.31.0

Package marker

"preinstall": "node index.js" with a multi-MB single-statement eval index.js

patch-client@4.0.4 tarball SHA256

031ba872d5a84bfb18115f432811e4b45180346a1bae653f7fd85f918e7bb3a3

patch-client@4.0.4 index.js SHA256

df1732f5bfec12e066be44dee02ec8a243e4868d38672c1b1d065359dd735a14

Decrypted payload SHA256

0dc06ecdaa63fe24859cfd955053c23245c536e4733480239d14bebf12688e35

AES key, tsc-transform-imports@1.2.2 _b (first-hand)

key 649e93cee547fba06e12234b43c1086f, iv 97962833c7b15176e7a6398f, tag b0ac646784bfa76ecb36d84fce5f9df1

AES keys, patch-client@4.0.4

_b fe0d71d57ecf4fa0a433185bf59a03f5, _p f5e5dca9b725ec18514c4b322ed35d2b

AES keys, host-inventory-client@5.0.3

_b 4c26cf9791bce1bfd4b84eba80ce2754, _p ec514c074caf0ffdce6c66a0e95753d8

Runtime artifacts

Bun download from github.com/oven-sh/bun/releases/download/bun-v1.3.13/; /tmp/p<random>.js, /tmp/b-<random>/bun, /tmp/kitty-<random>

Host changes

~/.claude/settings.json (SessionStart hook), .vscode/tasks.json (folderOpen task)

Anti-analysis env vars

__FAKE_PLATFORM__, TESTING_TAR_FAKE_PLATFORM, __IS_DAEMON, SKIP_DOMAIN

Affected packages

Confirmed 32 packages published through the same trusted publisher, with patch-client@4.0.4 as its detailed delivery sample; StepSecurity reported an overlapping set. We also confirm the malicious payload in tsc-transform-imports@1.2.4

Treat the published lists as a floor, not a ceiling, and verify any @redhat-cloud-services/* dependency by content and by publish timestamp around 2026-06-01, since malicious tarballs can be yanked before they are catalogued.

Package

Version

chrome

2.3.1

compliance-client

4.0.3

config-manager-client

5.0.4

entitlements-client

4.0.11

eslint-config-redhat-cloud-services

3.2.1

frontend-components

7.7.2

frontend-components-advisor-components

3.8.2

frontend-components-config

6.11.3

frontend-components-config-utilities

4.11.2

frontend-components-notifications

6.9.2

frontend-components-remediations

4.9.2

frontend-components-testing

1.2.1

frontend-components-translations

4.4.1

frontend-components-utilities

7.4.1

hcc-feo-mcp

0.3.1

hcc-kessel-mcp

0.3.1

hcc-pf-mcp

0.6.1

host-inventory-client

5.0.3

insights-client

4.0.4

integrations-client

6.0.4

javascript-clients-shared

2.0.8

notifications-client

6.1.4

patch-client

4.0.4

quickstarts-client

4.0.11

rbac-client

9.0.3

remediations-client

4.0.4

rule-components

4.7.2

sources-client

3.0.10

topological-inventory-client

3.0.10

tsc-transform-imports

1.2.2 and 1.2.4

types

3.6.1

vulnerabilities-client

2.1.8

Recommendations

If you installed any affected version, treat every credential reachable from that host or CI runner as compromised, including masked CI secrets, and assume ANTHROPIC_API_KEY and any reachable password-manager material were taken. Sequence the response carefully, because revoking credentials too early can backfire. The payload installs a destructive token monitor, a dead-man switch that watches a stolen GitHub token and, if it detects that the token has been invalidated, can run destructive commands such as wiping the user's home directory. Do not rotate the GitHub token first. Isolate the affected machine or CI runner, remove persistence, and preserve forensic evidence (disk image, npm cache, shell history, process and CI telemetry, GitHub audit logs) before rotating anything. Only once persistence is confirmed removed, and working from a clean system, rotate cloud keys, npm tokens, GitHub PATs, GitHub Actions secrets, and Vault and Kubernetes tokens. Pin affected dependencies back to the last known-good versions and regenerate lockfiles from trusted metadata. Removing persistence means more than npm uninstall. Inspect ~/.claude/settings.json and .vscode/tasks.json for hooks or tasks you did not add, and look for a token monitor installed as a user service or launch agent (for example gh-token-monitor and kitty-monitor units, with token, handler, and state files under ~/.config/), since all of these survive package removal. Audit your GitHub org for unexpected public repositories, for commits adding .github/workflows/codeql.yml on a chore/add-codeql-static-analysis branch, and for the pinned checkout SHA above. To prevent recurrence, pin each npm OIDC trusted publisher to one workflow file on main and require a manual-approval environment, scope workflow permissions tightly so jobs cannot freely mint OIDC tokens, and disable install scripts for CI installs where feasible.

Attribution

The TTPs and the "Miasma: The Spreading Blight" exfil-repo description match the Mini Shai-Hulud worm toolkit that SafeDep and others have tracked across recent npm incidents, including the TanStack and SAP @cap-js compromises.

References

SafeDep, "Mini Shai-Hulud 'Miasma: The Spreading Blight' Hits @redhat-cloud-services," 2026-06-01: https://safedep.io/redhat-cloud-services-hit-by-mini-shai-hulud-npm-worm/

StepSecurity, "Multiple redhat-cloud-services npm Packages compromised," 2026-06-01: https://www.stepsecurity.io/blog/multiple-redhat-cloud-services-npm-packages-compromised

JFrog Security Research, "Shai-Hulud - Miasma: The Spreading Blight Hits Red Hat npm Packages," 2026-06-01: https://research.jfrog.com/post/shai-hulud-miasma-redhat-cloud-services/

Malicious commit: https://github.com/RedHatInsights/frontend-components/commit/8bf051251ec3b973e39a313547e53421a2f8d2f6

Version diff (1.2.4 vs 1.2.2): https://npmdiff.dev/%40redhat-cloud-services%2Ftsc-transform-imports/1.2.4/1.2.2/package/index.js

Disclosure issues: RedHatInsights/javascript-clients#492 and #493, frontend-components#2329, platform-frontend-ai-toolkit#57

Background on the Shai-Hulud lineage: Unit 42, Datadog Security Labs, and Microsoft Security have all published analyses of prior waves.

Read More

Read Panther's latest threat research reports here.

Share:

Bolt-on AI closes alerts. Panther closes the loop.

See how Panther compounds intelligence across the SOC.

Bolt-on AI closes alerts. Panther closes the loop.

See how Panther compounds intelligence across the SOC.

Bolt-on AI closes alerts. Panther closes the loop.

See how Panther compounds intelligence across the SOC.

Bolt-on AI closes alerts. Panther closes the loop.

See how Panther compounds intelligence across the SOC.

Get product updates, webinars, and news

By submitting this form, you acknowledge and agree that Panther will process your personal information in accordance with the Privacy Policy.

Get product updates, webinars, and news

By submitting this form, you acknowledge and agree that Panther will process your personal information in accordance with the Privacy Policy.

Get product updates, webinars, and news

By submitting this form, you acknowledge and agree that Panther will process your personal information in accordance with the Privacy Policy.