
RCE pré-autenticação em React Server Components versões 19.0.0, 19.1.0, 19.1.1 e 19.2.0.
Este repositório contém uma reprodução (PoC) do CVE-2025-55182, uma vulnerabilidade crítica de segurança nos React Server Components (RSC) que permite execução arbitrária de código não autenticado.
A vulnerabilidade existe na forma como os React Server Components desserializam "Server Actions" de requisições de clientes. Especificamente, a função requireModule não validava se o nome da exportação solicitado era uma propriedade direta do módulo. Isso permitia que atacantes acessassem a propriedade constructor de funções exportadas, obtendo uma referência ao construtor global Function, que pode ser usado para executar código arbitrário.
Esta PoC utiliza um ambiente Node.js mínimo para isolar a vulnerabilidade na biblioteca react-server-dom-webpack, apenas para garantir que o exploit demonstre o bug na própria biblioteca, NÃO uma má configuração em um framework.
npmnpm install
[!NOTE] O
package.jsonestá fixado na versão vulnerável19.0.0.
Este script configura um servidor HTTP bruto que utiliza o runtime React vulnerável para decodificar requisições.
# tty1
node --conditions react-server server.js
Em um terminal separado, execute o exploit. Isto envia um payload Flight malicioso para o servidor.
# tty2
node exploit.js id
Você deve ver a saída do comando retornada na resposta:
Saída Esperada:
Response: uid=0(root) gid=0(root) groups=0(root)
Por que a vulnerabilidade aconteceu?
A função requireModule em ReactFlightDOMServerNode.js basicamente confiava em qualquer name que o cliente enviava. Ela fazia moduleExports[metadata[NAME]] sem verificar se aquela propriedade era realmente destinada a ser exposta. Então, se o cliente dissesse "bro, quero esta propriedade", o servidor apenas respondia "claro! aqui está, amigo".
Por que permitir que as pessoas acessem qualquer propriedade é uma má ideia?
Porque basicamente permite que qualquer pessoa alcance a cadeia de protótipos, até mesmo o constructor, o que é super perigoso. Se o módulo exporta uma função (como module.exports = () => {}), então seu constructor é literalmente o construtor global Function.
Por que obter o construtor Function significa RCE?
Uma vez que um invasor obtém o construtor Function, ele pode abusar do recurso "Bound Server Action". Ele associa uma string contendo JavaScript malicioso a ele (basicamente transformando-o em new Function("código malicioso")). E quando isso é executado, o servidor executa qualquer código que eles colocarem.
Por que o React realmente executaria essa função maliciosa?
Porque Server Actions podem ser acionadas por ID. Se o invasor cria um payload com um Action ID que aponta para a referência module#constructor deles, o React o resolve como uma ação normal e o executa. Essa "ação" é na verdade a função maliciosa deles.
Por que nada disso foi validado?
O sistema simplesmente assumia que id e name dos metadados da Server Reference sempre se refeririam a exportações válidas definidas pelo desenvolvedor. Não havia nenhuma verificação de segurança como hasOwnProperty para garantir que a propriedade solicitada era uma exportação real e não algo herdado da cadeia de protótipos.
server.js em vez de Next.js?Eu uso um server.js bruto (e um auxiliar webpack-runtime.js) para configurar manualmente o runtime React Server Components. Isso nos permite:
module.exports = fn). Um bundler real pode alterar como as exportações são encapsuladas, dependendo de sua configuração.react-server-dom-webpack, não no Next.js.react-server-dom-webpack assume que está executando dentro de um bundle Webpack. Nosso webpack-runtime.js fornece os globais que ele espera (__webpack_require__, __webpack_chunk_load__).
Isso não é mockar a vulnerabilidade, é apenas dar à biblioteca o runtime mínimo necessário para funcionar.Houve discussão sobre "PoCs inválidos" que só funcionam se o desenvolvedor expõe propositalmente coisas perigosas como child_process.exec.
Esta PoC não é uma dessas. Ela funciona em uma configuração normal e segura.
A função exposta é inofensiva
O aplicativo expõe uma simples função updateProfile que apenas retorna uma string e nada suspeito, sem comandos de shell.
O exploit foge completamente dessa função
A vulnerabilidade permite que o atacante ignore a exportação segura e vá direto para updateProfile.constructor, que é o construtor global Function.
O problema central é o acesso à propriedade
O React NÃO deveria ter permitido o acesso a .constructor. O desenvolvedor não pretendia expor o construtor Function, em vez disso, a desserialização insegura fez isso por ele.
O único requisito real é que o módulo exporte uma função diretamente (module.exports = fn), o que é super comum em CommonJS e muitas configurações de bundler.
O payload em exploit.js constrói uma mensagem Flight do React com três chunks:
id: "user-profile-action#constructor", significando "me dê o construtor".bound: aponta para o Chunk 2, que contém os argumentos.["console.log('nice try, diddy!')"]: a string de código malicioso.Quando o React desserializa isso:
user-profile-action..constructor => obtém o Function global.new Function("console.log('nice try, diddy!')")E isso é a RCE!
Atualize imediatamente para as versões corrigidas:
react-server-dom-webpack >= 19.0.1react-server-dom-parcel >= 19.0.1react-server-dom-turbopack >= 19.0.1O patch introduz verificações hasOwnProperty para impedir o acesso a propriedades herdadas e restringe uploads de arquivos base64.
Se você executar esta PoC contra uma versão corrigida, o servidor irá travar ou gerar erro com:
$ node --conditions react-server server.js
Listening on http://localhost:3000
/path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726
resolvedValue = resolvedValue.bind.apply(
^
TypeError: Cannot read properties of undefined (reading 'bind')
at /path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726:43
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v20.19.3
Isso confirma que o exploit falhou ao acessar a propriedade constructor (ela retornou undefined em vez de Function), e portanto a chamada subsequente .bind falhou.
Este código é apenas para fins educacionais e de teste. Não use este exploit contra sistemas que você não possui ou não tem permissão explícita para testar.
Lançado sob DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE.