A CVE-2025-55182 é uma vulnerabilidade crítica de execução remota de código pré-autenticação nos React Server , com uma pontuação CVSS de 10,0 — a classificação de gravidade mais elevada possível. No âmbito do Programa de BolsasOPSWAT , os nossos bolseiros realizaram uma análise técnica abrangente desta vulnerabilidade, examinando a sua causa principal no protocolo de deserialização do React Flight, a cadeia de exploração completa e o seu impacto generalizado no ecossistema web moderno. Este blogue apresenta as nossas conclusões, juntamente com orientações práticas para os defensores.

O React tornou-se uma das bibliotecas front-end mais utilizadas em todo o mundo, estando na base de uma parte significativa das mobile web e mobile modernas. Os inquéritos aos programadores do Stack Overflow classificam consistentemente o React entre as principais estruturas web, com uma adoção que ultrapassa os 40% dos programadores profissionais a nível global. Paralelamente a este crescimento, a equipa do React introduziu Server React Server (RSC) como uma funcionalidade central do React 19 — uma mudança de paradigma que transfere a lógica de renderização do cliente para o servidor, permitindo um desempenho otimizado e uma integração mais estreita entre o código do lado do servidor e do lado do cliente.
No entanto, esta evolução arquitetónica introduziu uma nova superfície de ataque crítica. A 29 de novembro de 2025, o investigador de segurança Lachlan Davidson comunicou uma vulnerabilidade na lógica de deserialização do lado do servidor do React ao programa Bug Bounty da Meta. Divulgada publicamente a 3 de dezembro de 2025, com o código CVE-2025-55182, a falha permite a execução remota de código sem autenticação através de uma única solicitação HTTP manipulada. Classificada como CWE-502 (Deserialização de Dados Não Confiáveis), a vulnerabilidade não requer autenticação, interação do utilizador nem configuração especial da aplicação — uma implementação padrão do tipo «create-next-app» criada para produção é imediatamente explorável.

O impacto foi imediato e grave. Em menos de 48 horas após a divulgação pública, foram observadas várias campanhas de exploração em circulação. De acordo com a The Shadowserver Foundation, foram identificados mais de 77 000 endereços IP públicos como potencialmente vulneráveis. A telemetria da Cloudflare registou mais de 582 milhões de tentativas de exploração na semana seguinte à divulgação, com uma intensidade de ataque média superior a 3.500 IPs de origem únicos por hora e um pico de 16.585 IPs de ataque simultâneos. A Wiz Research informou que 39% dos ambientes na nuvem continham instâncias vulneráveis. A CISA adicionou a vulnerabilidade ao seu catálogo de Vulnerabilidades Exploradas Conhecidas (KEV) a 5 de dezembro de 2025.
Os autores das ameaças agiram com notável rapidez e diversidade. A Trend Micro documentou várias campanhas — incluindo as campanhas das botnets «emerald» e «nuts» — que utilizaram beacons do Cobalt Strike, implantes Sliver, o agente de monitorização Nezha, túneis Fast Reverse Proxy (FRP) e uma nova carga útil denominada «Secret-Hunter», que aproveita ferramentas de código aberto para a recolha de credenciais, como o TruffleHog e o Gitleaks. Threat Intelligence Google Threat Intelligence identificou grupos de ameaças distintos ligados à China (UNC6600, UNC6586, UNC6588, UNC6603) a utilizar ferramentas especializadas — incluindo o tunelador MINOCAT, o downloader SNOWLIGHT, o backdoor COMPOOD e o backdoor HISONIC — a par de agentes ligados ao Irão e de grupos com motivações financeiras que conduzem campanhas de mineração de criptomoedas. A AWS documentou grupos ligados à China a experimentar código de exploração já a 4 de dezembro, antes de o código de prova de conceito completo estar disponível publicamente.
Sobre Server React Server
O React é uma biblioteca JavaScript para a criação de interfaces de utilizador, mantida pela Meta e por uma vasta comunidade de código aberto. Server React Server (RSC), introduzidos com o React 19, representam uma mudança fundamental na forma como as aplicações React lidam com a renderização. Ao contrário dos componentes de cliente tradicionais, que são executados inteiramente no navegador, os componentes de servidor são executados no servidor, produzindo uma representação serializada da interface do utilizador que é transmitida para o cliente. Este design reduz a quantidade de JavaScript enviada para o navegador, melhora as métricas de tempo de interação e permite o acesso direto a recursos do lado do servidor, tais como bases de dados e sistemas de ficheiros.

O RSC baseia-se num protocolo de serialização personalizado denominado «Flight» para codificar e transmitir dados entre o cliente e o servidor. Quando um cliente invoca uma Server (anteriormente conhecida como Server ), o navegador agrupa os argumentos da função numa solicitação HTTP estruturada, utilizando o formato Flight. O servidor deserializa esta carga útil, executa a função solicitada e transmite o resultado de volta ao cliente. Esta forte interdependência entre cliente e servidor (embora arquitetonicamente elegante) significa que qualquer falha na lógica de deserialização pode ter consequências imediatas e catastróficas, como demonstra o CVE-2025-55182.
A vulnerabilidade afeta não só o próprio React, mas todo o ecossistema de frameworks construídos com base nele. O Next.js (que recebeu um aviso separado, CVE-2025-66478, posteriormente rejeitado por ser uma duplicata), o React Router, o Waku, o plugin RSC do Parcel, o plugin RSC do Vite e o RedwoodSDK são todos afetados. Mesmo as aplicações que não definem explicitamente Server podem estar vulneráveis se o suporte a RSC estiver ativado no framework.
Contexto técnico
Antes de analisarmos a vulnerabilidade, é importante referir três conceitos fundamentais que estão na base da cadeia de exploração: o comportamento do comando `await` do JavaScript com objetos `thenable`, a percussão da cadeia de protótipos e o modelo de dados baseado em blocos do protocolo React Flight.
O `await` e os objetos `Thenable` do JavaScript
O operador `await` suspende a execução de uma função assíncrona até que a expressão aguardada seja resolvida. Quando o `await` encontra uma Promise nativa, aguarda a resolução e devolve o valor cumprido. No entanto, o `await` não requer uma Promise nativa — qualquer objeto com um método `.then()`, conhecido como «thenable», é tratado como uma construção semelhante a uma Promise.
Quando o `await` encontra um objeto `thenable`, invoca o método `.then()` desse objeto, passando as callbacks `resolve` e `reject` fornecidas pelo sistema. O valor passado para `resolve` torna-se o resultado da expressão `await`. Fundamentalmente, se o valor resolvido for ele próprio um thenable, o método .then() desse objeto aninhado é chamado recursivamente até que se chegue a um valor primitivo ou a uma Promise resolvida. Este comportamento de resolução recursiva é fundamental para a exploração da vulnerabilidade CVE-2025-55182.
Percurso da cadeia de protótipos
Todos os objetos JavaScript mantêm uma ligação interna ao seu protótipo, acessível através da propriedade __proto__. Quando se acede a uma propriedade num objeto, o motor JavaScript verifica primeiro as propriedades do próprio objeto. Se a propriedade não for encontrada, o motor percorre a cadeia de protótipos — subindo por cada ligação __proto__ — até que a propriedade seja encontrada ou a cadeia termine em undefined.
Um atacante pode explorar este mecanismo de herança para aceder a propriedades fora do âmbito pretendido de um objeto. Ao incluir __proto__ nos caminhos de acesso às propriedades, um atacante pode aceder a métodos internos e construtores que a aplicação nunca pretendia expor. Em JavaScript, a expressão obj.__proto__.constructor.constructor resulta no construtor global Function, que pode criar e executar funções arbitrárias a partir de uma entrada de cadeia de caracteres.
O React Flight Protocol e o modelo de dados baseado em blocos
When a client invokes a Server Function, the browser sends an HTTP POST request with a multipart/form-data body. Each form field contains a numbered “chunk” of serialized data. The Flight protocol uses special string prefixes to encode data types: $<id> references the resolved value of another chunk, $@<id> references the raw chunk object itself, $W<id> represents a Set, $K<id> represents FormData, and $B<id> triggers the blob handler.
Considere uma Server definida da seguinte forma:

O pedido HTTP correspondente contém vários campos de formulário, cada um composto por uma chave e um valor: o campo 0 contém a matriz de argumentos com referências como "$W1" e "$K2", enquanto os campos 1 e 2_* contêm os dados a que essas referências remetem. O servidor processa cada campo à medida que este chega, armazenando os resultados intermédios em objetos denominados «chunks».

Um chunk é um objeto thenable com quatro propriedades principais: status (o estado de resolução), value (os dados armazenados), reason (informações sobre o erro) e _response (uma referência ao objeto de resposta pai). Quando o servidor encontra o chunk await, o método .then() do chunk é invocado. Se o status do chunk for INITIALIZED, a callback de resolução recebe chunk.value. Se o status for PENDING, BLOCKED ou CYCLIC, as callbacks são enfileiradas para execução posterior.

O chunk 0 representa normalmente a matriz de argumentos da Server invocada. Depois de todos os campos do formulário terem sido recebidos e todas as referências internas terem sido resolvidas, chunk_0.value contém a matriz de argumentos totalmente montada, que é então passada para a função de destino.
Tratamento de pedidos de ponta a ponta (Next.js → Deserialização do React Flight)
O texto a seguir descreve como o Next.js processa um pedido Server recebido em condições normais, desde a camada HTTP até ao motor de deserialização do React Flight.

Função handleAction() - Next.js
Quando uma Server é invocada, o pedido entra na função `handleAction`. Esta função valida os metadados, verifica os cabeçalhos e os tokens CSRF e confirma se o pedido é uma ação de recuperação válida. É então criado um fluxo chamado busboyStream para analisar o corpo do formulário multipart. A função decodeReplyFromBusboy vincula emissores de eventos a este fluxo, acionando as funções de tratamento de deserialização ServerReact Serverà medida que os dados brutos são recebidos. O valor de retorno de decodeReplyFromBusboy é chunk_0; o operador await resolve-o e passa o seu valor montado para a Server invocada.

A função getChunk devolve o chunk correspondente a um determinado ID. Se esse chunk ainda não existir, cria um ResolvedModelChunk (se já houver dados em response._formData) ou um PendingChunk (se ainda não tiverem chegado dados para esse ID).

Quando a função `decodeReplyFromBusboy` devolve `chunk_0`, o bloco continua no estado PENDING. O operador `await` chama `chunk_0.then()` e armazena temporariamente as callbacks `resolve` e `reject` em `chunk_0.value` e `chunk_0.reason`. Estas callbacks são reativadas pela função `wakeChunk` assim que a resolução da referência estiver concluída.

Processo de deserialização - React Server
Quando o busboyStream recebe um campo de dados brutos completo, aciona o emissor de eventos «field», invoca a função resolveField e inicia a deserialização — convertendo os dados brutos do formulário em objetos JavaScript totalmente construídos. As seguintes funções controlam este processo.
resolveField(resposta, chave, valor)

A chave e o valor são anexados a response._formData. A função recupera então o fragmento correspondente ao ID que coincide com a chave. Se esse fragmento já existir, a função resolveModelChunk é chamada para o reconstruir. Esta resolução diferida é necessária porque um valor pode conter referências a campos cujos dados brutos ainda não chegaram; nesse caso, o React Server um PendingChunk com callbacks personalizados de resolução e rejeição para tratar essas referências posteriormente.
resolveModelChunk(chunk, valor, id)

resolveModelChunk cria um ResolvedModelChunk com o estado RESOLVED_MODEL e injeta os dados brutos. Em seguida, reconstrói o chunk através de initializeModelChunk e chama wakeChunk para acionar quaisquer callbacks de resolução e rejeição em fila, concluindo a resolução do objeto ou da referência.
initializeModelChunk(chunk)

A função `initializeModelChunk` altera o estado do chunk para `CYCLIC` — indicando que a resolução de referências está em curso — e inicia a deserialização. Ela constrói um objeto JavaScript bruto a partir de `chunk.value` utilizando `JSON.parse` e, em seguida, passa esse objeto para a função `reviveModel`.
reviveModel(resposta, objetoPai, chavePai, valor, referência)

O reviveModel processa recursivamente cada componente do objeto analisado. Quando encontra um valor de cadeia de caracteres, chama a função parseModelString para o processar.
parseModelString(resposta, objeto, chave, valor, referência)

A função `parseModelString` analisa o prefixo da string para lidar com diferentes tipos de codificação. No caso de referências que começam por $, a função `getOutlinedModel` é chamada para resolver a referência entre blocos.
getOutlinedModel(resposta, referência, objetoPai, chave, mapa)

O getOutlinedModel divide a referência no delimitador «:» para formar um caminho de acesso à propriedade e, em seguida, percorre esse caminho no objeto chunk de destino para devolver o valor referenciado. Conforme detalhado na Análise de Vulnerabilidades abaixo, a ausência de validação destes nomes de propriedades é precisamente o ponto onde reside a vulnerabilidade.
Análise de vulnerabilidade
Causa principal
CVE-2025-55182 originates from insufficient input validation in the getOutlinedModel() function within React’s server-side Flight reply handler (ReactFlightReplyServer.js). When a chunk reference includes a property path - such as $<id>:<prop1>:<prop2> - the function resolves it by traversing the specified properties on the target chunk object, computing the result as chunk[prop1][prop2].

A falha crítica reside no facto de estes nomes de propriedades nunca serem validados. O servidor não verifica se as propriedades solicitadas são propriedades próprias do objeto ou propriedades herdadas do protótipo. Um invasor pode, portanto, incluir __proto__ no caminho da propriedade para percorrer a cadeia de protótipos e alcançar métodos internos que nunca deveriam ser acessíveis a partir de entradas controladas pelo utilizador. Por exemplo, a referência $1:__proto__:then resolve-se em Chunk.prototype.then — uma função que o invasor pode então invocar com argumentos controlados.

Código vulnerável
A cadeia de exploração aproveita dois percursos de código distintos na lógica de deserialização Flight do React.
O primeiro é o Chunk.prototype.then, que determina o comportamento dos chunks enquanto thenables. Quando o operador await é aplicado a um chunk no estado INITIALIZED, é chamada a função resolve(chunk.value). Se chunk.value for, por sua vez, um thenable (um objeto com um método .then() ), o operador await invoca recursivamente chunk.value.then(). Esta resolução recursiva é o mecanismo através do qual um atacante redireciona a execução para uma função arbitrária.
O segundo é o manipulador do prefixo $B (blob) dentro da função parseModelString():

No caso $B, a função chama response._formData.get(response._prefix + id). Tanto _formData.get como _prefix são propriedades do objeto _response armazenado no interior do chunk. Ao controlar estas propriedades através da percussão da cadeia de protótipos, um atacante pode redirecionar esta chamada para invocar o construtor global Function com código arbitrário como argumento.
Exploração
Through prototype chain traversal, an attacker reaches the global Function constructor via the path <any_object>.constructor.constructor. Because Chunk.prototype.then is a function, the path $1:constructor:constructor resolves to the global Function constructor, which accepts a string and returns a callable function containing that code.

Para demonstrar o potencial impacto no mundo real, os nossos investigadores criaram uma carga útil de prova de conceito em consonância com a análise documentada de forma independente por várias equipas de investigação em segurança. A exploração funciona em duas fases:
Fase 1 - Construir o fragmento falso:
The object delivered in field 0 acts as a fake chunk. Its then property is set to Chunk.prototype.then via the reference path $1:__proto__:then, allowing the Flight deserialization engine to invoke prototype-level behavior on this attacker-constructed object. The _response._formData.get property is pointed at the global Function constructor via $1:constructor:constructor, and _response._prefix is set to the malicious JavaScript code. The value field contains the string {"then": "$B0"}, instructing the blob handler to invoke itself on the same chunk when resolved. The status field is set to resolved_model so that initializeModelChunk is triggered when .then() is called, causing value to be parsed and the blob handler to fire.
Como o campo 1 ainda não foi recebido nesta altura, o React Server cria Server callbacks de resolução e rejeição para tratar da referência pendente.
Fase 2 - Resolução do gatilho:
Assim que o campo 1 — que contém "$@0", uma referência bruta ao chunk 0 — é entregue, o chunk pendente é resolvido e aponta diretamente para o chunk falso. Isto aciona o wakeChunk, que processa as callbacks em fila e inicia a percussão da cadeia de protótipos durante a resolução da referência. Assim que o chunk falso estiver totalmente resolvido, o wakeChunk é chamado novamente. Como o callback de resolução para o chunk falso é a função de resolução implícita do Node.js, este invoca o método .then() do chunk e resolve o seu valor — acabando por construir e executar o código malicioso injetado através do construtor Function.
A exploração completa requer apenas uma única solicitação HTTP:

Replacing {{COMMAND}} with any JavaScript code executes it on the server. The reason: -1 field prevents a toString() error during processing. The Next-Action header may contain any arbitrary value - even x - because the vulnerable deserialization occurs before the server validates the requested Server Function. This is what makes the vulnerability pre-authentication: the payload is processed during the deserialization phase, before any application-level authentication or authorization logic is reached.
Caso a exploração seja bem-sucedida, um atacante obtém acesso total ao contexto de execução do Node.js no servidor, incluindo acesso ao `child_process` para a execução de comandos de shell, variáveis de ambiente que contêm credenciais de bases de dados e API , o sistema de ficheiros local e pontos de acesso de metadados na nuvem que permitem o movimento lateral.
Prova de Conceito
Os nossos colegas reproduziram a vulnerabilidade num ambiente de laboratório controlado, utilizando uma aplicação Next.js padrão gerada com o create-next-app e preparada para produção — sem quaisquer alterações à configuração predefinida. A reprodução confirmou que a carga útil de exploração de uma única solicitação descrita acima permite a execução remota de código de forma fiável.


A demonstração controlada revelou que um atacante com acesso à rede de um servidor Next.js vulnerável pode executar código Node.js arbitrário — incluindo a criação de um shell reverso através de `child_process.exec()`, a leitura de variáveis de ambiente e o acesso ao sistema de ficheiros local — sem fornecer quaisquer credenciais nem acionar quaisquer verificações de autenticação ao nível da aplicação. O valor arbitrário aceite para o cabeçalho Next-Action confirma ainda mais a natureza pré-autenticação da falha: o servidor processa e deserializa a carga útil antes de realizar qualquer pesquisa de ação ou verificação de autorização.
Mitigação
A equipa do React lançou correções a 3 de dezembro de 2025 — no mesmo dia em que a vulnerabilidade foi divulgada publicamente. As versões corrigidas estão disponíveis como React 19.0.1, 19.1.2 e 19.2.1. O patch adiciona uma validação rigorosa de propriedades em getOutlinedModel() e reviveModel(), bloqueando explicitamente a resolução de propriedades de protótipo herdadas — incluindo __proto__, constructor e prototype — a partir de caminhos de referência controlados pelo utilizador em cargas úteis Flight.
As organizações devem tomar as seguintes medidas imediatas:
- Atualize os pacotes React para uma versão corrigida (19.0.1, 19.1.2 ou 19.2.1) executando o comando npm install react-server-dom-webpack@latest, react-server-dom-parcel@latest ou react-server-dom-turbopack@latest, conforme o caso.
- Atualizar as dependências das estruturas - O Next.js, o React Router, o Waku e outras estruturas afetadas lançaram as correções correspondentes. Consulte o comunicado da equipa do React para obter informações sobre os procedimentos de atualização específicos para cada versão.
- Não confie exclusivamente nas medidas de mitigação dos fornecedores de alojamento — embora fornecedores como a Vercel tenham implementado regras WAF temporárias após a divulgação, estas são medidas provisórias e não substituem a aplicação de correções aos pacotes subjacentes.
- Analise os registos do servidor para detetar pedidos POST com cabeçalhos Next-Action e corpos multipart/form-data que contenham os padrões $@ ou __proto__, e verifique se existem chamadas inesperadas de child_process ou execSync nos registos da aplicação.
Mitigação com OPSWAT
OPSWAT , uma tecnologia proprietária da plataforma MetaDefender™, oferece a visibilidade e o controlo necessários para proteger contra vulnerabilidades como a CVE-2025-55182. Uma vez que esta falha reside em pacotes npm de código aberto (react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack), as organizações devem primeiro estabelecer um inventário completo dos locais onde estes componentes estão implementados na sua infraestrutura, antes de ser possível uma correção eficaz.

OPSWAT gera um inventário abrangente de todos os componentes de software, bibliotecas, contentores e dependências em uso. Ao analisar aplicações ou imagens de contentores que incluem pacotes React vulneráveis, o sistema assinala automaticamente o CVE-2025-55182 como «Crítico» e fornece orientações sobre as versões corrigidas disponíveis, permitindo que as equipas de segurança estabeleçam prioridades e tomem medidas corretivas antes que ocorra qualquer exploração.
OPSWAT está disponível tanto no MetaDefender — para a análise de aplicações individuais e imagens de contentores — como MetaDefender Software Chain™ — para uma visibilidade ao nível do pipeline ao longo de todo o ciclo de vida do desenvolvimento. Em conjunto, permitem às equipas de segurança:
- Localize rapidamente os componentes vulneráveis - Identifique imediatamente quais as aplicações e os contentores que incluem os pacotes react-server-dom-* afetados em versões vulneráveis, garantindo que nenhuma implementação seja esquecida.
- Garantir a aplicação proativa de correções - Monitorizar continuamente as dependências de código aberto para detetar pacotes desatualizados ou inseguros à medida que são publicados novos alertas, reduzindo assim o período de exposição.
- Garantir a conformidade e a transparência da cadeia de abastecimento - Cumprir os requisitos regulamentares mantendo um registo auditável de todos os componentes de software e das suas vulnerabilidades conhecidas.
A rapidez e a dimensão da exploração da vulnerabilidade CVE-2025-55182 — mais de 582 milhões de tentativas só na primeira semana — evidenciam por que razão a aplicação reativa de correções já não é suficiente. Saber o que se tem, onde é executado e quando fica vulnerável é a base de uma defesa proativa. Essa visibilidade começa com a SBOM.
Referências
- Equipa React - Vulnerabilidade crítica de segurança nos Server do React
- Wiz Research - React2Shell (CVE-2025-55182): Vulnerabilidade crítica no React
- Trend Micro - CVE-2025-55182: Análise do React2Shell, PoC Chaos e Exploração em Ambiente Real
- Akamai - CVE-2025-55182: RCE por deserialização em Server do React e do Next.js
- Google Cloud Vários agentes maliciosos exploram a vulnerabilidade React2Shell (CVE-2025-55182)
- AWS - Grupos de ciberameaças ligados à China exploram rapidamente o React2Shell
- NVD - CVE-2025-55182
- Compreender Server do React - Tony Alicea
- MDN Web Docs - Operador `await`
- Código-fonte do React - ReactFlightReplyServer.js (v19.0.0)
- Código-fonte do Next.js - action-handler.ts (v16.0.0)
