Display Eventbrite Events <= 6.5.6 - Missing Authorization
Description
The Display Eventbrite Events plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 6.5.6. 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:NTechnical Details
<=6.5.6What Changed in the Fix
Changes introduced in v6.5.7
Source Code
WordPress.org SVN### 1. Vulnerability Summary The **Display Eventbrite Events** plugin for WordPress (versions <= 6.5.6) is vulnerable to **Missing Authorization** in its AJAX handlers. Specifically, several AJAX actions registered with the `wp_ajax_nopriv_` hook (intended for unauthenticated users) fail to perform …
Show full research plan
1. Vulnerability Summary
The Display Eventbrite Events plugin for WordPress (versions <= 6.5.6) is vulnerable to Missing Authorization in its AJAX handlers. Specifically, several AJAX actions registered with the wp_ajax_nopriv_ hook (intended for unauthenticated users) fail to perform any capability checks or nonce verification. This allows unauthenticated attackers to invoke functions that interact with the Eventbrite API using the site's configured credentials, potentially exposing sensitive account information (organizations, events, organizers) or performing unauthorized API requests.
The vulnerability resides in the WidgetForEventbriteAPI\Includes\Widgets\Elementor\Eventbrite_Widget_Elementor_Helpers class, where handlers like get_organizations_for_key and send_events_for_key explicitly ignore nonce verification and lack current_user_can() checks.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Actions:
fetch_organizations_for_key,send_events_for_key,update_widget_content. - Payload Parameters:
action: The AJAX action name.token: An index (e.g.,0) corresponding to a stored API key, or a raw API token.organizationID: (For events) The Eventbrite organization ID.
- Authentication: None required (
wp_ajax_nopriv_hooks are used). - Preconditions: The plugin must have at least one Eventbrite API key configured in its settings (stored in the
widget-for-eventbrite-api-settingsoption).
3. Code Flow
- Entry Point: An unauthenticated request is sent to
admin-ajax.phpwithaction=fetch_organizations_for_key. - Hook Registration: In
includes/class-core.php(insidedefine_admin_hooksordefine_component_hooks), the plugin registers:add_action( 'wp_ajax_nopriv_fetch_organizations_for_key', array($widget_helpers, 'get_organizations_for_key') ); - Vulnerable Handler: The request is routed to
WidgetForEventbriteAPI\Includes\Widgets\Elementor\Eventbrite_Widget_Elementor_Helpers::get_organizations_for_key(). - Processing:
- The handler retrieves the
tokenfrom$_POST. - It explicitly skips nonce verification:
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a look up. - It calls
$this->utilities->map_api_index_to_key( $token ). Iftokenis0, it retrieves the first API key stored in the plugin's settings. - It then calls
get_organizations_list($token), which makes a remote request to the Eventbrite API using the server's key.
- The handler retrieves the
- Sink: The result (organization data) is returned directly to the unauthenticated user via
wp_send_json().
4. Nonce Acquisition Strategy
According to the source code in includes/widgets/elementor/class-eventbrite-widget-elementor-helpers.php, the functions get_organizations_for_key and send_events_for_key do not require a nonce.
The developer explicitly included // phpcs:ignore WordPress.Security.NonceVerification.Missing in these methods, confirming that the security check is absent by design. Therefore, no nonce acquisition is necessary for exploitation.
5. Exploitation Strategy
We will demonstrate the unauthorized access by retrieving the organization list associated with the site's configured Eventbrite API key.
Step 1: Retrieve Organizations
- Tool:
http_request - Method:
POST - URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=fetch_organizations_for_key&token=0 - Expected Response: A JSON array of organization objects.
Step 2: Retrieve Events (if Organization ID is found)
- Tool:
http_request - Method:
POST - URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=send_events_for_key&token=0&organizationID=[ID_FROM_STEP_1] - Expected Response: A JSON object mapping Eventbrite event IDs to titles.
6. Test Data Setup
To simulate a vulnerable environment, we must configure a mock API key in the plugin settings via WP-CLI:
# Set a dummy API key in the plugin settings option
wp option update widget-for-eventbrite-api-settings '{"key":[{"key":"THIS_IS_A_MOCK_KEY","label":"API Key 1"}],"webhook":"","background_api":0}' --format=json
7. Expected Results
- Success: The response from
fetch_organizations_for_keywill be a JSON response. If the key is valid, it returns Eventbrite data. If the key is dummy/invalid, the fact that the plugin attempts the API call and returns an Eventbrite error (e.g.,401 UnauthorizedorARGUMENTS_ERROR) instead of a WordPress403 Forbiddenconfirms the missing authorization check. - Failure: The server returns a
403 Forbiddenor0(default WordPress AJAX response for invalid actions).
8. Verification Steps
After performing the HTTP request, verify that the plugin indeed attempted to use the server-side credentials:
- Check the response body for strings like
"organizations"or Eventbrite-specific error messages ("error": "INVALID_AUTH"). - Use WP-CLI to confirm the action was registered as
nopriv:wp eval 'global $wp_filter; print_r($wp_filter["wp_ajax_nopriv_fetch_organizations_for_key"]);'
9. Alternative Approaches
If fetch_organizations_for_key is patched or restricted, try update_widget_content:
- Action:
update_widget_content - Payload:
action=update_widget_content&[other_params] - Note: This action is used by the Elementor widget to refresh content. Triggering it unauthenticated can be used to perform unauthorized cache refreshes or potentially manipulate widget displays depending on the (truncated) implementation of
update_elementor_widget_content.
Summary
The Display Eventbrite Events plugin for WordPress is vulnerable to unauthorized access and information disclosure due to missing capability checks and nonce verification on several AJAX handlers. Unauthenticated attackers can exploit this to leak sensitive Eventbrite account data, such as organization IDs, organizer lists, and venue information, by leveraging the site's stored API credentials.
Vulnerable Code
// includes/class-core.php add_action( 'wp_ajax_update_widget_content', array($widget_helpers, 'update_elementor_widget_content') ); add_action( 'wp_ajax_nopriv_update_widget_content', array($widget_helpers, 'update_elementor_widget_content') ); add_action( 'wp_ajax_fetch_organizations_for_key', array($widget_helpers, 'get_organizations_for_key') ); add_action( 'wp_ajax_nopriv_fetch_organizations_for_key', array($widget_helpers, 'get_organizations_for_key') ); add_action( 'wp_ajax_fetch_events_for_key', array($widget_helpers, 'send_events_for_key') ); add_action( 'wp_ajax_nopriv_fetch_events_for_key', array($widget_helpers, 'send_events_for_key') ); --- // includes/widgets/elementor/class-eventbrite-widget-elementor-helpers.php public function get_organizations_for_key() { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a look up $token = ( !empty( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '' ); $token = $this->utilities->map_api_index_to_key( $token ); wp_send_json( $this->get_organizations_list( $token ) ); } public function send_events_for_key() { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a look up $token = ( !empty( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : $this->get_default_api_key() ); $token = $this->utilities->map_api_index_to_key( $token ); $args = array( 'token' => $token, ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a look up if ( !empty( $_POST['organizationID'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a look up $args['organization_id'] = sanitize_text_field( wp_unslash( $_POST['organizationID'] ) ); }
Security Fix
@@ -170,19 +170,12 @@ add_action( 'wp_ajax_wfea_dismiss_notice', array($plugin_admin, 'wfea_dismiss_notice') ); add_filter( 'site_status_tests', array($plugin_admin, 'site_status_tests') ); add_action( 'wp_ajax_update_widget_content', array($widget_helpers, 'update_elementor_widget_content') ); - add_action( 'wp_ajax_nopriv_update_widget_content', array($widget_helpers, 'update_elementor_widget_content') ); add_action( 'wp_ajax_fetch_organizations_for_key', array($widget_helpers, 'get_organizations_for_key') ); - add_action( 'wp_ajax_nopriv_fetch_organizations_for_key', array($widget_helpers, 'get_organizations_for_key') ); add_action( 'wp_ajax_fetch_events_for_key', array($widget_helpers, 'send_events_for_key') ); - add_action( 'wp_ajax_nopriv_fetch_events_for_key', array($widget_helpers, 'send_events_for_key') ); add_action( 'wp_ajax_fetch_organizers_for_key', array($widget_helpers, 'send_organizers_for_key') ); - add_action( 'wp_ajax_nopriv_fetch_organizers_for_key', array($widget_helpers, 'send_organizers_for_key') ); add_action( 'wp_ajax_fetch_venues_options', array($widget_helpers, 'send_venues_options') ); - add_action( 'wp_ajax_nopriv_fetch_venues_options', array($widget_helpers, 'send_venues_options') ); add_action( 'wp_ajax_fetch_api_key_options', array($widget_helpers, 'send_api_key_options') ); - add_action( 'wp_ajax_nopriv_fetch_api_key_options', array($widget_helpers, 'send_api_key_options') ); add_action( 'wp_ajax_validate_date', array($widget_helpers, 'validate_date') ); - add_action( 'wp_ajax_nopriv_validate_date', array($widget_helpers, 'validate_date') ); add_action( 'init', function () { new \WidgetForEventbriteAPI\Includes\Widgets(); } ); @@ -191,34 +191,28 @@ } public function get_organizations_for_key() { - // phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a look up + check_ajax_referer( 'wfea-nonce', 'nonce' ); + if ( !current_user_can( 'edit_posts' ) ) { + wp_die( -1 ); + } $token = ( !empty( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '' ); $token = $this->utilities->map_api_index_to_key( $token ); wp_send_json( $this->get_organizations_list( $token ) ); } public function send_events_for_key() { - // phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a look up + check_ajax_referer( 'wfea-nonce', 'nonce' ); + if ( !current_user_can( 'edit_posts' ) ) { + wp_die( -1 ); + } $token = ( !empty( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : $this->get_default_api_key() ); $token = $this->utilities->map_api_index_to_key( $token ); $args = array( 'token' => $token, ); - // phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a look up if ( !empty( $_POST['organizationID'] ) ) { - // phpcs:ignore WordPress.Security.NonceVerification.Missing -- just a look up $args['organization_id'] = sanitize_text_field( wp_unslash( $_POST['organizationID'] ) ); }
Exploit Outline
To exploit this vulnerability, an unauthenticated attacker can send a POST request to the WordPress AJAX endpoint (/wp-admin/admin-ajax.php) with the 'action' parameter set to one of the vulnerable functions, such as 'fetch_organizations_for_key'. By providing a 'token' parameter (where '0' usually maps to the primary stored API key), the attacker can force the server to execute an API request to Eventbrite and return the JSON response containing sensitive organization and account data. No authentication or nonce is required as the plugin registers these actions using the 'wp_ajax_nopriv_' hook and explicitly skips nonce validation in the handler code.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.