
Документирует цепочку 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. Он помещал этот текст внутрь inline SVG для Node.js-версии ImageResponse. Отдельная ошибка экранирования в Satori позволяла тексту изменять генерируемую SVG-разметку. Затем Sharp/libvips передавал этот SVG в librsvg. Vercel отслеживал путь Next.js как CVE-2026-94545. Edge-версия ImageResponse не подвержена этому пути. Вот короткое воспроизведение для Next.js.
В ходе этого исследования я выявил уязвимые пути ввода в нескольких нижестоящих продуктах и подтвердил выполнение команд в некоторых из них, включая описанный выше случай с Next.js. То, как этот ввод достигает librsvg, различается от продукта к продукту. Именно поэтому ошибка в вышестоящем парсере изображений может проявиться в местах, которые на первый взгляд не связаны; недавний случай с libheif — ещё один пример. Это не означает, что каждый продукт, использующий librsvg, эксплуатируем удалённо.
ImageResponse. Обновитесь до 16.3.6.Если вы используете предсобранные бинарники Sharp, проверьте пакет @img/sharp-libvips-*, установленный вашим приложением. Он включает libvips и его зависимости, в том числе librsvg. Обновление системной копии librsvg может оставить ваше приложение использующим старую.
PoC для UAF включает генератор SVG для нескольких путей ввода и может использоваться для проверки use-after-free в начале цепочки.
На данный момент я не нашёл публичного эксплойта, который проводит эту цепочку полностью от специально сформированного SVG до выполнения команд. Однако ошибка и патчи уже публичны, и с текущими инструментами ИИ довольно легко восстановить полную цепочку по ним. Учитывая это, относитесь к эксплойту так, как будто он уже публичен, и как можно скорее обновите уязвимые зависимости или устраните путь ввода.
[!WARNING] Универсального RCE PoC для этой цепочки не существует; полезную нагрузку нужно адаптировать под каждую цель. Большинство публичных "PoC", которые я видел на данный момент, не воспроизводят реальную RCE-цепочку. Некоторые полагаются на SVG
<foreignObject>для заявленного выполнения, что не является описанным здесь путём эксплуатации.
Для первоначальной проверки UAF PoC практичнее. Я планирую опубликовать RCE PoC для случая с Next.js (CVE-2026-94545) в отдельном репозитории. Я также опубликую технический разбор в ближайшие недели, с шагами от UAF до выполнения команд и постмортемом, охватывающим этот прошедший месяц исследования цепочки.