Voltar à Buglist
BUGLIST / Next.js

CVE-2026-75604

9.0 / CríticaCorrigida

Path traversal no cache ISR do Next.js contorna o Proxy e permite RCE no Windows

Uma divergência entre a rota autorizada e o caminho usado pelo FileSystemCache permitia ler artefatos protegidos e, em uma cadeia específica, executar código sem autenticação.

Next.js / Path traversalCVE-2026-75604
Componente
packages/next/src/server/lib/incremental-cache/file-system-cache.ts
Versões afetadas
Next.js >= 13.4 e < 15.5.24; >= 16.0 e < 16.3.3
Versões corrigidas
Next.js 15.5.24 e 16.3.3
Advisory oficial
GitHub Security Advisory

Resumo

A CVE-2026-75604 é uma vulnerabilidade de path traversal no cache em disco do Next.js em servidores Windows. Uma rota com ..%5C podia passar pelo Proxy como uma URL pública e, depois da decodificação, virar um caminho com ..\ dentro do FileSystemCache.

No Windows, a barra invertida é um separador de diretório. O cache podia então sair do diretório associado à rota pública e alcançar outro artefato em .next/server.

Essa primitiva sustentava duas cadeias:

  • um Proxy bypass, no qual uma requisição anônima lia HTML e RSC de uma página estática protegida;
  • uma cadeia entre App Router, Pages Router e Server Actions que expunha o server-reference-manifest.json e podia terminar em RCE sem autenticação.

O advisory oficial atribuiu CVSS 9.0 e classificou a complexidade como alta. A falha não atingia todo projeto Next.js no Windows: a cadeia completa dependia de uma combinação específica de routers, cache e Server Action.

Condições para a cadeia RCE

O impacto de RCE exigia:

  • servidor Next.js executado nativamente sobre um filesystem Windows;
  • Pages Router e App Router na mesma aplicação;
  • Cache Components desativado;
  • cache padrão em disco;
  • uma rota ISR dinâmica no Pages Router;
  • uma rota armazenada pelo App Router;
  • uma Server Action compatível que capturasse um valor externo em sua closure.

Linux e macOS não interpretam \ como separador de diretório. A mesma chave não produz o traversal nesses sistemas.

Causa raiz

O Proxy e o cache não tomavam a decisão sobre a mesma identidade de rota.

Considere esta requisição:

GET /posts/..%5Cpremium

O Proxy recebia o pathname ainda codificado e o tratava como uma instância da rota pública /posts/[slug]. Mais tarde, o processamento da rota chamava decodePathParams(). O %5C se tornava uma barra invertida antes de o valor chegar ao cache.

O FileSystemCache combinava esse pathname com o diretório base. No Windows, o resultado era normalizado pelo filesystem:

.next\server\app\posts\..\premium.html
=> .next\server\app\premium.html

Assim, a autorização havia sido feita sobre /posts/..%5Cpremium, enquanto a leitura ou gravação usava o artefato de /premium.

O patch corrigiu os dois lados do problema: passou a escapar barras invertidas durante a formação do caminho e adicionou uma verificação para impedir que o destino resolvido saísse da raiz permitida do cache.

Cadeia de Proxy bypass

O primeiro reporte de @rafabd1 demonstrou o problema com uma página /premium gerada no build e protegida por uma sessão no Proxy. A rota /posts/[slug] era pública e usava ISR.

O acesso anônimo direto era bloqueado:

GET /premium
HTTP/1.1 307 Temporary Redirect
Location: /login

A variante com traversal, porém, fazia o cache devolver o artefato já existente da página protegida:

GET /posts/..%5Cpremium
HTTP/1.1 200 OK

Nenhuma vítima precisava aquecer o cache. O conteúdo de /premium já existia porque a página havia sido prerenderizada durante o build.

Se a entrada estivesse stale, a mesma chave também podia iniciar uma revalidação em background e substituir o conteúdo protegido pela renderização da rota pública. A falha afetava, portanto, a confidencialidade do conteúdo e a integridade dos artefatos ISR.

Cadeia RCE entre os dois routers

A cadeia RCE usava a diferença entre os formatos de cache dos dois routers.

Pages Router: <chave>.html + <chave>.json
App Router:   <chave>.html + <chave>.rsc + <chave>.meta

O App Router podia ser levado a criar um arquivo com o nome-base server-reference-manifest dentro de .next/server. O manifesto privado original já existia no mesmo diretório:

.next/server/server-reference-manifest.html  <- criado via traversal
.next/server/server-reference-manifest.json  <- manifesto privado existente

Uma segunda requisição pelo Pages Router encontrava esse par .html e .json e o aceitava como uma entrada ISR válida. O JSON retornado ao cliente era, na verdade, o server-reference-manifest.json, que contém IDs de Server Actions e a encryptionKey usada para proteger valores capturados por closures.

O vazamento da chave não executa código por si só. A etapa final exige uma Server Action compatível, com um valor externo capturado. O atacante pode forjar o payload cifrado da closure para substituir a referência legítima pelo construtor JavaScript Function. Quando a action usa o valor capturado, o código fornecido pelo atacante roda no processo do servidor.

Essa dependência explica a métrica de alta complexidade do advisory. O exploit precisa reconhecer rotas adequadas, construir o alias entre os dois formatos de cache e encontrar uma action que ofereça a primitiva necessária.

PoC público

Após a publicação do advisory e do patch, @rafabd1 reconstruiu a cadeia completa e publicou um PoC parametrizado para CVE-2026-75604. O repositório inclui um alvo local em Next.js 16.2.11 e permite informar as rotas de cache, o caminho da Server Action e o comando usado na validação.

PoC da CVE-2026-75604 executando um comando em um servidor Next.js no Windows

A demonstração acima usa o mesmo PoC público. Ele deve ser executado somente em sistemas próprios ou com autorização explícita.

Versões afetadas e correção

O advisory lista estes intervalos:

Next.js >= 13.4 e < 15.5.24
Next.js >= 16.0 e < 16.3.3

As versões corrigidas são 15.5.24 e 16.3.3. Não há workaround geral para aplicações afetadas hospedadas no Windows. A recomendação é atualizar e refazer o deploy:

npm install next@15.5.24

ou:

npm install next@16.3.3

Também é importante verificar o sistema operacional do processo que serve a aplicação. O ambiente usado para desenvolver ou gerar o build não determina o filesystem do servidor em produção.

Timeline e atribuição

  • 21 de julho de 2026, 21:19 UTC: @rafabd1 reportou à Vercel o path traversal no FileSystemCache e demonstrou a cadeia de Proxy bypass.
  • 21 de julho de 2026, 21:54 UTC: um segundo pesquisador reportou a cadeia de RCE baseada na mesma primitiva, cerca de 35 minutos depois.
  • 26 de julho de 2026: @rafabd1 enviou detalhes adicionais sobre o cenário de ataque e o impacto do bypass.
  • 25 de agosto de 2026: a Vercel publicou a GHSA-p293-qw3h-jr36, a CVE-2026-75604 e as versões corrigidas.

Uma explicação mais extensa da cadeia está disponível no writeup original.