## https://sploitus.com/exploit?id=6D04155A-85FA-5456-92B9-3FB322D19AC1
# CVE-2026-61946: One Appointment ID to Somebody Else's Booking
I found an unauthenticated insecure direct object reference in the WordPress
Easy Appointments plugin. The public reservation endpoint accepted an `id`
from the query string and passed the resulting data into the plugin's database
`replace()` path.
That meant a new public reservation request could be turned into an update of
an existing appointment row. No login, cookies, or WordPress account were
required. Give it another appointment's primary key, choose a real open slot,
and the plugin overwrote that booking with attacker-controlled customer and
appointment data.
| | |
|---|---|
| CVE | CVE-2026-61946 |
| Plugin | Easy Appointments |
| Slug | `easy-appointments` |
| Affected | ` 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 and CAPTCHA checks did not establish ownership of the supplied
appointment ID. They were also disabled by default in the configuration I
tested, so the request needed no session state at all.
The endpoint did perform an availability check. That did not fix the object
authorization problem; it only meant the attacker had to choose a valid public
location, service, worker, date, and currently open time slot.
## Triggering It
You need:
```text
1. A disposable WordPress lab running Easy Appointments $rem) {
if (!in_array($key, $dont_remove)) unset($data[$key]);
}
+
+unset($data['id']);
+$data['id'] = null;
unset($data['action']);
```
The public booking flow no longer gets to choose the database object's primary
key. The request is forced down the new-record path instead of being allowed to
replace an arbitrary existing appointment.
The same security commit also corrected the nonce option logic. That is useful
defence in depth, but nonce validation alone would not be an ownership check
for an attacker-supplied appointment ID. Removing the client-controlled key is
the direct IDOR fix.
The extracted patch is in [`patch/fix.diff`](patch/fix.diff).
## Repository Structure
```text
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
```
## Timeline
| 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 |
## Resources
* Patchstack advisory: https://patchstack.com/database/wordpress/plugin/easy-appointments/vulnerability/wordpress-easy-appointments-plugin-3-12-27-insecure-direct-object-references-idor-vulnerability
* CVE record: https://vulners.com/cve/CVE-2026-61946
* NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-61946
* WordPress plugin: https://wordpress.org/plugins/easy-appointments/
* Upstream source: https://github.com/ahmedkaludi/easy-appointments
* Security fix commit: https://github.com/ahmedkaludi/easy-appointments/commit/f3464999ba3723046bd6e13d032122c196dd9768
* Vulnerable tag: https://plugins.svn.wordpress.org/easy-appointments/tags/3.12.27/
* Fixed tag: https://plugins.svn.wordpress.org/easy-appointments/tags/3.12.28/
> **Disclaimer:** This PoC is published for defensive research and verification
> after patch availability. Do not use it against systems you do not own or have
> explicit authorization to test.
*CVE-2026-61946 - Fixed in Easy Appointments 3.12.28. Affected: 3.12.27 and earlier.*
*Daniel Wade - [GitHub](https://github.com/Rat5ak) - [Twitter/X](https://x.com/Nadsec11) - [Bluesky](https://bsky.app/profile/nadsec.online) - [Mastodon](https://cyberplace.social/@Nadsec) - [Medium](https://medium.com/@Nadsec) - [nadsec.online](https://nadsec.online)*