
CVE-2026-64638 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट: वर्डप्रेस लॉगिन में रिफ्लेक्टेड XSS को DOM क्लोबरिंग के साथ जोड़कर एडमिन अकाउंट टेकओवर और रिमोट कोड निष्पादन प्राप्त करना।
सॉफ़्टवेयर: WordPress Core ≤ 7.0.2 (7.0.3 से पहले के सभी संस्करण)
CVSS: 8.9 (उच्च)
CWE: CWE-79 — वेब पेज निर्माण के दौरान इनपुट का अनुचित न्यूट्रलाइज़ेशन
प्रमाणीकरण आवश्यक: कोई नहीं (Pre-Auth)
उपयोगकर्ता इंटरैक्शन: सक्रिय (एडमिन को 1 लिंक पर क्लिक करना आवश्यक है)
प्रभाव: XSS → खाता अधिग्रहण → रिमोट कोड निष्पादन
WordPress दुनिया की सबसे लोकप्रिय कंटेंट प्रबंधन प्रणाली है, जो इंटरनेट पर मौजूद 40% से अधिक वेबसाइटों का प्रतिनिधित्व करती है। हर WordPress साइट पर /wp-login.php पर एक लॉगिन पेज होता है — यह एक सार्वजनिक एंडपॉइंट है जिसे कोई भी बिना प्रमाणीकरण के एक्सेस कर सकता है।
जब कोई उपयोगकर्ता गलत उपयोगकर्ता नाम दर्ज करता है, तो WordPress एक त्रुटि संदेश प्रदर्शित करता है जिसमें उपयोगकर्ता द्वारा टाइप किया गया exact उपयोगकर्ता नाम होता है: “उपयोगकर्ता नाम X इस साइट पर पंजीकृत नहीं है।” समस्या इस तथ्य में निहित है कि उपयोगकर्ता नाम का मान बिना किसी escape फ़ंक्शन से गुज़ारे सीधे HTML प्रतिक्रिया में डाला जाता है — एक हमलावर को केवल वास्तविक उपयोगकर्ता नाम के बजाय HTML/JavaScript दर्ज करने की आवश्यकता होती है, और कोड ब्राउज़र में निष्पादित हो जाएगा।
यह एक Reflected XSS दोष है — पेलोड अनुरोध में निहित होता है और सर्वर द्वारा HTML में समान रूप से वापस परावर्तित किया जाता है। इसे खतरनाक बनाने वाली बात यह है कि यह दोष लॉगिन पेज पर स्थित है — एक ऐसा स्थान जहाँ एडमिन अक्सर आते हैं, जहाँ एडमिन के सत्र कुकीज़ चुराई जा सकती हैं।
शोध टीम ने आगे यह भी पता लगाया कि इस XSS को WordPress के emoji-loader में मौजूद DOM clobbering भेद्यता के साथ जोड़ा जा सकता है, जिससे JavaScript को बाहरी सर्वर से लोड किया जा सकता है। वहाँ से, एक हमलावर एक नया एडमिन खाता बना सकता है → webshell युक्त प्लगइन इंस्टॉल कर सकता है → सर्वर पर PHP कोड निष्पादित कर सकता है। इस शोषण श्रृंखला को XSS2Shell कहा जाता है।
| विशेषता | मान |
|---|---|
| CVE ID | CVE-2026-64638 |
| CVSS स्कोर | 8.9 (उच्च) |
| सॉफ़्टवेयर | WordPress Core ≤ 7.0.2 |
| प्रमाणीकरण | कोई आवश्यकता नहीं (Pre-Auth) |
| उपयोगकर्ता इंटरैक्शन | 1 क्लिक आवश्यक (एडमिन लिंक पर क्लिक करता है) |
| हमला जटिलता | उच्च |
| पैच किया गया | WordPress 7.0.3 (08/06/2026) |
| रिपोर्टकर्ता | pwn.ai टीम, HackerOne के माध्यम से |
| HackerOne रिपोर्ट | #3877102 |
WordPress हर पेज (लॉगिन पेज सहित) पर emoji-loader.js फ़ाइल के माध्यम से emoji समर्थन लोड करता है। यह स्क्रिप्ट id="wp-emoji-settings" वाले एलिमेंट से कॉन्फ़िगरेशन पढ़ती है:
// Before patch (vulnerable)
const settings = JSON.parse(
document.getElementById('wp-emoji-settings').textContent
);
document.getElementById() DOM में मिलान वाले id वाला पहला एलिमेंट लौटाता है। यदि कोई हमलावर मूल script टैग से पहले <div id="wp-emoji-settings"> इंजेक्ट करता है, तो getElementById वास्तविक कॉन्फ़िगरेशन के बजाय हमलावर की सामग्री पढ़ेगा। इस तकनीक को DOM clobbering कहा जाता है — HTML एलिमेंट इंजेक्ट करके JavaScript व्यवहार को अधिलेखित करना।
emoji कॉन्फ़िगरेशन में JavaScript फ़ाइल (concatemoji) लोड करने के लिए एक URL होता है। हमलावर इस URL को नियंत्रित करता है → बाहरी सर्वर से एक JS फ़ाइल लोड होती है → ब्राउज़र संदर्भ में मनमाना कोड निष्पादित होता है।
एक बार एडमिन संदर्भ में JavaScript निष्पादन प्राप्त हो जाने पर, हमलावर के पास पूर्ण WordPress एडमिन विशेषाधिकार होते हैं:
/wp-admin/user-new.php को कॉल करें/wp-admin/plugin-install.php के माध्यम से प्लगइन अपलोड करेंउपरोक्त 3 विधियों में से कोई भी सर्वर पर PHP कोड निष्पादन की अनुमति देती है — अर्थात RCE।
wordpress-develop पर फिक्स कमिट 0d6d42e से, मैंने wp-includes/user.php फ़ाइल में 3 स्थानों की पहचान की जहाँ उपयोगकर्ता नाम/ईमेल सीधे त्रुटि संदेश में डाला जाता है:
# View diff between vulnerable and patched versions
git diff 7.0.2..7.0.3 -- src/wp-includes/user.php
स्थान 1 — पंक्ति 189 (उपयोगकर्ता नाम मौजूद नहीं है):
पहले :
// BEFORE (vulnerable):
sprintf(
__( 'The username <strong>%s</strong> is not registered...' ),
$username // ← no escaping
)

बाद में :
// fixed:
sprintf(
__( 'The username <strong>%s</strong> is not registered...' ),
esc_html( $username ) // ← escaped
)
स्थान 2 — पंक्ति 216 (गलत पासवर्ड):
पहले :

// BEFORE:
'<strong>' . $username . '</strong>' // ← no escaping
बाद में :
// AFTER:
'<strong>' . esc_html( $username ) . '</strong>'
स्थान 3 — पंक्ति 299 (ईमेल के लिए गलत पासवर्ड):
पहले :

// BEFORE:
'<strong>' . $email . '</strong>' // ← no escaping
बाद में :
// AFTER:
'<strong>' . esc_html( $email ) . '</strong>'
POST अनुरोध से त्रुटि संदेश तक डेटा प्रवाह:
$_POST['log'] ← user input from login form
↓
wp_signon() [user.php:51]
$credentials['user_login'] = wp_unslash($_POST['log']) ← only removes backslashes
↓
wp_authenticate($username, $password) [pluggable.php:689]
$username = sanitize_user($username) ← strips HTML tags, but has a bypass
↓
wp_authenticate_username_password() [user.php:153]
get_user_by('login', $username) ← user not found
↓
sprintf('The username <strong>%s</strong>...', $username) ← XSS!
↓
WP_Error → login_header() → wp_admin_notice()
wp_kses_post(...) ← filters HTML but allows <div>, <a>, through
↓
HTML response → browser render → JavaScript execute
उपयोगकर्ता नाम HTML तक पहुँचने से पहले WordPress में 2 फ़िल्टरिंग परतें होती हैं:
परत 1: sanitize_user() — HTML टैग हटाने के लिए strip_tags() को कॉल करता है। हालाँकि, PHP के strip_tags() की ज्ञात सीमाएँ हैं: गैर-मानक टैग फ़ॉर्मेटिंग फ़िल्टर को बायपास कर सकती है।
परत 2: wp_kses_post() — HTML के एक सुरक्षित उपसमुच्चय को अनुमति देता है, जिसमें कुछ विशेषताओं (attributes) के साथ <div>, <a>, `` शामिल हैं (लेकिन onerror, onload जैसे इवेंट हैंडलर हटा देता है)। महत्वपूर्ण बात: wp_kses_post <div id="wp-emoji-settings"> को अनुमति देता है — वही एलिमेंट जो DOM clobbering के लिए आवश्यक है।
pwn.ai टीम ने एक उपयोगी पेलोड इंजेक्ट करने के लिए दोनों परतों को बायपास करने का एक तरीका खोजा। विशिष्ट तकनीकी विवरण सार्वजनिक रूप से जारी नहीं किए गए हैं।
यह देखने के लिए कि उपयोगकर्ता नाम बिना escaping के सीधे HTML में जाता है, मैंने निष्पादन श्रृंखला के प्रमुख बिंदुओं पर ब्रेकपॉइंट लगाने के लिए Xdebug + VS Code का उपयोग किया।
चरण 1 — लॉगिन फ़ॉर्म में XSS पेलोड दर्ज करें:
http://localhost:8282/wp-login.php तक पहुँचें, उपयोगकर्ता नाम के रूप में `` दर्ज करें और फिर Log In पर क्लिक करें। एक अलर्ट पॉपअप दिखाई देता है — XSS काम करता है।


चरण 2 — user.php:184 पर ब्रेकपॉइंट — जहाँ XSS होता है:
wp_authenticate_username_password() फ़ंक्शन के अंदर return new WP_Error(...) पर ब्रेकपॉइंट सेट करें। जब डिबगर रुकता है, तो निरीक्षण करें: