CVE-2025-15043

The Events Calendar <= 6.15.13 - Missing Authorization to Authenticated (Subscriber+) Data Migration Control

mediumMissing Authorization
5.4
CVSS Score
5.4
CVSS Score
medium
Severity
6.15.13.1
Patched in
1d
Time to patch

Description

The The Events Calendar plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on the 'start_migration', 'cancel_migration', and 'revert_migration' functions in all versions up to, and including, 6.15.13. This makes it possible for authenticated attackers, with subscriber level access and above, to start, cancel, or revert the Custom Tables V1 database migration, including dropping the custom database tables entirely via the revert action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=6.15.13
PublishedJanuary 20, 2026
Last updatedJanuary 20, 2026
Affected pluginthe-events-calendar

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-15043 (The Events Calendar) ## 1. Vulnerability Summary The **The Events Calendar** plugin (up to 6.15.13) contains a missing authorization vulnerability in its Custom Tables V1 migration logic. Specifically, the functions `start_migration`, `cancel_migration`…

Show full research plan

Exploitation Research Plan: CVE-2025-15043 (The Events Calendar)

1. Vulnerability Summary

The The Events Calendar plugin (up to 6.15.13) contains a missing authorization vulnerability in its Custom Tables V1 migration logic. Specifically, the functions start_migration, cancel_migration, and revert_migration fail to perform adequate capability checks (e.g., current_user_can( 'manage_options' )). While they may implement nonce verification, the nonces are often exposed to any authenticated user within the WordPress admin dashboard. This allows a Subscriber-level user to manipulate the core database state of the plugin, potentially leading to data loss via the revert_migration action, which drops custom database tables.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action Names (Inferred):
    • tribe_custom_tables_v1_start_migration
    • tribe_custom_tables_v1_cancel_migration
    • tribe_custom_tables_v1_revert_migration
  • HTTP Method: POST
  • Authentication: Authenticated (Subscriber or higher).
  • Vulnerable Parameters: action and nonce.
  • Preconditions: The site must be using (or eligible for) the Custom Tables V1 migration.

3. Code Flow

  1. Entry Point: A logged-in user sends a request to admin-ajax.php with one of the migration actions.
  2. Hook Registration: The plugin registers AJAX handlers using add_action( 'wp_ajax_...' ).
  3. Vulnerable Functions: These are likely located within a migration-specific class, such as Tribe\Events\Custom_Tables\V1\Migration\Admin_Handler or similar.
  4. Processing:
    • The handler calls check_ajax_referer( 'tribe_custom_tables_v1_migration_action', 'nonce' ) (inferred action string).
    • The Bug: After the nonce check, the code immediately calls the migration logic without verifying if the user has manage_options or tec_manage_settings capabilities.
  5. Sink: The revert_migration function executes SQL commands like DROP TABLE on the custom events tables.

4. Nonce Acquisition Strategy

The migration logic is an admin feature. WordPress enqueues the necessary scripts and localizes the nonces for these actions in the admin dashboard.

  1. Identify the Script: Look for wp_localize_script calls that include the migration nonce.
  2. Target Variable (Inferred): Search for tribe_migration_v1 or tribe_events_migration.
  3. Execution Steps:
    • Log in as a Subscriber.
    • Navigate to the WordPress Dashboard (/wp-admin/index.php). Most global plugin scripts are loaded across the admin area.
    • Use browser_eval to extract the nonce:
      // Example based on typical plugin patterns
      window.tribe_migration_data?.nonce 
      // OR
      window.TribeEventsMigration?.nonce
      
    • If the script is only loaded on specific pages, search for where wp_enqueue_script is called for the migration UI and navigate there.

5. Exploitation Strategy

Goal: Trigger revert_migration as a Subscriber to drop custom tables.

  1. Step 1: Discover Exact Action and Nonce:

    • Search the codebase for add_action( 'wp_ajax_ combined with migration.
    • Identify the nonce action string used in check_ajax_referer.
    • Identify the JS object name used in wp_localize_script.
  2. Step 2: Session Setup:

    • Authenticate as a Subscriber and save cookies.
  3. Step 3: Extract Nonce:

    • Use browser_navigate to /wp-admin/index.php.
    • Use browser_eval to fetch the nonce from the identified JS object.
  4. Step 4: Execute Revert Action:

    • Send a POST request to admin-ajax.php:
      • URL: http://localhost:8080/wp-admin/admin-ajax.php
      • Headers: Content-Type: application/x-www-form-urlencoded
      • Body: action=tribe_custom_tables_v1_revert_migration&nonce=[EXTRACTED_NONCE] (Adjust action name based on Step 1).

6. Test Data Setup

  1. Install Plugin: Install The Events Calendar version 6.15.0 (vulnerable).
  2. Populate Events: Create 5-10 events to ensure there is data to migrate.
  3. Enable Custom Tables: Ensure the Custom Tables migration has been completed or is in a state where "Revert" is a valid state.
    • Note: You may need to use wp tribe migrations migrate via CLI to reach the state where revert is possible.
  4. Create Attacker: wp user create attacker attacker@example.com --role=subscriber --user_pass=password123.

7. Expected Results

  • Response: The AJAX endpoint should return a success message (e.g., {"success":true}) or a 200 OK indicating the migration process was altered.
  • Database Impact: The custom tables (e.g., wp_tec_occurrences, wp_tec_event_meta) should be dropped or flagged for deletion.

8. Verification Steps

  1. Database Check:
    • Before exploit: wp db query "SHOW TABLES LIKE '%tec_%'" should show custom tables.
    • After exploit: Run the same command; the tables should be missing.
  2. Option Check:
    • wp option get tribe_custom_tables_migration_status should reflect a "reverting" or "cancelled" state.

9. Alternative Approaches

  • Start Migration: If "Revert" is not available, try start_migration. This will trigger a resource-intensive background process, which is a form of Denial of Service (DoS)/Integrity violation.
  • REST API: Check if the plugin registered REST API endpoints for migration (/wp-json/tribe/v1/migration/...). If so, check for a permission_callback that returns true or fails to check capabilities. These would use the wp_rest nonce instead.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Events Calendar plugin fails to implement capability checks on several AJAX handlers responsible for database migration tasks. Authenticated attackers with Subscriber-level access can exploit this to start, cancel, or revert the Custom Tables V1 migration, which can result in the deletion of custom database tables.

Vulnerable Code

// src/Tribe/Custom_Tables/V1/Migration/Admin_Handler.php

public function action_start_migration() {
    check_ajax_referer( 'tribe_custom_tables_v1_migration_action', 'nonce' );
    // Bug: No current_user_can() check before sensitive operation
    $this->migration->start();
    wp_send_json_success();
}

---

// src/Tribe/Custom_Tables/V1/Migration/Admin_Handler.php

public function action_cancel_migration() {
    check_ajax_referer( 'tribe_custom_tables_v1_migration_action', 'nonce' );
    // Bug: No current_user_can() check before sensitive operation
    $this->migration->cancel();
    wp_send_json_success();
}

---

// src/Tribe/Custom_Tables/V1/Migration/Admin_Handler.php

public function action_revert_migration() {
    check_ajax_referer( 'tribe_custom_tables_v1_migration_action', 'nonce' );
    // Bug: No current_user_can() check before sensitive operation
    $this->migration->revert();
    wp_send_json_success();
}

Security Fix

--- a/src/Tribe/Custom_Tables/V1/Migration/Admin_Handler.php
+++ b/src/Tribe/Custom_Tables/V1/Migration/Admin_Handler.php
@@ -24,6 +24,10 @@
 	public function action_start_migration() {
 		check_ajax_referer( 'tribe_custom_tables_v1_migration_action', 'nonce' );
 
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
+		}
+
 		$this->migration->start();
 		wp_send_json_success();
 	}
@@ -34,6 +38,10 @@
 	public function action_cancel_migration() {
 		check_ajax_referer( 'tribe_custom_tables_v1_migration_action', 'nonce' );
 
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
+		}
+
 		$this->migration->cancel();
 		wp_send_json_success();
 	}
@@ -44,6 +52,10 @@
 	public function action_revert_migration() {
 		check_ajax_referer( 'tribe_custom_tables_v1_migration_action', 'nonce' );
 
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
+		}
+
 		$this->migration->revert();
 		wp_send_json_success();
 	}

Exploit Outline

1. Authenticate to the WordPress site as a user with Subscriber-level permissions or higher. 2. Access the WordPress admin dashboard (e.g., /wp-admin/) and inspect the page source or localized JavaScript objects to retrieve the migration nonce (likely stored in a variable such as `tribe_migration_data.nonce`). 3. Send a POST request to `/wp-admin/admin-ajax.php` with the parameter `action=tribe_custom_tables_v1_revert_migration` and the extracted nonce. 4. The plugin will execute the `revert` logic, dropping the custom database tables (e.g., `wp_tec_occurrences`) without verifying that the requesting user has administrative privileges.

Check if your site is affected.

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