Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

الخلاصاتاتصالالخصوصية© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2026-94545- — أداة إثبات مفهوم بلغة Python للثغرة CVE-2026-94545: تستغل إدخال SVG غير المُهرَّب في Next.js next/og لتحفيز تلف libxml2 وسلسلة ROP لتنفيذ تعليمات برمجية عن بُعد دون مصادقة. | Kitploit
أدوات/GitHubGitHub/mrdark-ops/cve-2026-94545-
توليد الحمولةتحليل الثغرات الأمنيةالاستغلالشيل كوداستغلال تطبيقات الويبأمن الويباستغلال الملفات الثنائية
GitHubmrdark-ops/cve-2026-94545-

CVE-2026-94545-

أداة إثبات مفهوم بلغة Python للثغرة CVE-2026-94545: تستغل إدخال SVG غير المُهرَّب في Next.js next/og لتحفيز تلف libxml2 وسلسلة ROP لتنفيذ تعليمات برمجية عن بُعد دون مصادقة.

عرض المستودع
11منذ شهر واحدلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2026-94545: تنفيذ تعليمات برمجية عن بُعد دون مصادقة في next/og الخاص بـ Next.js

لأغراض البحث الأمني فقط، وفي بيئات الثغرات المصرّح بها.

العنصرالقيمة
CVECVE-2026-94545 / GHSA-vcvr-r3jv-pc5j
المنبعSatori GHSA-wx4j-mvgx-mqwp
السبب الجذريCWE-116 (ترميز أو تهريب المخرجات بشكل غير صحيح) → إفساد ذاكرة محلل XML الأصلي → تنفيذ تعليمات برمجية عن بُعد
المتأثر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) + Node.js v24.20.0 الثنائي الرسمي x64 (غير PIE)

مع بيئة تشغيل Edge (export const runtime = 'edge') أو عند عدم تثبيت sharp، يمر العرض عبر صندوق رمل resvg-wasm، لذا لا تصل هذه الثغرة إلى سلسلة التحليل الأصلية وبالتالي لا تتحول إلى تنفيذ تعليمات برمجية عن بُعد.

سلسلة الثغرة

يُدمَج إدخال المستخدم في SVG كنص عادي، ثم يُحشى هذا الـ SVG بالكامل داخل رابط data: ويُسلَّم إلى محلل XML مكتوب بلغة C لجولة ثانية من التحليل. لا يُجرى بينهما سوى ترميز URL؛ أما تهريب XML فلا يُنفَّذ أبدًا.

الطبقة 1: App Router — الإدخال غير الموثوق يدخل دالة العرض

النمط النموذجي: يضع POST /api/og جسم الطلب بالكامل، أو يضع GET /api/og?value= معامل الاستعلام، مباشرةً داخل <title> الخاص بـ SVG في 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 ثم يمرّره لعملية التحويل إلى صورة نقطية

تسلسل عمل @vercel/og المضمّن في Next.js (تُؤكّد تعليقات الملف المُجمّع أنه يحتوي على [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 المزدوجة التي تحمل كلمات 8 بايت little-endian)، وبذلك يتم اختطاف تدفق التحكم.

الطبقة 6: ROP بعنوان ثابت لعقدة Node غير PIE → execve

إن ملف Node.js linux-x64 الثنائي الصادر رسميًا هو ملف تنفيذي غير PIE بقاعدة تحميل ثابتة؛ عناوين PLT / GOT والأدوات ثابتة، لذا لا حاجة إلى تسريب معلومات.

تم بناء هذا PoC مقابل Node.js v24.20.0 الرسمي x64؛ الأدوات كالتالي:

الرمزالعنوانالغرض
POP_RDI0xA1EAEDpop rdi ; ret
POP_RAX0x7F38D4pop rax ; ret
POP_RSI0x95823Dpop rsi ; ret
POP_RDX0x776758pop rdx ; ret
STOSQ_RET0xD16725stosq ; ret (يكتب إلى scratch qword بـ qword)
EXECVE_PLT0x745FA0PLT stub الخاص بـ execve
SCRATCH0x6B5F500عنوان قابل للكتابة ثابت

تستخدم سلسلة ROP أولاً pop rax / stosq لكتابة "/bin/sh\0" و"-c\0" وسلسلة الأمر ومصفوفة argv في SCRATCH بـ qword تلو الآخر، ثم تضبط rdi = path وrsi = argv وrdx = envp(0)، وتقفز إلى EXECVE_PLT:

execve("/bin/sh", ["/bin/sh", "-c", "<command>"], NULL)

تُستبدل عملية العرض نفسها بـ execve؛ ينقطع اتصال HTTP ويتوقف الخدمة عن الاستجابة — وهذا مظهر متأصل من مظاهر الاستغلال الناجح لهذه الثغرة. ترث العملية الجديدة بيئة عملية Node ويمكنها تنفيذ أوامر عشوائية.

استخدام السكربت

exploit.py مُنفَّذ باستخدام مكتبة Python 3 القياسية، دون أي تبعيات خارجية. يرمّز الأمر إلى إحداثيات SVG <path>، ثم يجمّع حمولة تُرسَل عبر POST إلى /api/og بواسطة XInclude + كيانات DTD متداخلة.

سلسلة ROP مُعايَرة وفق حزمة Node v24.20.0 / sharp 0.35.4 الموصوفة أعلاه. يجب أن يستوفي الهدف الشروط التالية:

تنزيل الأداة