1. Vulnerability Background
What is this vulnerability?
- The Events Calendar WordPress plugin contains missing authorization checks in its Custom Tables V1 migration AJAX handlers.
- Specifically, the functions
start_migration,cancel_migration, andrevert_migrationinsrc/Events/Custom_Tables/V1/Migration/Ajax.phpdid not verify the current user’s capabilities before executing sensitive migration actions. - As a result, any authenticated user with a subscriber-level account or higher could invoke these actions.
Why is it critical/important?
- These handlers control database migration operations for event custom tables.
- An attacker can start or cancel an administrative migration process, and critically can call the revert action.
- The revert action can drop the custom tables entirely, potentially causing significant data loss and service disruption.
- The issue is an authorization failure rather than a misconfiguration or information leak, making it a high-impact vulnerability.
What systems/versions are affected?
- The vulnerability affects The Events Calendar plugin for WordPress in all versions up to and including 6.15.13.
- Any WordPress site running the plugin in a vulnerable version is at risk if it allows authenticated users with subscriber-level access or above.
2. Technical Details
Root cause analysis
- The vulnerable code used
check_ajax_referer(self::NONCE_ACTION)to validate the AJAX request. check_ajax_refererverifies the nonce and helps prevent CSRF, but it does not enforce user permissions.- The targeted methods lacked a subsequent
current_user_can(...)check. - This omission meant authenticated users could call admin-level AJAX endpoints without being authorized to perform the underlying operation.
Attack vector and exploitation conditions
- The exploit path is an authenticated AJAX request to WordPress’
admin-ajax.phpendpoint. - The attacker needs:
- a valid authenticated account on the site,
- knowledge of the plugin’s AJAX action names and nonce,
- the ability to issue HTTP requests to the application.
- Since the plugin exposes these actions through its UI and scripts, the attacker can discover the endpoints through browser developer tools or page source inspection.
Security implications
- Unauthorized users can:
- trigger event custom table migrations,
- cancel a legitimate migration in progress,
- revert the migration and drop custom event tables.
- The revert action is especially dangerous because it can destroy application data without requiring administrator credentials.
- The vulnerability also highlights a broader issue: several AJAX handlers in the same migration code path were missing capability checks, indicating a systemic authorization lapse.
3. Patch Analysis
What code changes were made?
- In
src/Events/Custom_Tables/V1/Migration/Ajax.php, the patch adds capability checks immediately after nonce validation in sensitive methods. - Example change pattern:
check_ajax_referer(self::NONCE_ACTION);if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error([...]); return; }
- The patch was applied to:
start_migrationcancel_migrationrevert_migration
- Related handlers such as
send_reportandpaginate_eventsalso received similar checks, indicating a consistent fix across migration-related AJAX endpoints.
How do these changes fix the vulnerability?
- They enforce that only users with administrative capabilities may execute migration operations.
- Non-admin authenticated users now receive a JSON error and the handler exits before any sensitive action occurs.
- This removes the gap where nonce validation alone was mistakenly treated as sufficient authorization.
Security improvements introduced
- Added least-privilege enforcement on migration endpoints.
- Prevented low-privilege accounts from manipulating database migration state.
- Standardized authorization behavior for multiple AJAX handlers in the migration class.
- Reduced the attack surface for the Custom Tables V1 migration workflow.
4. Proof of Concept (PoC) Guide
Prerequisites for exploitation
- WordPress site running The Events Calendar plugin version 6.15.13 or earlier.
- A valid authenticated account with subscriber-level privileges or higher.
- Access to the plugin’s AJAX nonce/token, typically via client-side scripts or page source.
- Ability to send HTTP POST requests to
/wp-admin/admin-ajax.php.
Step-by-step exploitation approach
- Authenticate to the WordPress site with a non-admin user.
- Identify the AJAX action name and nonce:
- inspect the plugin’s admin/migration pages,
- locate the localized JavaScript object or hidden form fields that contain the nonce.
- Construct a POST request to
/wp-admin/admin-ajax.php. - Include the appropriate parameters:
action=<migration_ajax_action>tec_ajax_nonce=<nonce>or the plugin-specific nonce field- for a safe test, include
tec_events_custom_tables_v1_migration_dry_run=1if callingstart_migration
- Send the request and analyze the JSON response.
Expected behavior vs exploited behavior
- Expected: only administrators should be able to execute migration controls. low-privilege requests should be rejected with a permission error.
- Exploited: a subscriber-level user receives a successful response and the requested migration action is executed, including the possibility of harmful revert behavior.
How to verify the vulnerability exists
- Issue a request to the relevant AJAX endpoint as a non-admin user.
- If the response indicates success or returns migration status information, the site is vulnerable.
- On patched systems, the request should return an error such as “You do not have permission to migrate events.”
- Safe verification can use the dry-run parameter to avoid destructive changes while confirming that the handler is accessible.
5. Recommendations
Mitigation strategies
- Upgrade The Events Calendar plugin to a version later than 6.15.13 immediately.
- If patching is not possible, restrict access to
admin-ajax.phpfor low-privilege authenticated users where feasible. - Disable or limit the plugin if it is not required.
Detection methods
- Monitor admin AJAX traffic for migration-related action names from non-admin accounts.
- Alert on requests to
admin-ajax.phpwith parameters matchingstart_migration,cancel_migration, orrevert_migration. - Correlate these requests with subscriber or editor sessions.
- Check for unusual migration rollback activity or missing event custom tables.
Best practices to prevent similar issues
- Always enforce both CSRF protection and explicit capability checks on privileged AJAX handlers.
- Validate permissions before performing any database schema or migration operation.
- Use
current_user_can()with an appropriate capability, not just nonce validation. - Review plugin endpoints for authorization gaps, especially in admin-facing functionality.
- Log and audit sensitive actions so unauthorized attempts can be detected quickly.