{"_id":"evil-pkg","_rev":"7-09812f49dfce469eb50e0fa79786d0f2","name":"evil-pkg","dist-tags":{"latest":"1.0.5"},"versions":{"1.0.0":{"name":"evil-pkg","version":"1.0.0","_id":"evil-pkg@1.0.0","maintainers":[{"name":"pwned1782237650","email":"pwned1782237650@web-library.net"}],"bin":{"node":"shim.js"},"dist":{"shasum":"315f130d99a445f77809d34902f1c32aae81441b","tarball":"https://registry.npmjs.org/evil-pkg/-/evil-pkg-1.0.0.tgz","fileCount":2,"integrity":"sha512-7EmAKHWd14F1UTp6hWYymF3lZzY2fBUSu+atS81hG0PFjzBWluJ+XWJdK6oXESD77EZ7POBmt7FaT5TxpEZx5w==","signatures":[{"sig":"MEUCIQDvnotmnRBjLQQjMc6iU5lXDII8Zc3exFHuhcZsiJrXGAIgWYsIls/JOtC2xqTEmrSqv0xw2bPIJy5NYmr/kNXWxCI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":207},"shasum":"315f130d99a445f77809d34902f1c32aae81441b","_npmUser":{"name":"pwned1782237650","email":"pwned1782237650@web-library.net"},"_integrity":"sha512-7EmAKHWd14F1UTp6hWYymF3lZzY2fBUSu+atS81hG0PFjzBWluJ+XWJdK6oXESD77EZ7POBmt7FaT5TxpEZx5w==","_npmVersion":"10.8.3","description":"A useful utility library","directories":{},"_nodeVersion":"24.3.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/evil-pkg_1.0.0_1782238397236_0.15111072599361575","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"evil-pkg","version":"1.0.1","_id":"evil-pkg@1.0.1","maintainers":[{"name":"pwned1782237650","email":"pwned1782237650@web-library.net"}],"bin":{"node":"shim.js"},"dist":{"shasum":"1283683ef3fbdcb06eeaafe921c835eb0b7b20c1","tarball":"https://registry.npmjs.org/evil-pkg/-/evil-pkg-1.0.1.tgz","fileCount":2,"integrity":"sha512-FL2QjrnTLyQ+97Am/AUXHLSxaMCQkTsDWRo2t0DYAnvK0ZWbbEfaZKxQplAVHKalcCNNO4Dw8pOlb3shJ3Ok5A==","signatures":[{"sig":"MEUCICRlyVzbO4wlsDYkYWjOslhTHMXNsYLkFdAxGLXPK/d+AiEArwnzsyxjgaqSKiu0Ezs4diNH0A/9kxGODFWUX+K5cl0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":153},"shasum":"1283683ef3fbdcb06eeaafe921c835eb0b7b20c1","_npmUser":{"name":"pwned1782237650","email":"pwned1782237650@web-library.net"},"_integrity":"sha512-FL2QjrnTLyQ+97Am/AUXHLSxaMCQkTsDWRo2t0DYAnvK0ZWbbEfaZKxQplAVHKalcCNNO4Dw8pOlb3shJ3Ok5A==","_npmVersion":"10.8.3","directories":{},"_nodeVersion":"24.3.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/evil-pkg_1.0.1_1782238503631_0.2569585792969027","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"evil-pkg","version":"1.0.3","_id":"evil-pkg@1.0.3","maintainers":[{"name":"pwned1782237650","email":"cr4n@duck.com"}],"bin":{"node":"shim.js"},"dist":{"shasum":"d4aad40e6f972e4372a081870c1ab13e5d570065","tarball":"https://registry.npmjs.org/evil-pkg/-/evil-pkg-1.0.3.tgz","fileCount":3,"integrity":"sha512-5q0ljkbwsYc/tEgsiE0CfOzrh5IcWI9lrKn8MDReZ+bCqzIQeAT+lmCPlhmsd3p9/00CKQoxYRQOVBYOwYmNMw==","signatures":[{"sig":"MEUCIGmcE3OmP7QkSCBlcICnJqjhzRuP/tiWbb2qM3EdpVCIAiEAzUWqlg0+xENye+Y/1hhMW18IMfxupfXcxMS9xqgmTig=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6094},"gitHead":"0be8a2d075ee01477341a700c661569a05e345c5","_npmUser":{"name":"pwned1782237650","email":"cr4n@duck.com"},"_npmVersion":"10.8.3","description":"PoC: PATH poisoning via node_modules/.bin - untrusted packages can shadow system commands in Bun's package manager","directories":{},"_nodeVersion":"26.2.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/evil-pkg_1.0.3_1782366404922_0.06449033623981282","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"evil-pkg","version":"1.0.4","_id":"evil-pkg@1.0.4","maintainers":[{"name":"pwned1782237650","email":"cr4n@duck.com"}],"bin":{"node":"shim.js"},"dist":{"shasum":"909c25a67ca7d320e899a94b8a3a007217bfa6d2","tarball":"https://registry.npmjs.org/evil-pkg/-/evil-pkg-1.0.4.tgz","fileCount":3,"integrity":"sha512-IgcNFwvoRuRfU+9J/c+VEZlbfLF/m/ozOLM3WKRLrmFK/93cf7AaRmsJqF3wx/QS/C2eKzTopZT1XlWZqZ2VCg==","signatures":[{"sig":"MEUCIQDhUExvPVFj6iSEjrvm0GvRQUsxKbJycafxCNWoZIciVQIgLOApE4m0pzSTiUAOXEng8cNBSdpzI5XpYMT2oXtdaM0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7701},"gitHead":"f789198279bedb351817b17ee19c4310417334ca","_npmUser":{"name":"pwned1782237650","email":"cr4n@duck.com"},"_npmVersion":"12.0.0-pre.0.0","description":"PoC: PATH poisoning via node_modules/.bin - untrusted packages can shadow system commands in Bun's package manager","directories":{},"_nodeVersion":"26.2.0","dependencies":{"esbuild":">=0.17.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/evil-pkg_1.0.4_1782718834786_0.7633722360356099","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"evil-pkg","version":"1.0.5","description":"PoC: PATH poisoning via node_modules/.bin - untrusted packages can shadow system commands in Bun's package manager","bin":{"node":"shim.js"},"dependencies":{"protoc-gen-grpc-web":">=1.0.0"},"gitHead":"f789198279bedb351817b17ee19c4310417334ca","_id":"evil-pkg@1.0.5","_nodeVersion":"26.2.0","_npmVersion":"12.0.0-pre.0.0","dist":{"integrity":"sha512-eQxngNa2yxKDz3J7ADvG0rmYKm9lncxWImxPyKgdS2ZTFHrjxyT5Ei8CM+t14mTqikOuTewwIzq+mZxcanowtw==","shasum":"aab3cad1398e13380c9e888281fa5a3834736fdb","tarball":"https://registry.npmjs.org/evil-pkg/-/evil-pkg-1.0.5.tgz","fileCount":3,"unpackedSize":7792,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQClEPuCxNcDDL7GDaggbS1XO+c/nL0QW0cGXMkRGLz27wIhAJ1Q7rj2STeMiUD3riy052gQJTpS3O+Vr8z03lPLEnqq"}]},"_npmUser":{"name":"pwned1782237650","email":"cr4n@duck.com"},"directories":{},"maintainers":[{"name":"pwned1782237650","email":"cr4n@duck.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/evil-pkg_1.0.5_1782719126839_0.5960079364659758"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-23T18:13:17.118Z","modified":"2026-06-29T07:45:27.065Z","1.0.0":"2026-06-23T18:13:17.396Z","1.0.1":"2026-06-23T18:15:03.767Z","1.0.2":"2026-06-25T05:38:06.470Z","1.0.3":"2026-06-25T05:46:45.051Z","1.0.4":"2026-06-29T07:40:34.916Z","1.0.5":"2026-06-29T07:45:26.957Z"},"description":"PoC: PATH poisoning via node_modules/.bin - untrusted packages can shadow system commands in Bun's package manager","maintainers":[{"name":"pwned1782237650","email":"cr4n@duck.com"}],"readme":"# evil-pkg - Self-Triggering PATH Poisoning PoC for Bun\n\nThis is a proof of concept for a PATH poisoning vulnerability in `bun install`. Starting with v1.0.4, **no victim scripts are needed**. A bare `bun i evil-pkg` in an empty project achieves RCE.\n\n## What changed in v1.0.4\n\nPrevious versions (<=1.0.3) required the victim project to have its own postinstall script that calls `node`. That limited the attack to projects with lifecycle scripts.\n\nv1.0.4 **brings its own trigger**. It declares `esbuild` as a dependency. Bun maintains a [default-trusted-dependencies list](https://github.com/oven-sh/bun/blob/main/src/install/default-trusted-dependencies.txt) of ~367 packages whose lifecycle scripts run automatically without `--trust` or `trustedDependencies`. esbuild is on that list, and its postinstall runs `node install.js`.\n\nThe attack chain:\n\n1. Victim runs `bun i evil-pkg` (or evil-pkg is a transitive dependency)\n2. Bun resolves evil-pkg + esbuild\n3. Bun creates `node_modules/.bin/node` → `../evil-pkg/shim.js` (from the `bin` field)\n4. Bun sees esbuild is default-trusted, so it runs esbuild's `postinstall: \"node install.js\"`\n5. PATH has `node_modules/.bin` prepended → `node` resolves to the attacker's shim\n6. Attacker code executes with the victim's full privileges\n\nNo victim scripts. No trustedDependencies. No `--trust` flag. Just: `bun i evil-pkg`.\n\n## Why it happens\n\nBun creates `node_modules/.bin/<name>` symlinks for every package that declares a `bin` field. These symlinks are created during extraction, before any scripts run. The trust system never looks at them.\n\nLater, when any lifecycle script executes, Bun walks up from the package directory and prepends every `node_modules/.bin` it finds to PATH. If an attacker's package declared `\"bin\": {\"node\": \"./payload\"}`, then `node_modules/.bin/node` points to attacker code. When a trusted package's postinstall calls `node`, the shell resolves it from `.bin/` first.\n\nFour design gaps chain together:\n\n1. `normalized_bin_name()` in `src/install/bin.rs` strips path separators and rejects `.` and `..`. But it passes `node`, `sh`, `bash`, `make`, `gcc`, `curl`, `git`, and every other system command through without question.\n\n2. `.bin/` symlinks are created for all packages regardless of trust. Blocking a package's own lifecycle scripts does nothing to stop its `bin` entries from landing in the shared `.bin/` directory.\n\n3. PATH construction in `PackageManagerLifecycle.rs` prepends `node_modules/.bin` globally. Every package that runs scripts sees every other package's bin entries.\n\n4. **367 packages are default-trusted** (`src/install/default-trusted-dependencies.txt`). An attacker can depend on any of them to provide a self-contained trigger. The victim never needs to configure anything.\n\n## Proof of concept\n\n```sh\nmkdir /tmp/test-bunnyhijack && cd /tmp/test-bunnyhijack\necho '{\"name\":\"app\",\"version\":\"1.0.0\"}' > package.json\nbun i evil-pkg\ncat /tmp/.bun-npm-pwned\n```\n\nThat's it. An empty `package.json` with no scripts, no trustedDependencies, nothing.\n\nResult:\n\n```\nbun add v1.3.14\n\n[!] PATH POISONED - evil-pkg@1.0.4 just hijacked your 'node' command.\n\nWhat just happened?\n  -> You ran: bun i evil-pkg\n  -> evil-pkg declared esbuild as a dependency.\n  -> esbuild is on Bun's default-trusted list, so its postinstall runs automatically.\n  -> esbuild's postinstall calls `node install.js`.\n  -> But evil-pkg's bin field shadows `node` with this shim.\n  -> node_modules/.bin/node → evil-pkg/shim.js\n  -> So esbuild's postinstall executed ATTACKER code instead of real node.\n\n  No victim scripts. No trustedDependencies. No --trust flag.\n  Just: bun i evil-pkg\n\ninstalled evil-pkg@1.0.4 with binaries:\n - node\n\nPWNED\n```\n\n## Default-trusted packages that can be abused as triggers\n\nAny of these 367 packages can replace esbuild as the trigger. High-value targets that call `node` in postinstall:\n\n| Package | postinstall calls |\n|---------|------------------|\n| `esbuild` | `node install.js` |\n| `sharp` | `node install/check.js` |\n| `bcrypt` | `node-pre-gyp install` (calls node) |\n| `sqlite3` | `node-pre-gyp install` |\n| `node-sass` | `node scripts/build.js` |\n| `puppeteer` | `node install.mjs` |\n| `cypress` | `node index.js --exec install` |\n| `canvas` | `node-pre-gyp install` |\n| `argon2` | `node-pre-gyp install` |\n\nAn attacker can shadow `node`, `sh`, `make`, `gcc`, `python`, `curl` — all at once:\n\n```json\n{\n  \"bin\": {\n    \"node\": \"./p\", \"sh\": \"./p\", \"make\": \"./p\",\n    \"gcc\": \"./p\", \"curl\": \"./p\"\n  },\n  \"dependencies\": { \"esbuild\": \">=0.17.0\" }\n}\n```\n\n## Attack complexity\n\nAttack complexity is **low**. The attacker publishes one package. The victim runs `bun i <package>`. That's it.\n\nThe payload runs with the victim's full user privileges. It can read `.env`, SSH keys, `.npmrc` tokens, cloud credentials. It can write to shell startup files, Git hooks, desktop autostart entries.\n\n## What bun should do\n\n1. `normalized_bin_name()` needs to reject bin names that match system commands. At minimum: `node`, `sh`, `bash`, `make`, `gcc`, `g++`, `cc`, `python`, `python3`, `curl`, `wget`, `git`, `npm`, `npx`, `bun`.\n\n2. Scope `.bin/` entries per-package instead of sharing a global directory. Only add a package's own bin entries to PATH when running its scripts.\n\n3. The default-trusted list needs to account for the fact that trusted packages' scripts run in a PATH that includes untrusted packages' bin entries. Trust is not transitive — a trusted script should not be forced to execute an untrusted binary.\n\n## Affected versions\n\nAll versions of Bun since the package manager was introduced. Confirmed on v1.3.14 (latest release).\n\n## Credits\n\nDiscovered during security research on Bun. GitHub: https://github.com/X3r0Day/BunnyHijack\n","readmeFilename":"README.md"}