Move Miasma npm abuse into dedicated page

This commit is contained in:
Carlos Polop
2026-06-25 18:00:45 +02:00
parent b3aea588e8
commit 1b27af5f46
3 changed files with 98 additions and 37 deletions
+1
View File
@@ -17,6 +17,7 @@
- [Gh Actions - Artifact Poisoning](pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-artifact-poisoning.md) - [Gh Actions - Artifact Poisoning](pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-artifact-poisoning.md)
- [GH Actions - Cache Poisoning](pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-cache-poisoning.md) - [GH Actions - Cache Poisoning](pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-cache-poisoning.md)
- [Gh Actions - Context Script Injections](pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-context-script-injections.md) - [Gh Actions - Context Script Injections](pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-context-script-injections.md)
- [GH Actions - npm Supply Chain Abuse](pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-npm-supply-chain-abuse.md)
- [Accessible Deleted Data in Github](pentesting-ci-cd/github-security/accessible-deleted-data-in-github.md) - [Accessible Deleted Data in Github](pentesting-ci-cd/github-security/accessible-deleted-data-in-github.md)
- [Basic Github Information](pentesting-ci-cd/github-security/basic-github-information.md) - [Basic Github Information](pentesting-ci-cd/github-security/basic-github-information.md)
- [Gitea Security](pentesting-ci-cd/gitea-security/README.md) - [Gitea Security](pentesting-ci-cd/gitea-security/README.md)
@@ -745,42 +745,14 @@ import base64,os;exec(base64.b64decode(os.environ["STAGE2_B64"]))
Drop the line above into a file such as `evil.pth` inside `site-packages` and it will execute during Python startup. This is especially useful in build agents that continuously spawn Python tooling (`pip`, linters, test runners, release scripts). Drop the line above into a file such as `evil.pth` inside `site-packages` and it will execute during Python startup. This is especially useful in build agents that continuously spawn Python tooling (`pip`, linters, test runners, release scripts).
#### `binding.gyp` / node-gyp execution (`Phantom Gyp`) #### npm supply-chain pivots from GitHub Actions
Not every install-time execution path lives in `package.json` lifecycle hooks. `node-gyp`'s `configure` step looks for a `binding.gyp` file in the package directory, so a compromised publisher can shift execution into the native build path and bypass controls that only audit `preinstall` / `postinstall`. For `binding.gyp` / Phantom Gyp execution, wormable npm publishing with stolen CI identities, and the limits of trusted publishing provenance after workflow compromise, check:
Practical implications during an assessment: {{#ref}}
gh-actions-npm-supply-chain-abuse.md
{{#endref}}
- Inspect the **published tarball**, not only the Git repo, for unexpected `binding.gyp`, `node-gyp`, or native-addon metadata in packages that should be pure JavaScript.
- Treat a sudden `binding.gyp` addition as an **execution primitive**, especially if defenders rely on lifecycle-hook monitoring or `--ignore-scripts` as their main intake control.
- Review release jobs that run `npm install`, `npm rebuild`, or other dependency build steps after restoring untrusted artifacts/caches, because a poisoned package can move execution to the native-build path.
#### Wormable npm publishing from CI / stolen maintainer identities
Once code runs in a maintainer workstation or release workflow, a single stolen registry identity can be turned into **self-propagating package compromise**:
1. Harvest maintainer secrets (`~/.npmrc`, PATs, OIDC request env vars, cloud creds, SSH keys).
2. Enumerate which packages the compromised identity or team can publish to.
3. Republish malicious versions across each writable package so downstream `npm install` executions create more credential-generation nodes.
This becomes more dangerous when the release workflow uses **trusted publishing**: if the attacker steals the GitHub Actions OIDC material from a job with `id-token: write`, the poisoned release can still receive **valid provenance** because the legitimate workflow really built it. Provenance answers _which workflow built this artifact_, not _whether the workflow/source tree was clean_.
Useful checks:
- Look for workflows that combine `id-token: write` with `npm publish`, `changesets`, or release bots and **do not require a human approval step**.
- Check whether the compromised identity can enumerate org/team package access (for example with `npm access ls-packages`) and whether package publishing bypasses 2FA.
- Add friction to the propagation loop with **staged publishing** / human 2FA approval and `minimumReleaseAge` quarantine before consuming newly published versions.
#### Repository-local AI assistant persistence
A compromised publisher or repo writer does not need to stop at install-time execution. Another persistence layer is to commit **assistant instruction/config files** into the repository so the next developer who opens the project feeds attacker-controlled instructions into local tooling. High-signal paths include:
- `.claude/settings.json`
- `.cursor/rules`
- `.gemini/`
- editor/IDE task or settings files that steer AI helpers
This is useful after CI compromise because the attacker can push those files with a stolen maintainer token, and the trigger later becomes **repository open / assistant load** instead of `npm install`. During reviews, diff for new assistant-policy files with the same suspicion level as new workflow files or build scripts.
#### Alternate exfil when outbound traffic is filtered #### Alternate exfil when outbound traffic is filtered
@@ -995,12 +967,8 @@ An organization in GitHub is very proactive in reporting accounts to GitHub. All
- [A Survey of 20242025 Open-Source Supply-Chain Compromises and Their Root Causes](https://words.filippo.io/compromise-survey/) - [A Survey of 20242025 Open-Source Supply-Chain Compromises and Their Root Causes](https://words.filippo.io/compromise-survey/)
- [Weaponizing the Protectors: TeamPCPs Multi-Stage Supply Chain Attack on Security Infrastructure](https://unit42.paloaltonetworks.com/teampcp-supply-chain-attacks/) - [Weaponizing the Protectors: TeamPCPs Multi-Stage Supply Chain Attack on Security Infrastructure](https://unit42.paloaltonetworks.com/teampcp-supply-chain-attacks/)
- [Mini Shai-Hulud: Frequently asked questions about the TeamPCP npm and PyPI supply chain campaign](https://www.tenable.com/blog/mini-shai-hulud-frequently-asked-questions) - [Mini Shai-Hulud: Frequently asked questions about the TeamPCP npm and PyPI supply chain campaign](https://www.tenable.com/blog/mini-shai-hulud-frequently-asked-questions)
- [What the Miasma campaign reveals about the new supply chain threat model and the underground market for developer credentials](https://www.tenable.com/blog/what-the-miasma-campaign-reveals-about-the-new-supply-chain-threat-model-and-the-underground)
- [Events that trigger workflows - GitHub Docs](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows) - [Events that trigger workflows - GitHub Docs](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows)
- [Trusted publishing for npm packages | npm Docs](https://docs.npmjs.com/trusted-publishers/) - [Trusted publishing for npm packages | npm Docs](https://docs.npmjs.com/trusted-publishers/)
- [Staged publishing for npm packages | npm Docs](https://docs.npmjs.com/staged-publishing/)
- [Generating provenance statements | npm Docs](https://docs.npmjs.com/generating-provenance-statements/) - [Generating provenance statements | npm Docs](https://docs.npmjs.com/generating-provenance-statements/)
- [npm orgs | npm Docs](https://docs.npmjs.com/using-npm/orgs.html)
- [node-gyp README](https://github.com/nodejs/node-gyp)
{{#include ../../../banners/hacktricks-training.md}} {{#include ../../../banners/hacktricks-training.md}}
@@ -0,0 +1,92 @@
# GH Actions - npm Supply Chain Abuse
{{#include ../../../banners/hacktricks-training.md}}
## Overview
After an attacker gets code execution in a GitHub Actions release workflow, maintainer workstation, or package build pipeline, npm publishing becomes a high-impact pivot. The goal is usually to steal publisher identity material, publish malicious versions, and turn downstream installs into more credential-generation nodes.
Typical credential sources:
- `~/.npmrc`, `NPM_TOKEN`, registry sessions, and npm automation tokens.
- GitHub PATs, `GITHUB_TOKEN`, release-bot credentials, SSH keys, and `.netrc` / git credential helpers.
- GitHub Actions OIDC request material (`ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN`) in jobs with `id-token: write`.
- Cloud credentials, Vault tokens, Kubernetes service account tokens, and `.env` files present in the release environment.
## Install-Time Execution Primitives
### Lifecycle hooks
The classic npm route is to publish a malicious package version with `preinstall`, `install`, `postinstall`, or `prepare` scripts. Any developer workstation or CI job that installs the version executes attacker-controlled code.
```json
{
"scripts": {
"postinstall": "node ./scripts/collect.js"
}
}
```
Defenders often monitor these scripts, so red-team reviews should also inspect less obvious execution paths.
### `binding.gyp` / node-gyp execution (Phantom Gyp)
Not every install-time execution path lives in `package.json` lifecycle hooks. `node-gyp`'s configure step looks for a `binding.gyp` file in the package directory, so a compromised publisher can shift execution into the native build path and bypass controls that only audit `preinstall` / `postinstall`.
Practical checks:
- Inspect the **published tarball**, not only the Git repo, for unexpected `binding.gyp`, `node-gyp`, or native-addon metadata in packages that should be pure JavaScript.
- Treat a sudden `binding.gyp` addition as an execution primitive, especially if defenders rely on lifecycle-hook monitoring or `--ignore-scripts`.
- Review release jobs that run `npm install`, `npm rebuild`, or dependency build steps after restoring untrusted artifacts/caches.
## Wormable npm Publishing
Once code runs in a maintainer workstation or release workflow, a single stolen registry identity can be turned into self-propagating package compromise:
1. Harvest maintainer secrets (`~/.npmrc`, PATs, OIDC request env vars, cloud creds, SSH keys).
2. Enumerate packages the compromised identity or team can publish to.
3. Republish malicious versions across each writable package.
4. Let downstream installs create more credential-generation nodes.
Useful enumeration from a compromised npm identity:
```bash
npm whoami
npm access ls-packages
npm access ls-collaborators <scope-or-package>
```
Attackers usually prefer packages with frequent CI installs, transitive popularity, or release automation that will install the malicious version quickly.
## Trusted Publishing and Provenance Limits
Trusted publishing/OIDC removes long-lived static npm tokens, but it does not make a compromised release workflow safe. If the attacker controls code that runs in a job with `id-token: write`, the malicious release can still receive valid provenance because the legitimate workflow really built and published it.
Provenance answers **which workflow built this artifact**, not **whether the workflow, source tree, cache, or build steps were clean**.
High-signal review points:
- Workflows combining `id-token: write` with `npm publish`, `pnpm publish`, `changesets`, release bots, or custom publish wrappers.
- Release jobs that restore caches or artifacts from lower-trust workflows before publishing.
- Jobs that publish without human approval, environment protection rules, or a second reviewer.
- Workflows that request OIDC before all build inputs have been verified.
## Hardening
- Use trusted publishing/OIDC instead of static npm tokens, but pair it with protected environments and human approval for sensitive scopes.
- Add staged publishing / human 2FA approval for high-impact packages where possible.
- Use `minimumReleaseAge` or equivalent dependency quarantine controls before consuming newly published package versions.
- Separate cache keys by trust boundary and never execute restored cache contents before integrity checks.
- Diff published tarballs against source repositories, and alert on unexpected native build metadata such as `binding.gyp`.
- Disable or tightly review lifecycle scripts in CI (`npm config set ignore-scripts true`) where builds do not need them.
- Monitor package access (`npm access ls-packages`) and remove stale maintainers, bots, and teams.
## References
- [What the Miasma campaign reveals about the new supply chain threat model and the underground market for developer credentials](https://www.tenable.com/blog/what-the-miasma-campaign-reveals-about-the-new-supply-chain-threat-model-and-the-underground)
- [Trusted publishing for npm packages | npm Docs](https://docs.npmjs.com/trusted-publishers/)
- [Staged publishing for npm packages | npm Docs](https://docs.npmjs.com/staged-publishing/)
- [npm orgs | npm Docs](https://docs.npmjs.com/using-npm/orgs.html)
- [node-gyp README](https://github.com/nodejs/node-gyp)
{{#include ../../../banners/hacktricks-training.md}}