# Compromised AsyncAPI npm packages: inside a CI supply-chain attack

Attacker exploited a pull_request_target PwnRequest in AsyncAPI's Netlify docs CI to steal the release bot's GitHub token, then published backdoored npm packages.

- Payout: Real-world hack
- Severity: critical
- Program: AsyncAPI
- Credited: Datadog Security Labs
- Disclosed: 2026-07-14
- Write-up quality: 9/10
- Original: https://securitylabs.datadoghq.com/articles/compromised-asyncapi-npm-packages/
- Library page: https://bugbountylibrary.com/r/blog%3Asecuritylabs-datadoghq-com-articles-compromised-asyncapi-npm-packages
- Tags: domain:oss impact:data-leak impact:integrity kind:incident platform:threat-intel sev:critical tech:github-actions tech:nodejs technique:recon technique:source-review vuln:secrets-leak

**Target:** GitHub Actions CI workflow (pull_request_target) and npm release pipeline
**Root cause:** The Docs Preview (Netlify) workflow used pull_request_target with a path filter on apps/generator/docs/**, so attacker-controlled code in a fork PR ran with repo secrets and a privileged token. Stolen asyncapi-bot credentials were then used to push malicious commits that the release pipeline published.
**Steps:**
1. Open fork of asyncapi/generator and commit malicious JS under apps/generator/docs/** (commit 47be388).
2. Open PR #2155 from the fork; pull_request_target triggers manual-netlify-preview.yml with secrets.
3. Payload fetches second stage from `https://rentry.co/elzotebo` and evals it.
4. Second stage writes `/tmp/observer-<pid>.js`, scans `/proc/*/environ` for `INPUT_GITHUB-TOKEN`, `NETLIFY_AUTH_TOKEN`, `NETLIFY_SITE_ID`.
5. Patch `/home/runner/work/_actions/actions/checkout/v5/dist/index.js` so exfil runs at job exit, POSTing creds to a Rentry paste.
6. Use stolen asyncapi-bot token to push commit 3eab3ec with `fix:` message to `next` branch, matching release workflow trigger.
7. Release workflow publishes @asyncapi/generator@3.3.1 and siblings to npm with valid provenance.
**How found:** Researchers noticed 36 spam 'docs: add donation files' PRs clustered minutes before the attack, spotted a numbering gap at PR #2155, and pivoted to the Actions API run 29307859879 which leaked the fork owner elzotebo999 and head SHA 47be388, recoverable from the upstream repo after fork deletion.
**Exploitation:** Attacker flooded the repo with 36 spam donation PRs to hide PR #2155, triggered the vulnerable pull_request_target job from a fork, dumped CI env vars and patched actions/checkout to exfiltrate the asyncapi-bot GitHub token and Netlify tokens to Rentry. With the bot token they pushed a `fix:`-prefixed commit to `next`, letting the legitimate Release workflow publish backdoored packages that run a detached node payload fetching a second stage from IPFS, which harvests AWS/K8s/GCP/npm/Slack/Discord/Telegram credentials and persists via .vscode/tasks.json and .claude/settings.json.
**Escalation:** CI token theft chained into release-bot account takeover, then into npm supply-chain compromise reaching 3M+ weekly downloaders and downstream developer machines.
**Insight:** When a repo is flooded with spam PRs, look for gaps in PR numbering and query the Actions runs API for pull_request_target runs — the run metadata retains the fork owner and head SHA even after the fork and PR are deleted, and the commit stays reachable via the upstream repo URL.
