
Документирует цепочку RCE через use-after-free в librsvg VectorFreed (CVE-2026-96889) с PoC-генератором SVG и рекомендациями по устранению для librsvg, Next.js и Satori.
В этом документе представлена «VectorFreed» — цепочка уязвимостей, начинающаяся с use-after-free, которую я (@rafabd1) обнаружил в librsvg (CVE-2026-96889). В сборке Node.js/Sharp/libvips эта ошибка привела к выполнению команд. Здесь описана сама ошибка, пути, подтверждённые на данный момент в приложениях, и доступные сейчас исправления.
SVG может включать в себя другой SVG через XInclude. На уязвимом пути librsvg начинает разбор включённого документа, пока libxml2 всё ещё раскрывает сущность во внешнем. Если включённый SVG объявляет сущность с тем же именем, librsvg заменяет и освобождает первую сущность. libxml2 по-прежнему хранит указатель на неё.
Когда разбор включённого документа завершается, libxml2 продолжает работу с этим старым указателем. К этому моменту память может уже принадлежать чему-то другому, поэтому последующие записи могут её повредить. Падение — один из результатов. В других случаях это также приводило к выполнению команд.
Атакующему нужна специально сформированная SVG-разметка, чтобы достичь librsvg. Это может произойти, когда приложение принимает SVG-файл, но также может произойти, когда приложение строит SVG из пользовательского ввода. Само по себе использование librsvg где-то в дереве зависимостей не устанавливает эксплуатируемый путь; ввод должен достичь уязвимой сборки в момент рендеринга изображения.
Протестированный нами маршрут Next.js принимал текст вместо загрузки SVG. Он помещал этот текст внутрь встроенного SVG для версии ImageResponse под Node.js. Отдельная ошибка экранирования в Satori позволяла тексту изменять сгенерированную SVG-разметку. Затем Sharp/libvips передавал этот SVG в librsvg. Vercel отслеживал этот путь Next.js как CVE-2026-94545. Версия ImageResponse для Edge не подвержена этому пути. Вот короткое воспроизведение для Next.js.
Второй маршрут Next.js достигал той же ошибки librsvg через API оптимизации изображений. Когда API принимал контролируемый атакующим источник изображения, специально сформированное изображение могло обойти отклонение SVG на уровне фреймворка и впоследствии обрабатывалось как SVG в Sharp/libvips. Это делало вышестоящее повреждение памяти доступным для неаутентифицированных запросов на Linux с glibc без использования Satori или ImageResponse.
Vercel опубликовал этот маршрут как GHSA-2464-fhr4-w9xw в рамках вышестоящего CVE-2026-96889. В рекомендации он оценён как Critical с оценкой CVSS 9.5.
В Ghost путь другой. Сотрудник, включая Contributor, мог создать карточку закладки для сайта, который он контролирует. Обработка изображения карточки могла затем выполнить команды на сервере Ghost. Ghost опубликовал это как CVE-2026-105642.
В ходе этого исследования я выявил уязвимые пути ввода в нескольких нижестоящих продуктах и подтвердил выполнение команд в некоторых из них, включая оба маршрута Next.js и случай с Ghost выше. То, как этот ввод достигает librsvg, различается от продукта к продукту. Именно поэтому ошибка в вышестоящем парсере изображений может проявиться в местах, которые на первый взгляд не кажутся связанными; недавний случай с libheif — ещё один пример. Это не означает, что каждый продукт, использующий librsvg, эксплуатируем удалённо.
ImageResponse): Затронуты версии с 16.2.0 по 16.3.5, когда приложение помещает контролируемые злоумышленником значения в SVG-контент, атрибуты или стили. Обновитесь до 16.3.6.>= 10.0.0 <= 15.5.27 и > 16.0.0 <= 16.4.0. Обновитесь до 15.5.28 или 16.4.1. Sharp 0.35.5 или более поздняя версия содержит исправление из upstream.Если вы используете предварительно собранные бинарники Sharp, проверьте, какой пакет @img/sharp-libvips-* установило ваше приложение. Он включает в себя libvips и его зависимости, в том числе librsvg. Обновление системной копии librsvg может оставить ваше приложение использующим старую.
PoC для UAF включает генератор SVG для нескольких путей ввода и может использоваться для проверки use-after-free в начале цепочки.
PoC для RCE через Next.js ImageResponse от EQSTLab — это рабочий пример этой цепочки для зафиксированной в нём сборки Node.js/Sharp. Он автоматически генерирует и отправляет полезную нагрузку. Рабочий эксплойт теперь общедоступен, поэтому как можно скорее обновите затронутые зависимости или смягчите путь ввода.
[!WARNING] Универсального PoC для RCE по этой цепочке не существует. Пример EQSTLab нацелен на зафиксированную в нём сборку; для других целей могут потребоваться иные полезные нагрузки. PoC, которые полагаются на SVG
<foreignObject>для выполнения команд, не демонстрируют описанную здесь цепочку.
Для первичной проверки на других целях практичнее использовать PoC для UAF. В ближайшие недели я также опубликую технический разбор с шагами от UAF до выполнения команд и постмортем, охватывающий этот прошедший месяц исследования цепочки.