$ cat cve-2025-55182-react2shell.mdx
CVE-2025-55182: React2Shell — RCE in React Server Components
Authorization: this is an educational proof of concept for security research. Only run it against an app you own or are explicitly authorized to test. Unauthorized use against systems you don't control can be a crime.
Summary
React2Shell (CVE-2025-55182) is a critical CVSS 9.8 remote code
execution bug in React Server Components (RSC). The flaw lives in how RSC
deserializes multipart FormData: attacker-controlled fields are not properly
validated, which opens a prototype pollution path that ultimately reaches
child_process.execSync — turning an ordinary form submission into arbitrary
command execution on the server, with no authentication required.
This repository ships a minimal vulnerable app plus a Python exploit that crafts the polluting payload, sends it, and executes commands on the host running the Node.js process.
How the vulnerability works
RSC accepts server-action arguments and form submissions as multipart
FormData and reconstructs the original JavaScript values on the server. The
deserializer walks the incoming fields and rebuilds nested objects — but it does
so without sanitizing the keys and structural markers an attacker can supply.
By shaping a request that abuses those markers, an attacker can walk the prototype chain instead of a plain object, poisoning properties that the RSC runtime later trusts. The payload chains a few gadgets:
- a
thenproperty, so the polluted object is treated as a thenable and its resolution path is hijacked; - a
statusfield that pushes the deserializer into an "already resolved" state; - a nested
_responseobject that carries the command to run, wrapped in a command-execution prefix; FormDataconstructor-reference manipulation to bridge from the parsed form into the gadget that invokeschild_process.execSync.
Once the poisoned state is consumed, the command in _response is handed to
execSync and runs with the privileges of the Node.js process.
Affected versions
| React | Status |
|---|---|
19.0.0 | Vulnerable |
19.1.0 | Vulnerable |
19.1.1 | Vulnerable |
19.2.0 | Vulnerable |
Only applications using React Server Components are exposed — the sink is in the RSC FormData deserialization path, not in client-side React.
Impact
Successful exploitation gives an unauthenticated attacker arbitrary OS command execution as the user running the Node.js server. That's enough to read secrets and environment variables, pivot within the network, or fully take over the host — the full confidentiality / integrity / availability compromise that the CVSS 9.8 score reflects.
Proof of concept
The exploit builds the prototype-pollution payload, posts it as multipart
FormData to the vulnerable server action, and returns the command output. The
end-to-end flow:
- Craft — assemble the JSON gadget (
then/status/_response+FormDataconstructor refs) with the operator-supplied command. - Send — POST it as multipart
FormDatato the vulnerable RSC endpoint. - Pollute — the deserializer walks the prototype chain and reaches the
execSyncsink. - Execute — the command runs on the server and its output comes back in the response.
# python3 exploit.py -c "<command>" run a single command
# python3 exploit.py -i interactive shell
# python3 exploit.py -c "whoami" -v verbose (show the raw payload/response)Run it
Prerequisites: Node.js with npm, and Python 3 with the requests library.
1. Bring up the vulnerable app:
npm install
pip3 install requests
npm run dev2. Fire a single command from a second terminal:
python3 exploit.py -c "whoami"Useful commands to confirm execution: pwd, ls -la, cat <file>, and env
to enumerate environment variables (a common place for leaked secrets).
3. Drop into an interactive shell to run commands back-to-back:
python3 exploit.py -i4. Inspect the wire format with verbose mode when you want to see the raw payload and server response:
python3 exploit.py -c "whoami" -v