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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
Instatic-Stored-XSS-CVE-2026-103931 — إثبات مفهوم وتحليل لثغرة XSS مخزّنة في مرشّح URL ‏isSafeUrl() الخاص بـ Instatic، حيث تتجاوز أحرف التحكم C0 البادئة حظر مخطط javascript:. | Kitploit
أدوات/GitHubGitHub/overgrowncarrot1/instatic-stored-xss-cve-2026-103931
التحليل الثابتتحليل الثغرات الأمنيةتحليل الكودالاستغلالاستغلال تطبيقات الويبأمن الويب
GitHubovergrowncarrot1/instatic-stored-xss-cve-2026-103931

Instatic-Stored-XSS-CVE-2026-103931

إثبات مفهوم وتحليل لثغرة XSS مخزّنة في مرشّح URL ‏isSafeUrl() الخاص بـ Instatic، حيث تتجاوز أحرف التحكم C0 البادئة حظر مخطط javascript:.

الأكثر شعبية

عرض الكل →

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

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

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

عرض جميع الأدوات →
عرض المستودع
منذ يوم واحدلم تتم المراجعة بعد
مشاركة

تجاوز عامل تصفية مخطط URL في isSafeUrl() عبر أحرف التحكم C0 البادئة

المتأثر: Instatic v0.0.13 / commit 63ad5d6 (وجميع المراجعات الأقدم التي تحتوي على src/core/html-sanitize/index.ts) المكوّن: src/core/html-sanitize/index.ts → isSafeUrl() / safeUrl() التصنيف: CWE-79 (XSS مخزّن) عبر CWE-184 (قائمة مدخلات محظورة غير مكتملة)

الملخص

isSafeUrl() هي نقطة الاختناق الوحيدة التي تحجب عناوين URL من نوع javascript: وvbscript: وdata: في كامل الناشر. وهي تُطبّع المدخلات باستخدام .replace(/[\t\n\r]/g, '').trim() قبل اختبار بادئة المخطط.

يقوم محلّل URL وفق معيار WHATWG بإزالة جميع أحرف التحكم C0 البادئة (U+0000–U+001F) والمسافة قبل قراءة المخطط. بينما تُزيل دالة String.prototype.trim() في JavaScript فقط الأحرف U+0009 وU+000A وU+000B وU+000C وU+000D وU+0020 والمسافات اليونيكودية — وتترك U+0000–U+0008 وU+000E–U+001F في مكانها.

لذلك فإن عنوان URL مسبوق مثلاً بـ U+0001 يُبلَّغ عنه كآمن من قِبل الحارس، بينما يقوم كل متصفح بتحليله وتنفيذه كمخطط javascript:.

إثبات المفهوم

مقابل src/core/html-sanitize/index.ts غير المعدّل:

payload           : "\x01javascript:alert(document.domain)"
isSafeUrl()       : true          <-- guard reports "safe"
WHATWG URL scheme : javascript:   <-- what the browser actually runs
safeUrl() output  : "\x01javascript:alert(document.domain)"   (NOT collapsed to "#")

27 من أصل 32 حرف تحكم C0 تتجاوز الفحص. يتم تحييد U+0000 بواسطة تحليل سمات HTML (NUL → U+FFFD)، مما يترك 26 بادئة قابلة للاستغلال بشكل موثوق (U+0001–U+0008، U+000E–U+001F). ويتغلب نفس التجاوز على مرشّحات vbscript: وdata:.

تغطي المجموعة الاختبارية الحالية (src/__tests__/publisher/utils.test.ts) تغيير حالة الأحرف والجدولة المضمّنة (java\tscript:) لكنها لا تغطي أبداً حرف تحكم بادئاً، ولهذا لم يُكتشف هذا.

المسار من البداية إلى النهاية

يُعلن base.link عن href: { type: 'url' } ويُصنّفه LinkPropsSchema كـ Type.String({ default: '#' }) غير مقيّد — لا يوجد تحقق من صحة URL وقت الكتابة. لذلك:

  1. يوجّه escapeProps() خصائص type: 'url' | 'image' | 'media' إلى isSafeUrl(value) ? value : '#' — فيمرّ الحمولة خاماً وبشكل متعمّد دون تهريب HTML.
  2. يُصدر render() الخاص بـ base.link `<a href="https://github.com/overgrowncarrot1/instatic-stored-xss-cve-2026-103931/blob/main/%24%7BsafeUrl%28props.href%29%7D" …>`. ويعيد safeUrl() الفحص بنفس isSafeUrl() المعطوبة، ثم escapeHtml() — التي تهرب فقط & < > " ' ولا تمس أحرف التحكم.
  3. تصل الحمولة حرفياً إلى سمة href: <a href="https://github.com/overgrowncarrot1/instatic-stored-xss-cve-2026-103931/blob/main/%5Cx01javascript%3Aalert%28document.domain%29" target="_self">Click me</a>

نقاط الوصول المتأثرة

كل هذه تمر عبر نفس isSafeUrl():

نقطة الوصولالملف
كل خاصية وحدة url / image / media (رابط href، زر href، صورة src، فيديو src/poster، نموذج action، نموذج redirectUrl)src/core/publisher/escapeProps.ts:108
سمات HTML مخصّصة عشوائية يضبطها المستخدم على أي عقدةsrc/core/htmlAttributes/attributes.ts:66
رابط/صورة Markdown href وsrcsrc/core/markdown/renderMarkdown.ts:75
faviconUrl للموقعsrc/core/publisher/render.ts:322
استدعاء safeUrl() في كل وحدة أساسيةsrc/modules/base/utils/escape.ts

التأثير

تصعيد صلاحيات من محرّر منخفض الصلاحيات إلى اختراق كامل لصلاحيات المسؤول.

يمتلك دور Client المدمج صلاحية site.content.edit، وهي كافية لضبط href لرابط أو سمة HTML مخصّصة. ثم يُعرض عنوان URL المحقون في لوحة محرّر المسؤول، وهي إطار srcdoc iframe — من نفس الأصل مع /admin.

يضبط server/securityHeaders.ts:69-72 فقط frame-ancestors 'none'; base-uri 'self'; object-src 'none' على /admin، مع تعليق برمجي صريح بأن سياسة script-src لم تُضبط بعد بشكل متعمّد. وبدون script-src، لا شيء يمنع عنوان URL من نوع javascript: من التنفيذ على أصل المسؤول.

عندما يفتح Owner أو Admin الصفحة المتأثرة في المحرّر ويُنشّط العنصر، تُنفَّذ الحمولة من نفس الأصل مع تطبيق SPA الخاص بالمسؤول. كوكي الجلسة هو HttpOnly، لذا لا يمكن قراءته مباشرة — لكن الحمولة يمكنها تشغيل واجهة برمجة تطبيقات المسؤول بصفة الضحية (إنشاء حساب owner، قراءة الأسرار، أو تثبيت إضافة، ونقطة دخول خادمها مسار لتنفيذ الكود).

على الموقع المنشور يكون التأثير أضيق: يضبط cspPlan.ts القيمة script-src 'none' (أو 'self')، مما يحجب عناوين URL من نوع javascript:. لكن server/publish/frontendInjections.ts:377 يخفّف ذلك إلى 'self' 'unsafe-inline' لأي صفحة تحمل سكربتاً مضمّناً، و'unsafe-inline' تعيد السماح بعناوين URL من نوع javascript: — لذا يمكن الوصول إلى XSS على الموقع المنشور في تلك الصفحات.

لاحظ أن docstring الخاص بـ attributes.ts يحدّد بالفعل هذا النموذج التهديدي بالضبط ("على الموقع المنشور وأيضاً، وبشكل أخطر، داخل لوحة محرّر المسؤول (من نفس الأصل مع /admin)") — لكن الحارس ببساطة لا ينفّذه بشكل كامل.

التحقق

تم التأكيد في متصفح (Chromium، إطارات srcdoc iframe تعيد إنتاج CSP لكل أصل، وتعرض المخرجات بايت-ببايت من safeUrl() الخاصة بالمستودع نفسه):

الأصل المُعاد إنتاجهCSP المطبّقالنتيجة
لوحة محرّر المسؤول (/admin)لا شيء — كما يُصدره securityHeaders.tsتم تنفيذ javascript:
صفحة منشورة (خط الأساس)script-src 'none' — كما يُصدره cspPlan.tsمحجوب (script-src-elem)
صفحة منشورة مع سكربت مضمّنscript-src 'self' 'unsafe-inline' — كما يُصدره frontendInjections.ts:377انظر الملاحظة

الصفّان الأولان نتائج مرصودة. أبلغ التنفيذ على أصل المسؤول عن document.domain كأصل الخدمة، مما يؤكد التنفيذ من نفس الأصل وليس من سياق معتم.

تحفّظ بشأن الدقة: تُطبّق أداة الاختبار كل سياسة عبر <meta http-equiv>، بينما يرسلها Instatic كترويسة استجابة HTTP. هذان متكافئان لفرض script-src، لكن إعادة الإنتاج على نسخة bun run dev حيّة ستحمل الترويسات الحقيقية.

الإصلاح

جرّد النطاق الكامل C0 + المسافة بدلاً من الاعتماد على trim(). هذا بالضبط ما تفعله تعبير isJavaScriptProtocol الخاص بـ React مع بادئته ^[\u0000-\u001F ]* — وهو تحقق متقاطع مفيد بأن هذه فئة تجاوز معروفة وواقعية وليست نظرية.

--- a/src/core/html-sanitize/index.ts
+++ b/src/core/html-sanitize/index.ts
@@ -30,7 +30,11 @@ export function escapeHtml(value: unknown): string {
   * normalisation browsers apply during URL parsing.
   */
  export function isSafeUrl(url: string): boolean {
-  const normalized = url.replace(/[\t\n\r]/g, '').trim().toLowerCase()
+  const normalized = String(url ?? '')
+    .replace(/[\t\n\r]/g, '')
+    .replace(/^[\u0000-\u0020]+/, '')
+    .replace(/[\u0000-\u0020]+$/, '')
+    .toLowerCase()
    return (
      !normalized.startsWith('javascript:') &&
      !normalized.startsWith('vbscript:') &&

تم التحقق مقابل الملف المرقّع: الحمولة محجوبة، وsafeUrl() تُسقطها إلى #، و0 من أصل 99 عنوان URL خطير مسبوق بتحكم/مسافة يبقى مقبولاً، وجميع حالات اختبار isSafeUrl الـ 14 الحالية تتصرف بشكل مطابق.

اختبار انحدار مقترح

it('blocks javascript: behind a leading C0 control character', () => {
  for (let i = 0x00; i <= 0x1f; i++) {
    expect(isSafeUrl(String.fromCharCode(i) + 'javascript:alert(1)')).toBe(false)
  }
})

الدفاع في العمق

فكّر أيضاً في ضبط script-src حقيقية على /admin (المتابعة المؤجّلة المذكورة في server/securityHeaders.ts)، والتي ستحتوي فئة الأخطاء بأكملها بدلاً من هذه الحالة الواحدة.

تنزيل الأداة