Target: React2Shell (Next.js / React Server Components) Component: Flight Protocol Parser Vector: HTTP Request (Insecure Deserialization)
React2Shell is a critical vulnerability affecting the ecosystem of Next.js and React Server Components. The core issue lies in the Flight Protocol, a streaming format designed to replace static JSON for server-client communication.
While JSON is safe because it is static, Flight allows the parser to dynamically reconstruct the object structure at runtime. This dynamic capability was exploited to create a massive Deserialization flaw.
The Context: This is a CVSS 10.0 vulnerability. It requires No Authentication, No User Interaction, and can be triggered by a single HTTP request, resulting in Full Shell Access on the server.
Root Cause: Insecure Deserialization via Prototype Traversal.
Attackers do not view JavaScript objects as code; they view them as a network of references. The goal is to traverse from a basic object up to the Function constructor, which allows arbitrary code execution.
$id).obj[key]) to access properties.__proto__) and access inherited properties like constructor.The parser has a specific behavior for “Thenables.” Any object containing a .then property is treated as a Promise. The parser automatically executes .then() to resolve it. Attackers use this to force the parser into specific execution paths.
To weaponize the traversal, specific gadgets are used:
$@: Requests the raw chunk (unparsed), allowing access to internal properties like status.$B: The Blob Gadget. This triggers the path _response._formData.get().The attacker constructs a malicious chunk that combines these elements. We control _response, and we force the parser to call a getter on our payload.
// The Official Payload Logic
crafted_chunk: {
"then": "$1:__proto__:then", // Hook the Flow
"status": "unresolved_model", // Force Logic
"_response": {
"_formData": {
// The Weapon: Accessing the Constructor
"get": "$1:constructor:constructor"
}
}
}
The chain resolves to Function("...")(). The attacker passes shell commands into this function constructor, achieving Remote Code Execution.
The remediation involves restricting property access to the object’s own properties, preventing prototype traversal.
Vulnerable Logic (JavaScript):
// BEFORE: JavaScript blindly climbs the prototype chain.
// If metadata[NAME] is "constructor", it returns the Function constructor.
return moduleExports[metadata[NAME]];
Patched Logic (TypeScript):
// PATCHED CODE in React Server Components
// AFTER: Explicit Property Check using hasOwnProperty.
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
return moduleExports[metadata[NAME]];
}
return undefined;
hasOwnProperty only looks at the object itself. It stops the traversal to inherited constructors, neutralizing the attack path.
JavaScript is too helpful. The prototype chain is a powerful feature, but in security contexts, it is a liability. When deserializing untrusted input, never assume property access is safe. Always enforce strict boundaries using hasOwnProperty or Object.create(null).
Immediate Action: