
Documenta la cadena de RCE por use-after-free de VectorFreed en librsvg (CVE-2026-96889) con un PoC de generador de SVG y guía de remediación para librsvg, Next.js y Satori.
Este documento presenta "VectorFreed", una cadena de vulnerabilidades que comienza con un use-after-free que yo (@rafabd1) encontré en librsvg (CVE-2026-96889). En una compilación de Node.js/Sharp/libvips, este fallo condujo a la ejecución de comandos. Cubre el fallo, las rutas confirmadas en aplicaciones hasta ahora y las correcciones disponibles actualmente.
Un SVG puede incluir otro SVG mediante XInclude. En la ruta vulnerable, librsvg comienza a analizar un documento incluido mientras libxml2 todavía está expandiendo una entidad en el documento externo. Si el SVG incluido declara una entidad con el mismo nombre, librsvg reemplaza y libera la primera entidad. libxml2 todavía mantiene un puntero a ella.
Cuando finaliza el análisis del documento incluido, libxml2 continúa con ese puntero antiguo. Para entonces, la memoria puede pertenecer ya a otra cosa, por lo que escrituras posteriores pueden corromperla. Un crash es uno de los resultados. En otros casos, esto también condujo a la ejecución de comandos.
El atacante necesita un marcado SVG manipulado para llegar a librsvg. Eso puede ocurrir cuando una aplicación acepta un archivo SVG, pero también puede ocurrir cuando la aplicación construye un SVG a partir de la entrada del usuario. El mero hecho de usar librsvg en algún lugar del árbol de dependencias no establece una ruta explotable; la entrada tiene que llegar a una compilación afectada cuando se renderiza la imagen.
La ruta de Next.js que probamos aceptaba texto en lugar de una carga de SVG. Colocaba ese texto dentro de un SVG en línea para la versión de Node.js de ImageResponse. Un fallo de escape independiente en Satori permitía que el texto modificara el marcado SVG generado. Sharp/libvips luego pasaba ese SVG a librsvg. Vercel registró la ruta de Next.js como CVE-2026-94545. La versión Edge de ImageResponse no se ve afectada por esta ruta. Aquí hay una breve reproducción en Next.js.
Una segunda ruta de Next.js alcanzaba el mismo fallo de librsvg a través de la API de Image Optimization. Cuando la API aceptaba una fuente de imagen controlada por el atacante, una imagen manipulada podía superar el rechazo de SVG del framework y luego ser tratada como SVG por Sharp/libvips. Esto exponía la corrupción de memoria upstream a solicitudes no autenticadas en Linux basado en glibc sin usar Satori ni ImageResponse.
Vercel publicó esta ruta como GHSA-2464-fhr4-w9xw, bajo el CVE upstream CVE-2026-96889. El aviso la califica como Crítica con una puntuación CVSS de 9.5.
Ghost tiene una ruta diferente. Un usuario del personal, incluido un Contributor, podía crear una tarjeta de marcador para un sitio que controlara. El procesamiento de la imagen de la tarjeta podía entonces ejecutar comandos en el servidor de Ghost. Ghost publicó esto como CVE-2026-105642.
Durante esta investigación, identifiqué rutas de entrada afectadas en múltiples productos downstream y confirmé la ejecución de comandos en varios de ellos, incluidas ambas rutas de Next.js y el caso de Ghost anterior. Cómo llega esa entrada a librsvg varía de un producto a otro. Por eso un fallo upstream en un analizador de imágenes puede aparecer en lugares que no parecen relacionados a primera vista; el reciente caso de libheif es otro ejemplo. No significa que todos los productos que usan librsvg sean explotables de forma remota.
ImageResponse): Las versiones 16.2.0 a 16.3.5 se ven afectadas cuando una aplicación coloca valores controlados por el atacante en contenido, atributos o estilos SVG. Actualiza a 16.3.6.>= 10.0.0 <= 15.5.27 y > 16.0.0 <= 16.4.0 como afectadas. Actualiza a 15.5.28 o 16.4.1. Sharp 0.35.5 o posterior contiene la corrección upstream.Si utilizas los binarios precompilados de Sharp, comprueba el paquete @img/sharp-libvips-* que instaló tu aplicación. Este incluye libvips y sus dependencias, incluido librsvg. Actualizar la copia del sistema de librsvg puede dejar tu aplicación usando la antigua.
El PoC de UAF incluye un generador de SVG para varias rutas de entrada y puede utilizarse para comprobar el use-after-free al inicio de la cadena.
El PoC de RCE de ImageResponse de Next.js de EQSTLab es un ejemplo funcional de esa cadena para su compilación fijada de Node.js/Sharp. Genera y envía el payload automáticamente. Un exploit funcional es público ahora, así que actualiza las dependencias afectadas o mitiga la ruta de entrada lo antes posible.
[!WARNING] No existe un PoC universal de RCE para esta cadena. El ejemplo de EQSTLab apunta a su compilación fijada; otros objetivos pueden necesitar payloads diferentes. Los PoCs que dependen de
<foreignObject>de SVG para la ejecución de comandos no demuestran la cadena descrita aquí.
Para la validación inicial en otros objetivos, el PoC de UAF es más práctico. También publicaré el análisis técnico en las próximas semanas, con los pasos desde el UAF hasta la ejecución de comandos y un post-mortem que abarca este último mes de investigación sobre la cadena.