Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

FeedsContatoPrivacidade© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-94545- — PoC em Python para CVE-2026-94545: explora entrada SVG não escapada no next/og do Next.js para acionar corrupção do libxml2 e uma cadeia ROP para RCE não autenticado. | Kitploit
Ferramentas/GitHubGitHub/mrdark-ops/cve-2026-94545-
Geração de PayloadsAnálise de VulnerabilidadesExploraçãoShellcodeExploração de Aplicações WebSegurança WebExploração de Binários
GitHubmrdark-ops/cve-2026-94545-

CVE-2026-94545-

PoC em Python para CVE-2026-94545: explora entrada SVG não escapada no next/og do Next.js para acionar corrupção do libxml2 e uma cadeia ROP para RCE não autenticado.

Ver Repositório
11há 1 mêsAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2026-94545: Execução Remota de Código Não Autenticada no next/og do Next.js

Apenas para uso em pesquisa de segurança, em ambientes de vulnerabilidade autorizados.

ItemValor
CVECVE-2026-94545 / GHSA-vcvr-r3jv-pc5j
UpstreamSatori GHSA-wx4j-mvgx-mqwp
Causa RaizCWE-116 (Codificação ou Escape de Saída Impróprios) → Corrupção de memória do parser XML nativo → RCE
AfetadoNext.js 16.2.0 – 16.3.5 (runtime Node com sharp instalado); Satori empacotado ≥ 0.0.27 e < 0.33.5
CorrigidoNext.js 16.3.6 / Satori 0.33.5
ImpactoExecutar comandos arbitrários no processo do serviço Next.js sem autenticação
Stack TestadaNext.js 16.3.5 + sharp 0.35.4 (libvips 8.18.6, librsvg 2.62.91, libxml2 2.15.3) + binário oficial x64 do Node.js v24.20.0 (non-PIE)

Com o runtime Edge (export const runtime = 'edge') ou quando o sharp não está instalado, a renderização passa pelo sandbox resvg-wasm, portanto esta vulnerabilidade não alcança a cadeia de parsing nativa e, consequentemente, não se torna RCE.

Cadeia de Vulnerabilidade

A entrada do usuário é concatenada no SVG como texto simples, e esse SVG é então inserido inteiro em uma URL data: e entregue a um parser XML em linguagem C para uma segunda rodada de parsing. Entre esses passos, apenas a codificação de URL é realizada; o escape XML nunca é feito.

Camada 1: App Router — Entrada Não Confiável Entra na Função de Renderização

Padrão típico: POST /api/og coloca todo o corpo da requisição, ou GET /api/og?value= coloca o parâmetro de consulta, diretamente no <title> do SVG de ImageResponse, sem filtragem ou escape.

import { ImageResponse } from 'next/og'

export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'

async function render(request) {
  const url = new URL(request.url)
  const value = request.method === 'POST'
    ? await request.text()
    : (url.searchParams.get('value') ?? '')
  return new ImageResponse(
    <svg width="1200" height="630"><title>{value}</title></svg>,
    { width: 1200, height: 630 }
  )
}

export const GET = render
export const POST = render

ImageResponse recebe uma árvore de elementos JSX e retorna um PNG. Qualquer rota que grave dados da requisição em nós filhos / atributos / estilos de SVG é um sink.

Camada 2: Encaminhamento de Entrada do next/og

node_modules/next/og.js encaminha para next/dist/server/og/image-response.js. O runtime Node carrega dinamicamente next/dist/compiled/@vercel/og/index.node.js (caminho nativo); o runtime Edge carrega o caminho wasm e não é afetado por esta vulnerabilidade.

Camada 3: @vercel/og — O Satori Gera o SVG e Depois o Entrega para Rasterização

O fluxo do @vercel/og empacotado no Next.js (comentários do artefato compilado confirmam que ele contém [email protected]) é:

  1. Usar o Satori para serializar a árvore JSX em uma string SVG.
  2. Se sharp for detectado, entregar o SVG à biblioteca nativa para rasterização; caso contrário, recorrer ao resvg-wasm.
const svg = await satori2(element, { width, height, fonts, ... })
if (sharp) {
  pngBuffer = await sharp(new TextEncoder().encode(svg)).resize(width).png().toBuffer()
} else {
  const resvgJS = new resvg.Resvg(svg, { fitTo: { mode: ... } })
}

Neste ponto, a entrada do usuário já se tornou uma string SVG e é passada literalmente para o sharp (libvips nativo).

Camada 4: Satori 0.25.0 — O texto é apenas codificado em URL, nunca escapado em XML

Para um <svg> escrito explicitamente em JSX, o Satori o serializa inteiro em uma URL data:image/svg+xml e depois o incorpora à saída como um <image>.

vercel/[email protected] src/layout.ts: chama SVGNodeToImage ao entrar em um nó <svg>.

src/handler/preprocess.ts tem duas falhas principais:

const SVGSymbols = /[\r\n%#()<>?[\\\]^`{|}"']/g   // Only used for URI percent-encoding

export function translateSVGNodeToSVGString(node, inheritedColor) {
  if (typeof node !== 'object') return String(node)  // Flaw 1: string child nodes are returned as-is, with no XML entity escaping
  return `<${type}${attrs}${styles}>${translateSVGNodeToSVGString(children, currentColor)}</${type}>`
}

export async function SVGNodeToImage(element, inheritedColor) {
  return `data:image/svg+xml;utf8,${`<svg ...>...</svg>`.replace(SVGSymbols, encodeURIComponent)}`
  // Flaw 2: only URI percent-encoding is performed, not XML escaping
}

&, < e > não são escritos como &amp; / &lt; / &gt;. A codificação percentual é apenas uma exigência de sintaxe de URL; quando o parser downstream carrega a URL data:, %3C/title%3E é restaurado para um </title> real, e o texto do atacante transita de "dados" de volta para "estrutura do documento".

Quando a entrada é </title><xi:include …/><title>, o SVG intermediário entregue pelo Satori ao sharp assume a seguinte forma (decodificado para ilustração):

<svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
  <image x="0" y="0" width="1200" height="630"
     href="data:image/svg+xml;utf8,...<title></title><xi:include
           xmlns:xi='http://www.w3.org/2001/XInclude'
           href='data:image/svg+xml,...' parse='xml'/><title></title>..." />
</svg>

Camada 5: sharp → libvips → librsvg → libxml2 — Análise Nativa e Corrupção de Memória

sharp(SVG buffer)
 → libvips  vips_foreign_load_svg_buffer()
 → librsvg  rsvg_handle_new_from_stream_with_flags()
              ├─ Parses the outer SVG
              ├─ When it encounters <image href="data:image/svg+xml,...">, loads and re-parses the sub-document
              └─ XML parsing is delegated to libxml2
 → libxml2  xmlStringLenDecodeEntities / xmlXIncludeProcessNode / xmlXIncludeLoadDoc

A carga útil usa duas camadas de injeção para controlar a estrutura de entrada do libxml2:

  1. O </title><xi:include parse="xml" href="data:…"> externo faz o parser saltar para o carregamento de um documento aninhado totalmente controlado pelo atacante.
  2. Dentro do documento aninhado, entidades DTD aninhadas + XInclude são então usadas para construir um grafo de expansão e referência de entidades excessivamente profundo / amplo.

O estado interno do libxml2 é levado além do esperado, resultando em uma escrita fora dos limites. O efeito de corrupção é atingido de forma confiável por meio de sequências de coordenadas <path> de SVG cuidadosamente organizadas (doubles IEEE-754 carregando palavras little-endian de 8 bytes), sequestrando assim o fluxo de controle.

Camada 6: ROP de endereço fixo em Node não-PIE → execve

O binário linux-x64 oficialmente lançado do Node.js é um executável não-PIE com base de carregamento fixa; os endereços de PLT / GOT e gadgets são constantes, portanto nenhum vazamento de informação é necessário.

Este PoC é construído para o Node.js v24.20.0 oficial x64; os gadgets são os seguintes:

SímboloEndereçoFinalidade
POP_RDI0xA1EAEDpop rdi ; ret
POP_RAX0x7F38D4pop rax ; ret
POP_RSI0x95823Dpop rsi ; ret
POP_RDX0x776758pop rdx ; ret
STOSQ_RET0xD16725stosq ; ret (escreve no scratch qword por qword)
EXECVE_PLT0x745FA0stub PLT de execve
SCRATCH0x6B5F500Endereço gravável fixo
Baixar ferramenta