joaovicdev
exploitexploitCVE-2025-55182

CVE-2025-55182: React2Shell — RCE in React Server ComponentsCVE-2025-55182: React2Shell — RCE nos React Server Components

React2Shell — a CVSS 10.0 unauthenticated RCE in the React Server Components deserializer (react-server-dom-*), added to CISA KEV two days after disclosure. Root cause, affected versions, and a working Python PoC.React2Shell — uma RCE não autenticada CVSS 10.0 no desserializador dos React Server Components (react-server-dom-*), adicionada ao CISA KEV dois dias depois da divulgação. Causa raiz, versões afetadas e uma PoC funcional em Python.

Proof of concept on GitHubProva de conceito no GitHub

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 CVSS 10.0 pre-authentication remote code execution bug in React Server Components (RSC), disclosed on 2025-12-03. The flaw is not in react or react-dom — it lives in the three RSC bundler packages that decode payloads sent to Server Function endpoints: react-server-dom-webpack, react-server-dom-parcel, and react-server-dom-turbopack.

NVD classifies it as CWE-502 — Deserialization of Untrusted Data. In React's own words, "the vulnerable code unsafely deserializes payloads from HTTP requests to Server Function endpoints." No authentication, no user interaction: an ordinary POST to an endpoint the framework mounts for you is enough.

This repository ships a minimal vulnerable app plus a Python exploit that crafts the 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. That reconstruction happens in ReactFlightReplyServer.js, and it is the trust boundary — it turns bytes the attacker fully controls into live JavaScript objects.

What the maintainers confirmed

The fix (facebook/react#35277) was authored by Sebastian Markbåge, who names the root cause directly:

"These hasOwnProperty checks were the ones that were missing. This is the critical fix. Without it, you can drill into objects not created by the parser itself."

That is the whole primitive. Without an own-property guard, the deserializer resolves keys along the prototype chain instead of staying inside the plain object it just built — so the attacker gets to reference properties the parser never created, and reach code the parser was never supposed to call. The rest of the PR, in the author's words, addresses "other gadgets and to slow down reverse engineering."

React deliberately withheld the exploitation details at disclosure time ("further details of the vulnerability will be provided after the rollout of the fix is complete"), which is why the advisory describes the class of bug and not the chain.

What this PoC does

The gadget chain below is this lab's observed path, not vendor-published detail — treat it as one working route through the primitive above, not as the canonical exploit:

  • a then property, so the object is duck-typed as a thenable and its resolution path is hijacked;
  • a status field that pushes the deserializer into an "already resolved" state;
  • a nested _response object carrying the command to run;
  • FormData constructor-reference manipulation to bridge from the parsed form into the gadget that invokes command execution.

Once the poisoned state is consumed, the command runs with the privileges of the Node.js process.

Affected versions

PackageVulnerableFixed
react-server-dom-webpack19.0.0, 19.1.0, 19.1.1, 19.2.019.0.1, 19.1.2, 19.2.1
react-server-dom-parcel19.0.0, 19.1.0, 19.1.1, 19.2.019.0.1, 19.1.2, 19.2.1
react-server-dom-turbopack19.0.0, 19.1.0, 19.1.1, 19.2.019.0.1, 19.1.2, 19.2.1

NVD additionally lists Next.js 15.0.0 – 16.0.7 as affected, since it ships the RSC runtime transitively. React's advisory names the exposed ecosystem: Next.js, React Router, Waku, @parcel/rsc, @vitejs/plugin-rsc, and rwsdk (Redwood SDK).

You are not affected if your React code does not use a server, or if you do not use a framework, bundler, or bundler plugin that supports RSC. React Native is unaffected unless a monorepo pulls in the vulnerable packages.

Note the shape of the fix: every patched release is a patch-level bump. There is no code of yours in the vulnerable path, and nothing of yours to rewrite.

Impact

Successful exploitation gives an unauthenticated attacker arbitrary OS command execution as the user running the Node.js server — enough to read secrets and environment variables, pivot within the network, or take over the host.

The CVSS v3.1 vector assigned by Facebook, Inc. is AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H — 10.0. The detail worth reading there is S:C (Scope: Changed): the compromised component is the RSC runtime, but the impact lands on the whole host. That single metric is the difference between the 9.8 this class of bug usually scores and a clean 10.0.

This was not theoretical for long:

DateEvent
2025-12-03Disclosure, patches published, advisory GHSA-fv66-9v8q-g76r
2025-12-05Added to the CISA Known Exploited Vulnerabilities catalog
2025-12-12CISA remediation deadline for federal agencies (BOD 22-01)

CISA flags it with knownRansomwareCampaignUse: Known, and AWS reported China-nexus threat groups exploiting it in the wild. CISA's own guidance is to "check for signs of potential compromise on all internet accessible REACT instances after applying mitigations" — patching alone does not answer whether you were already hit.

Proof of concept

The exploit builds the payload, posts it as multipart FormData to the vulnerable server action, and returns the command output. The end-to-end flow:

  1. Craft — assemble the JSON gadget (then / status / _response + FormData constructor refs) with the operator-supplied command.
  2. Send — POST it as multipart FormData to the vulnerable RSC endpoint.
  3. Resolve — the deserializer walks past the missing own-property guard and reaches the execution gadget.
  4. Execute — the command runs on the server and its output comes back in the response.
exploit.py — usage
# 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.

The lab pins Next.js 15.1.3 exactly — an unpinned install would resolve to a patched RSC runtime and the exploit would simply stop working. Keep the pin.

1. Bring up the vulnerable app:

terminal 1
npm install
pip3 install requests
npm run dev

2. Fire a single command from a second terminal:

terminal 2
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 -i

4. Inspect the wire format with verbose mode when you want to see the raw payload and server response:

python3 exploit.py -c "whoami" -v

References

Autorização: esta é uma prova de conceito educacional para pesquisa de segurança. Rode apenas contra uma aplicação que você possui ou está explicitamente autorizado a testar. Uso não autorizado contra sistemas que você não controla pode ser crime.

Resumo

O React2Shell (CVE-2025-55182) é uma execução remota de código pré-autenticação CVSS 10.0 nos React Server Components (RSC), divulgada em 03/12/2025. A falha não está no react nem no react-dom — ela vive nos três pacotes de bundler do RSC que decodificam payloads enviados aos endpoints de Server Function: react-server-dom-webpack, react-server-dom-parcel e react-server-dom-turbopack.

A NVD classifica como CWE-502 — Deserialization of Untrusted Data. Nas palavras do próprio React, "o código vulnerável desserializa de forma insegura payloads de requisições HTTP para endpoints de Server Function". Sem autenticação, sem interação do usuário: um POST comum a um endpoint que o framework monta para você já basta.

Este repositório traz uma aplicação vulnerável mínima mais um exploit em Python que monta o payload, envia e executa comandos no host que roda o processo Node.js.

Como a vulnerabilidade funciona

O RSC aceita argumentos de server actions e envios de formulário como FormData multipart e reconstrói os valores JavaScript originais no servidor. Essa reconstrução acontece em ReactFlightReplyServer.js, e é ali que está a fronteira de confiança — é onde bytes totalmente controlados pelo atacante viram objetos JavaScript vivos.

O que os mantenedores confirmaram

A correção (facebook/react#35277) foi escrita por Sebastian Markbåge, que nomeia a causa raiz diretamente:

"Essas checagens de hasOwnProperty eram as que faltavam. Esta é a correção crítica. Sem ela, dá para navegar dentro de objetos que não foram criados pelo próprio parser."

É essa a primitiva inteira. Sem uma guarda de propriedade própria, o desserializador resolve chaves ao longo da cadeia de protótipos em vez de ficar dentro do objeto simples que acabou de construir — então o atacante consegue referenciar propriedades que o parser nunca criou, e alcançar código que o parser nunca deveria chamar. O resto do PR, nas palavras do autor, trata de "outros gadgets e de dificultar a engenharia reversa".

O React deliberadamente omitiu os detalhes de exploração no momento da divulgação ("mais detalhes da vulnerabilidade serão fornecidos após a conclusão do rollout da correção"), e é por isso que o advisory descreve a classe do bug e não a cadeia.

O que esta PoC faz

A cadeia de gadgets abaixo é o caminho observado neste lab, não um detalhe publicado pelo fornecedor — trate como uma rota funcional através da primitiva acima, não como o exploit canônico:

  • uma propriedade then, para o objeto ser tipado por pato como um thenable e ter o caminho de resolução sequestrado;
  • um campo status que empurra o desserializador para um estado de "já resolvido";
  • um objeto _response aninhado carregando o comando a executar;
  • manipulação de referências ao construtor de FormData para fazer a ponte do formulário interpretado até o gadget que invoca a execução de comandos.

Uma vez consumido o estado envenenado, o comando roda com os privilégios do processo Node.js.

Versões afetadas

PacoteVulneráveisCorrigidas
react-server-dom-webpack19.0.0, 19.1.0, 19.1.1, 19.2.019.0.1, 19.1.2, 19.2.1
react-server-dom-parcel19.0.0, 19.1.0, 19.1.1, 19.2.019.0.1, 19.1.2, 19.2.1
react-server-dom-turbopack19.0.0, 19.1.0, 19.1.1, 19.2.019.0.1, 19.1.2, 19.2.1

A NVD lista ainda o Next.js 15.0.0 – 16.0.7 como afetado, já que ele carrega o runtime do RSC transitivamente. O advisory do React nomeia o ecossistema exposto: Next.js, React Router, Waku, @parcel/rsc, @vitejs/plugin-rsc e rwsdk (Redwood SDK).

Você não é afetado se o seu código React não usa servidor, ou se você não usa um framework, bundler ou plugin de bundler que suporte RSC. React Native não é afetado, a menos que um monorepo puxe os pacotes vulneráveis.

Repare no formato da correção: toda release corrigida é um bump de patch. Não há código seu no caminho vulnerável, e nada seu para reescrever.

Impacto

A exploração bem-sucedida dá a um atacante não autenticado execução arbitrária de comandos do sistema operacional como o usuário que roda o servidor Node.js — o suficiente para ler segredos e variáveis de ambiente, pivotar dentro da rede ou tomar o host.

O vetor CVSS v3.1 atribuído pela Facebook, Inc. é AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H — 10.0. O detalhe que vale ler ali é o S:C (Scope: Changed): o componente comprometido é o runtime do RSC, mas o impacto cai sobre o host inteiro. Essa única métrica é a diferença entre o 9.8 que essa classe de bug costuma tirar e um 10.0 redondo.

Isso não ficou teórico por muito tempo:

DataEvento
03/12/2025Divulgação, patches publicados, advisory GHSA-fv66-9v8q-g76r
05/12/2025Adicionada ao catálogo CISA Known Exploited Vulnerabilities
12/12/2025Prazo da CISA para remediação em agências federais (BOD 22-01)

A CISA marca com knownRansomwareCampaignUse: Known, e a AWS reportou grupos de ameaça ligados à China explorando a falha em ambiente real. A própria orientação da CISA é "procurar sinais de comprometimento em todas as instâncias REACT acessíveis pela internet depois de aplicar as mitigações" — aplicar o patch, sozinho, não responde se você já foi atingido.

Prova de conceito

O exploit monta o payload, envia como FormData multipart para a server action vulnerável e devolve a saída do comando. O fluxo ponta a ponta:

  1. Montagem — junta o gadget JSON (then / status / _response + referências ao construtor de FormData) com o comando fornecido pelo operador.
  2. Envio — faz o POST como FormData multipart para o endpoint RSC vulnerável.
  3. Resolução — o desserializador passa pela guarda de propriedade própria ausente e alcança o gadget de execução.
  4. Execução — o comando roda no servidor e a saída volta na resposta.
exploit.py — uso
# python3 exploit.py -c "<comando>"   roda um único comando
# python3 exploit.py -i               shell interativo
# python3 exploit.py -c "whoami" -v   verboso (mostra payload/resposta brutos)

Rodando o lab

Pré-requisitos: Node.js com npm, e Python 3 com a biblioteca requests.

O lab fixa o Next.js 15.1.3 exatamente — uma instalação sem pin resolveria para um runtime RSC corrigido e o exploit simplesmente pararia de funcionar. Mantenha o pin.

1. Suba a aplicação vulnerável:

terminal 1
npm install
pip3 install requests
npm run dev

2. Dispare um único comando em um segundo terminal:

terminal 2
python3 exploit.py -c "whoami"

Comandos úteis para confirmar a execução: pwd, ls -la, cat <arquivo> e env para enumerar variáveis de ambiente (um lugar comum para segredos vazados).

3. Entre em um shell interativo para rodar comandos em sequência:

python3 exploit.py -i

4. Inspecione o formato na rede com o modo verboso, quando quiser ver o payload e a resposta brutos:

python3 exploit.py -c "whoami" -v

Referências