CVE-2026-42669

EventPrime – Events Calendar, Bookings and Tickets <= 4.3.2.0 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
4.3.2.1
Patched in
8d
Time to patch

Description

The EventPrime – Events Calendar, Bookings and Tickets plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 4.3.2.0. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=4.3.2.0
PublishedMay 12, 2026
Last updatedMay 19, 2026

What Changed in the Fix

Changes introduced in v4.3.2.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-42669 ## 1. Vulnerability Summary The **EventPrime – Events Calendar, Bookings and Tickets** plugin for WordPress is vulnerable to **Missing Authorization** in versions up to and including 4.3.2.0. Specifically, the function `cancel_current_booking_process` i…

Show full research plan

Exploitation Research Plan - CVE-2026-42669

1. Vulnerability Summary

The EventPrime – Events Calendar, Bookings and Tickets plugin for WordPress is vulnerable to Missing Authorization in versions up to and including 4.3.2.0. Specifically, the function cancel_current_booking_process in includes/class-ep-ajax.php lacks a capability check (e.g., current_user_can()). This function is intended to release seats held during a booking process. Because it is likely registered as a wp_ajax_nopriv action (to allow guest users to cancel their sessions), any unauthenticated attacker who obtains a valid nonce can trigger this function to manipulate event seating data, specifically resetting seat statuses in the database.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: ep_cancel_current_booking_process (Inferred from function name cancel_current_booking_process and common plugin prefix).
  • Parameters:
    • action: ep_cancel_current_booking_process
    • security: A nonce associated with the event-registration-form-nonce action.
    • event_id: The ID of the target event (post type em_event).
    • ticket_data: A JSON-encoded string containing area and seat UID information.
  • Authentication: Unauthenticated (if nopriv is registered).
  • Preconditions:
    1. The EventM_Live_Seating_List_Controller class must exist (typically part of the Live Seating extension).
    2. The target event must have seating data stored in the em_seat_data post meta.

3. Code Flow

  1. Entry Point: The AJAX handler for ep_cancel_current_booking_process is triggered via admin-ajax.php.
  2. Nonce Check: includes/class-ep-ajax.php:14 calls wp_verify_nonce( $_POST['security'], 'event-registration-form-nonce' ).
  3. Class Check: Line 19 checks class_exists( 'EventM_Live_Seating_List_Controller' ).
  4. Data Retrieval: Line 23 retrieves existing seating data: get_post_meta( $event_id, 'em_seat_data', true ).
  5. Modification: The code iterates through the user-provided ticket_data (Line 28). If a matching seat is found and its type is 'hold', it resets the seat:
    • type becomes 'general'
    • hold_time becomes ''
    • seatColor is set to the available color from the plan.
  6. Sink: Line 73 calls update_post_meta( $event_id, 'em_seat_data', maybe_serialize( $event_seat_data ) ), updating the database with the modified object.

4. Nonce Acquisition Strategy

The nonce event-registration-form-nonce is required. It is typically localized for the frontend booking page when a user views an event with seating enabled.

  1. Identify Localization: Search the codebase for event-registration-form-nonce to find the wp_localize_script call. It is likely localized into a global JS object like ep_event_booking or eventprime.
  2. Create Target Content: Create an event and a page containing the booking shortcode if necessary.
    wp post create --post_type=em_event --post_title="Vulnerable Event" --post_status=publish
    
  3. Navigate and Extract:
    • Use browser_navigate to visit the single event page.
    • Use browser_eval to extract the nonce. Based on common plugin patterns:
      window.ep_event_booking?.security // Or search for the key associated with 'event-registration-form-nonce'
      

5. Exploitation Strategy

Step 1: Payload Construction

The ticket_data parameter must be a JSON array of objects.
Structure:

[
  {
    "seats": [
      {
        "area_id": "1",
        "seat_data": [
          { "uid": "0-0" } 
        ]
      }
    ]
  }
]

Note: uid format is row-column (e.g., 0-0).

Step 2: HTTP Request

Submit the request via http_request.

  • Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=ep_cancel_current_booking_process&security=[NONCE]&event_id=[EVENT_ID]&ticket_data=[JSON_PAYLOAD]
    

6. Test Data Setup

To demonstrate the modification of post meta, we must simulate a "hold" status in the database:

  1. Create Event: Use WP-CLI to create an em_event.
  2. Set Seating Meta:
    # Create a dummy seating object where seat 0-0 in area 1 is 'hold'
    # Structure based on class-ep-ajax.php logic
    php -r '$data = new stdClass(); 
            $data->{"1"} = new stdClass(); 
            $data->{"1"}->seats = [0 => [0 => (object)["col" => 0, "type" => "hold", "hold_time" => "123456", "seatColor" => "#ff0000"]]];
            echo serialize($data);' > /tmp/seat_meta.txt
    
    wp post meta update [EVENT_ID] em_seat_data --file=/tmp/seat_meta.txt
    
  3. Mock Controller Class: If the "Live Seating" extension is not installed, use wp eval to define a dummy EventM_Live_Seating_List_Controller class so the class_exists check passes.

7. Expected Results

  • Response: The server should return {"success":true,"data":true} (the result of update_post_meta).
  • Data Change: The em_seat_data meta for the event will be updated. Specifically, the seat at 0-0 will change from type: "hold" to type: "general".

8. Verification Steps

After the exploit, verify the database state using WP-CLI:

wp post meta get [EVENT_ID] em_seat_data

The output should show the seat type as general and hold_time as an empty string.

9. Alternative Approaches

If the ep_cancel_current_booking_process action name is incorrect, verify the registered AJAX actions using:

wp eval 'global $wp_filter; print_r(array_keys($wp_filter["wp_ajax_nopriv_"] ?? []));'

Look for actions containing cancel or booking.

If the class check EventM_Live_Seating_List_Controller cannot be bypassed, the exploit still demonstrates "Unauthorized Access" to the function logic, but the response will be {"success":true,"data":"not a seated event"}. This still proves the missing capability check.

Research Findings
Static analysis — not yet PoC-verified

Summary

The EventPrime plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on the cancel_current_booking_process function. This allows unauthenticated attackers to manipulate event seating data, specifically resetting seat statuses from 'hold' to 'available' in the database.

Vulnerable Code

// includes/class-ep-ajax.php:10
    public function cancel_current_booking_process() {
        // Add security checks 
        if( wp_verify_nonce( $_POST['security'], 'event-registration-form-nonce' ) ) {
            $event_id = absint( $_POST['event_id'] ); 
            $ticket_data = json_decode( stripslashes( $_POST['ticket_data'] ) );

            if ( ! class_exists( 'EventM_Live_Seating_List_Controller' ) ) {
                wp_send_json_success( 'not a seated event' );
            }

            $event_seat_data = get_post_meta( $event_id, 'em_seat_data', true );
            if( ! empty( $event_seat_data ) ) { 
                // ... (truncated loop logic resetting seat status) ...

               $update =  update_post_meta( $event_id, 'em_seat_data', maybe_serialize( $event_seat_data ) );
               wp_send_json_success($update);

            } else {
                wp_send_json_success('not a seated event'); 
            }
        } else {
            wp_send_json_error( array( 'message' => esc_html__( 'Security check failed. Please refresh the page and try again later.', 'eventprime-event-seating' ) ) );
        }
    }

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/eventprime-event-calendar-management/4.3.2.0/event-prime.php /home/deploy/wp-safety.org/data/plugin-versions/eventprime-event-calendar-management/4.3.2.1/event-prime.php
--- /home/deploy/wp-safety.org/data/plugin-versions/eventprime-event-calendar-management/4.3.2.0/event-prime.php	2026-04-03 06:06:20.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/eventprime-event-calendar-management/4.3.2.1/event-prime.php	2026-04-20 09:26:20.000000000 +0000
@@ -16,7 +16,7 @@
  * Plugin Name:       EventPrime – Modern Events Calendar, Bookings and Tickets
  * Plugin URI:        https://theeventprime.com
  * Description:       Beginner-friendly Events Calendar plugin to create free as well as paid Events. Includes Event Types, Event Sites & Performers too.
- * Version:           4.3.2.0
+ * Version:           4.3.2.1
  * Author:            EventPrime Event Calendar
  * Author URI:        https://theeventprime.com/
  * License:           GPL-2.0+
@@ -35,7 +35,7 @@
  * Start at version 1.0.0 and use SemVer - https://semver.org
  * Rename this for your plugin and update it as you release new versions.
  */
-define( 'EVENTPRIME_VERSION', '4.3.2.0' );
+define( 'EVENTPRIME_VERSION', '4.3.2.1' );
 define('EM_DB_VERSION',4.0);
 if( ! defined( 'EP_PLUGIN_FILE' ) ) {
     define( 'EP_PLUGIN_FILE', __FILE__ );
--- /home/deploy/wp-safety.org/data/plugin-versions/eventprime-event-calendar-management/4.3.2.0/includes/class-ep-ajax.php	2026-04-03 06:06:20.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/eventprime-event-calendar-management/4.3.2.1/includes/class-ep-ajax.php	2026-04-20 09:26:20.000000000 +0000
@@ -545,12 +545,14 @@
-                $response->item_total     = (float)$data['ep_event_booking_total_tickets'];                
-                
-                // $redirect                 = esc_url( add_query_arg( array( 'order_id' => $new_post_id ), get_permalink( ep_get_global_settings( 'booking_details_page' ) ) ) );
-                $redirect                 = add_query_arg( array( 'order_id' => $new_post_id ), esc_url( get_permalink( $ep_functions->ep_get_global_settings( 'booking_details_page' ) ) ) );
-                $response->redirect       = apply_filters( 'ep_booking_redirection_url', $redirect, $new_post_id );
-                wp_send_json_success( $response );
+                $response->item_total     = (float)$data['ep_event_booking_total_tickets'];
+                $response->flush_booking_timer_nonce = wp_create_nonce( 'flush_event_booking_timer_nonce' );
+                $response->order_key      = get_post_meta( $new_post_id, 'ep_order_key', true );
+                
+                // $redirect                 = esc_url( add_query_arg( array( 'order_id' => $new_post_id ), get_permalink( ep_get_global_settings( 'booking_details_page' ) ) ) );
+                $redirect                 = add_query_arg( array( 'order_id' => $new_post_id ), esc_url( get_permalink( $ep_functions->ep_get_global_settings( 'booking_details_page' ) ) ) );
+                $response->redirect       = apply_filters( 'ep_booking_redirection_url', $redirect, $new_post_id );
+                wp_send_json_success( $response );
             } else{
                 wp_send_json_error( array( 'error' => esc_html__( 'Security check failed. Please refresh the page and try again later.', 'eventprime-event-calendar-management' ) ) );
             }
@@ -571,55 +573,67 @@
-    public function paypal_sbpr() {
-        if ( ! check_ajax_referer( 'flush_event_booking_timer_nonce', 'security', false ) ) {
-            wp_send_json_error( array( 'error' => esc_html__( 'Security check failed. Please refresh the page and try again later.', 'eventprime-event-calendar-management' ) ) );
-        }
-
-        if ( empty( $_POST ) ) {
-            wp_send_json_error( array( 'error' => esc_html__( 'Data Not Found', 'eventprime-event-calendar-management' ) ) );
-        }
-
-        $ep_functions  = new Eventprime_Basic_Functions;
-        $data          = $ep_functions->ep_sanitize_input( $_POST['data'] ?? array() );
-        if ( ! is_array( $data ) ) {
-            wp_send_json_error( array( 'error' => esc_html__( 'Invalid payment data.', 'eventprime-event-calendar-management' ) ) );
-        }
-        $booking_id    = absint( $_POST['order_id'] ?? 0 );
-
-        $payment_amount = $data['purchase_units'][0]['amount']['value'] ?? '';
-        $paypal_order_id = isset( $data['id'] ) ? sanitize_text_field( $data['id'] ) : ( isset( $data['order_id'] ) ? sanitize_text_field( $data['order_id'] ) : '' );
-
-        if ( empty( $booking_id ) || empty( $data ) || $payment_amount === '' ) {
-            wp_send_json_error( array( 'error' => esc_html__( 'Invalid payment data.', 'eventprime-event-calendar-management' ) ) );
-        }
-
-        $order_info = maybe_unserialize( get_post_meta( $booking_id, 'em_order_info', true ) );
-        $booking_status = get_post_meta( $booking_id, 'em_status', true );
-        $booking_user = absint( get_post_meta( $booking_id, 'em_user', true ) );
-
-        if ( ! empty( $booking_user ) && get_current_user_id() !== $booking_user ) {
-            wp_send_json_error( array( 'error' => esc_html__( 'You are not allowed to confirm this booking.', 'eventprime-event-calendar-management' ) ) );
-        }
-
-        if ( empty( $order_info['booking_total'] ) ) {
-            wp_send_json_error( array( 'error' => esc_html__( 'Payment amount mismatch.', 'eventprime-event-calendar-management' ) ) );
-        }
+    public function paypal_sbpr() {
+        $booking_id    = absint( $_POST['order_id'] ?? 0 );
+        $request_order_key = isset( $_POST['order_key'] ) ? sanitize_text_field( $_POST['order_key'] ) : '';
+        $stored_order_key = ! empty( $booking_id ) ? get_post_meta( $booking_id, 'ep_order_key', true ) : '';
+        $is_valid_nonce = check_ajax_referer( 'flush_event_booking_timer_nonce', 'security', false );
+        $is_valid_order_key = ( ! empty( $stored_order_key ) && ! empty( $request_order_key ) && hash_equals( (string) $stored_order_key, (string) $request_order_key ) );
+
+        if ( empty( $_POST ) ) {
+            wp_send_json_error( array( 'error' => esc_html__( 'Data Not Found', 'eventprime-event-calendar-management' ) ) );
+        }
+
+        $ep_functions  = new Eventprime_Basic_Functions;
+        $data          = $ep_functions->ep_sanitize_input( $_POST['data'] ?? array() );
+        if ( ! is_array( $data ) ) {
+            wp_send_json_error( array( 'error' => esc_html__( 'Invalid payment data.', 'eventprime-event-calendar-management' ) ) );
+        }
+        $payment_amount = $data['purchase_units'][0]['amount']['value'] ?? '';
+        $paypal_order_id = isset( $data['id'] ) ? sanitize_text_field( $data['id'] ) : ( isset( $data['order_id'] ) ? sanitize_text_field( $data['order_id'] ) : '' );
+
+        if ( empty( $booking_id ) || empty( $data ) || $payment_amount === '' ) {
+            wp_send_json_error( array( 'error' => esc_html__( 'Invalid payment data.', 'eventprime-event-calendar-management' ) ) );
+        }
+
+        $order_info = maybe_unserialize( get_post_meta( $booking_id, 'em_order_info', true ) );
+        $booking_status = get_post_meta( $booking_id, 'em_status', true );
+        $booking_user = absint( get_post_meta( $booking_id, 'em_user', true ) );
+        $stored_random_order_id = sanitize_text_field( (string) get_post_meta( $booking_id, 'em_random_order_id', true ) );
+
+        if ( empty( $order_info['booking_total'] ) ) {
+            wp_send_json_error( array( 'error' => esc_html__( 'Payment amount mismatch.', 'eventprime-event-calendar-management' ) ) );
+        }
 
         if ( ! empty( $booking_status ) && strtolower( $booking_status ) === 'completed' ) {
             wp_send_json_error( array( 'error' => esc_html__( 'Booking already completed.', 'eventprime-event-calendar-management' ) ) );
         }
 
-        $verify = $this->verify_paypal_order( $paypal_order_id, $order_info['booking_total'], $ep_functions->ep_get_global_settings( 'currency' ), $booking_id );
-        if ( is_wp_error( $verify ) ) {
-            wp_send_json_error( array( 'error' => $verify->get_error_message() ) );
-        }
-
-        $payment_status = strtolower( $verify['status'] );
-        $payment_amount = $verify['amount'];
+        $verify = $this->verify_paypal_order( $paypal_order_id, $order_info['booking_total'], $ep_functions->ep_get_global_settings( 'currency' ), $booking_id );
+        if ( is_wp_error( $verify ) ) {
+            wp_send_json_error( array( 'error' => $verify->get_error_message() ) );
+        }
+
+        $verified_random_order_id = sanitize_text_field( $verify['custom_id'] ?? '' );
+        $is_verified_random_order = ( ! empty( $stored_random_order_id ) && ! empty( $verified_random_order_id ) && hash_equals( (string) $stored_random_order_id, (string) $verified_random_order_id ) );
+
+        if ( ! empty( $stored_random_order_id ) && ! $is_verified_random_order ) {
+            wp_send_json_error( array( 'error' => esc_html__( 'You are not allowed to confirm this booking.', 'eventprime-event-calendar-management' ) ) );
+        }
+
+        if ( ! empty( $booking_user ) && get_current_user_id() !== $booking_user && ! $is_valid_order_key && ! $is_verified_random_order ) {
+            wp_send_json_error( array( 'error' => esc_html__( 'You are not allowed to confirm this booking.', 'eventprime-event-calendar-management' ) ) );
+        }
+
+        if ( ! $is_valid_nonce && ! $is_valid_order_key && ! $is_verified_random_order ) {
+            wp_send_json_error( array( 'error' => esc_html__( 'Security check failed. Please refresh the page and try again later.', 'eventprime-event-calendar-management' ) ) );
+        }
+
+        $payment_status = strtolower( $verify['status'] );
+        $payment_amount = $verify['amount'];

Exploit Outline

The exploit targets the `/wp-admin/admin-ajax.php` endpoint using the `ep_cancel_current_booking_process` action. An unauthenticated attacker first obtains a valid `event-registration-form-nonce` from an event booking page. They then send a POST request containing the nonce, a target `event_id`, and a JSON-encoded `ticket_data` object specifying seat UIDs (e.g., "0-0") and areas. Because the AJAX handler only verifies the nonce and fails to perform any capability checks (e.g., `current_user_can()`), the server proceeds to update the `em_seat_data` post meta in the database, effectively releasing seat holds or resetting seating configuration for any event.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.