React2Shell RCE

PDF REPORT
STATUS
DECLASSIFIED
SEVERITY
10.0
DISCOVERED
2025-12-03
UPLOADED
2025-12-07
#WEB #JAVASCRIPT #NEXTJS #REACT #REACTSERVERCOMPONENTS #INSECUREDESERIALIZATION #PROTOTYPETRAVERSAL #RCE #UNAUTHENTICATED #CVSS10

1. The Patient

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.

2. The Diagnosis

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.

  1. The Protocol: The Flight Protocol sends data in chunks with gaps and references ($id).
  2. The Flaw: When parsing these references, React used standard square bracket notation (obj[key]) to access properties.
  3. The Oversight: React did not check if the property belonged to the object itself. This allowed the parser to climb the Prototype Chain (__proto__) and access inherited properties like constructor.

3. The Kill-Chain

Phase 1: The Thenable Trap

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.

Phase 2: The Gadgets ($@ and $B)

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().

Phase 3: The Payload Logic

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" 
     }
  }
}

Phase 4: Execution

The chain resolves to Function("...")(). The attacker passes shell commands into this function constructor, achieving Remote Code Execution.

4. The Fix

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.

Developer Takeaway

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:

  1. Patch: Upgrade to Next.js 14.2.19+ or 15.0.5+.
  2. Audit: Review any custom deserialization logic in your applications.
// END TRANSMISSION CREDIT: msanft, maple3142