
Documenta a cadeia de RCE use-after-free do VectorFreed no librsvg (CVE-2026-96889) com um PoC gerador de SVG e orientações de remediação para librsvg, Next.js e Satori.
Este documento apresenta o "VectorFreed", uma cadeia de vulnerabilidades que começa com um use-after-free que eu (@rafabd1) encontrei no librsvg (CVE-2026-96889). Em uma build de Node.js/Sharp/libvips, esse bug levou à execução de comandos. Ele aborda o bug, os caminhos confirmados em aplicações até agora e as correções disponíveis no momento.
Um SVG pode incluir outro SVG por meio de XInclude. No caminho vulnerável, o librsvg começa a analisar um documento incluído enquanto o libxml2 ainda está expandindo uma entidade no documento externo. Se o SVG incluído declarar uma entidade com o mesmo nome, o librsvg substitui e libera a primeira entidade. O libxml2 ainda mantém um ponteiro para ela.
Quando a análise do documento incluído termina, o libxml2 continua com esse ponteiro antigo. A memória já pode pertencer a outra coisa nesse momento, então escritas posteriores podem corrompê-la. Um crash é um dos resultados. Em outros casos, isso também levou à execução de comandos.
O atacante precisa de marcação SVG criada especificamente para alcançar o librsvg. Isso pode acontecer quando uma aplicação aceita um arquivo SVG, mas também pode acontecer quando a aplicação constrói um SVG a partir da entrada do usuário. Apenas usar o librsvg em algum lugar da árvore de dependências não estabelece um caminho explorável; a entrada precisa alcançar uma build afetada quando a imagem é renderizada.
A rota do Next.js que testamos aceitava texto em vez de um upload de SVG. Ela colocava esse texto dentro de um SVG inline para a versão Node.js do ImageResponse. Um bug de escaping separado no Satori permitia que o texto alterasse a marcação SVG gerada. O Sharp/libvips então passava esse SVG para o librsvg. A Vercel rastreou o caminho do Next.js como CVE-2026-94545. A versão Edge do ImageResponse não é afetada por esse caminho. Aqui está uma breve reprodução no Next.js.
Uma segunda rota do Next.js alcançou o mesmo bug do librsvg por meio da API de Image Optimization. Quando uma fonte de imagem controlada pelo atacante era aceita pela API, uma imagem criada especificamente podia passar pela rejeição de SVG do framework e depois ser tratada como SVG pelo Sharp/libvips. Isso expôs a corrupção de memória upstream a requisições não autenticadas em Linux baseado em glibc sem usar o Satori ou o ImageResponse.
A Vercel publicou essa rota como GHSA-2464-fhr4-w9xw, sob o CVE-2026-96889 upstream. O advisory a classifica como Crítica com uma pontuação CVSS de 9.5.
O Ghost tem um caminho diferente. Um usuário da equipe, incluindo um Contributor, podia criar um card de bookmark para um site que controlasse. O processamento da imagem do card podia então executar comandos no servidor do Ghost. O Ghost publicou isso como CVE-2026-105642.
Durante esta pesquisa, identifiquei caminhos de entrada afetados em vários produtos downstream e confirmei a execução de comandos em vários deles, incluindo ambas as rotas do Next.js e o caso do Ghost acima. Como essa entrada alcança o librsvg varia de produto para produto. É por isso que um bug upstream em um parser de imagens pode aparecer em lugares que não parecem relacionados à primeira vista; o recente caso do libheif é outro exemplo. Isso não significa que todo produto que usa o librsvg seja explorável remotamente.
ImageResponse): As versões 16.2.0 a 16.3.5 são afetadas quando uma aplicação coloca valores controlados pelo atacante em conteúdo, atributos ou estilos SVG. Atualize para 16.3.6.>= 10.0.0 <= 15.5.27 e > 16.0.0 <= 16.4.0 como afetadas. Atualize para 15.5.28 ou 16.4.1. O Sharp 0.35.5 ou posterior contém a correção upstream.Se você usa os binários pré-compilados do Sharp, verifique o pacote @img/sharp-libvips-* que sua aplicação instalou. Ele empacota o libvips e suas dependências, incluindo o librsvg. Atualizar a cópia do sistema do librsvg pode deixar sua aplicação usando a versão antiga.
O PoC de UAF inclui um gerador de SVG para vários caminhos de entrada e pode ser usado para verificar o use-after-free no início da cadeia.
O PoC de RCE do ImageResponse do Next.js do EQSTLab é um exemplo funcional dessa cadeia para sua build fixada de Node.js/Sharp. Ele gera e envia o payload automaticamente. Um exploit funcional já é público, então atualize as dependências afetadas ou mitigue o caminho de entrada o quanto antes.
[!WARNING] Não existe um PoC universal de RCE para esta cadeia. O exemplo do EQSTLab tem como alvo sua build fixada; outros alvos podem precisar de payloads diferentes. PoCs que dependem de
<foreignObject>de SVG para execução de comandos não demonstram a cadeia descrita aqui.
Para validação inicial em outros alvos, o PoC de UAF é mais prático. Também publicarei o write-up técnico nas próximas semanas, com os passos do UAF até a execução de comandos e um post-mortem cobrindo este último mês de pesquisa sobre a cadeia.