ilGhera Support System for WooCommerce <= 1.2.6 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Ticket Deletion
Description
The ilGhera Support System for WooCommerce plugin for WordPress is vulnerable to unauthorized modification and loss of data due to a missing capability check on the 'delete_single_ticket_callback' and 'change_ticket_status_callback' functions in all versions up to, and including, 1.2.6. This makes it possible for authenticated attackers, with Subscriber-level access and above, to delete arbitrary support tickets and modify their status.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.2.6What Changed in the Fix
Changes introduced in v1.2.7
Source Code
WordPress.org SVN# Vulnerability Research Plan: CVE-2025-14034 (ilGhera Support System for WooCommerce) ## 1. Vulnerability Summary The **ilGhera Support System for WooCommerce** plugin (versions <= 1.2.6) contains a missing authorization vulnerability in its AJAX handlers for ticket management. Specifically, the f…
Show full research plan
Vulnerability Research Plan: CVE-2025-14034 (ilGhera Support System for WooCommerce)
1. Vulnerability Summary
The ilGhera Support System for WooCommerce plugin (versions <= 1.2.6) contains a missing authorization vulnerability in its AJAX handlers for ticket management. Specifically, the functions delete_single_ticket_callback and change_ticket_status_callback are registered via the wp_ajax_ hook but fail to implement capability checks (e.g., current_user_can()) or ownership validation. This allows any authenticated user (starting from the Subscriber role) to delete or modify the status of arbitrary support tickets by sending a crafted AJAX request with a valid nonce.
2. Attack Vector Analysis
- Endpoints:
/wp-admin/admin-ajax.php - AJAX Actions:
delete-ticket(for arbitrary ticket deletion)change-ticket-status(for arbitrary status modification)
- Vulnerable Functions:
delete_single_ticket_callbackandchange_ticket_status_callbackinincludes/class-wc-support-system.php. - Authentication: Authenticated (Subscriber-level or higher).
- Preconditions:
- The plugin must be active.
- WooCommerce must be installed and active.
- A valid nonce for the specific action must be obtained.
- The attacker must know (or brute-force) the
ticket_id.
3. Code Flow
- Registration: In
includes/class-wc-support-system.php, the__constructmethod (lines 69-71) registers the AJAX handlers:add_action( 'wp_ajax_delete-ticket', array( $this, 'delete_single_ticket_callback' ) ); add_action( 'wp_ajax_change-ticket-status', array( $this, 'change_ticket_status_callback' ) ); - Execution: When a logged-in user sends a request to
admin-ajax.phpwithaction=delete-ticket, WordPress executesdelete_single_ticket_callback. - Missing Check: The callback functions verify the nonce (see section 4) but lack a
current_user_can('manage_options')check or logic to ensure theticket_idbelongs to the requesting user. - Sink: The functions perform direct database deletions or updates on the support ticket tables based on the provided
ticket_idparameter.
4. Nonce Acquisition Strategy
The plugin exposes the necessary nonces to authenticated users via wp_localize_script in the wss_scripts (front-end) and wss_admin_scripts (back-end) functions.
Steps to obtain nonces:
- Identify the Support Page: The plugin enqueues its scripts on the page defined in the WordPress option
wss-page. - Setup: Ensure a page contains the shortcode
[support-tickets-table]. - Observation: The scripts are localized under the global JavaScript object
wssData. - Acquisition:
- Log in as a Subscriber.
- Navigate to the support page.
- Use
browser_evalto extract the nonces:deleteSingleTicketNonce:wssData.deleteSingleTicketNoncechangeTicketStatusNonce:wssData.changeTicketStatusNonce
Action Strings (from lines 112-113):
wss-delete-single-ticketwss-change-ticket-status
5. Exploitation Strategy
Arbitrary Ticket Deletion
- Method: POST
- URL:
http://<target>/wp-admin/admin-ajax.php - Body (URL-encoded):
action:delete-ticketticket_id:[ID_OF_TARGET_TICKET]security:[deleteSingleTicketNonce]
- Expected Response:
1(or JSON success message, depending on internal callback implementation).
Arbitrary Ticket Status Change
- Method: POST
- URL:
http://<target>/wp-admin/admin-ajax.php - Body (URL-encoded):
action:change-ticket-statusticket_id:[ID_OF_TARGET_TICKET]status:closed(or other valid status)security:[changeTicketStatusNonce]
6. Test Data Setup
- Prerequisite: Install and activate WooCommerce.
- Plugin Setup: Activate ilGhera Support System for WooCommerce.
- Configuration:
- Create a page titled "Support" with content
[support-tickets-table]. - Set the plugin option
wss-pageto the ID of this new page (wp option update wss-page <page_id>).
- Create a page titled "Support" with content
- User Creation:
- Create an Admin user (to create "victim" data).
- Create a Subscriber user (the attacker).
- Data Creation:
- As Admin, create a support ticket.
- Use
wp db queryto find the ID of the created ticket in the custom table (typicallywp_wss_tickets).
7. Expected Results
- Deletion: The ticket record with the specified
ticket_idshould be removed from the database. - Status Change: The
statuscolumn for the specifiedticket_idshould be updated to the attacker-supplied value. - Unauthorized Access: Both actions should succeed even when performed by a Subscriber who does not own the ticket and has no administrative privileges.
8. Verification Steps
After performing the HTTP requests:
- Verify Deletion:
wp db query "SELECT * FROM wp_wss_tickets WHERE id = [ID_OF_TARGET_TICKET]" # Expected: Empty result - Verify Status Change:
wp db query "SELECT status FROM wp_wss_tickets WHERE id = [ID_OF_TARGET_TICKET]" # Expected: The status matches the value sent in the exploit
9. Alternative Approaches
If the ticket_id is not passed as ticket_id, try id. If the nonce parameter is not security, try nonce or wss_nonce, as these are common patterns in ilGhera plugins. Check the js/wss.js file via http_request if parameter names need confirmation.
Summary
The ilGhera Support System for WooCommerce plugin for WordPress is vulnerable to unauthorized data modification and loss due to missing capability and ownership checks in its AJAX handlers. This allow authenticated users, such as Subscribers, to delete arbitrary support tickets or modify their status by providing a valid nonce and ticket ID.
Vulnerable Code
// includes/class-wc-support-system.php around line 870 public function change_ticket_status_callback() { $ticket_id = isset( $_POST['ticket_id'] ) ? sanitize_text_field( wp_unslash( $_POST['ticket_id'] ) ) : ''; $new_status = isset( $_POST['status'] ) ? sanitize_text_field( wp_unslash( $_POST['status'] ) ) : ''; $update_time = current_time( 'mysql' ); if ( $ticket_id && $new_status ) { $this->update_ticket( $ticket_id, $update_time, $new_status ); --- // includes/class-wc-support-system.php around line 1332 public function delete_single_ticket_callback() { if ( isset( $_POST['wss-delete-single-ticket-nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wss-delete-single-ticket-nonce'] ) ), 'wss-delete-single-ticket' ) ) { $ticket_id = isset( $_POST['ticket_id'] ) ? sanitize_text_field( wp_unslash( $_POST['ticket_id'] ) ) : ''; if ( $ticket_id ) { $this->delete_ticket( $ticket_id );
Security Fix
Only in /home/deploy/wp-safety.org/data/plugin-versions/wc-support-system/1.2.6: composer.json Only in /home/deploy/wp-safety.org/data/plugin-versions/wc-support-system/1.2.6: composer.lock @@ -872,6 +872,18 @@ if ( $ticket_id && $new_status ) { + // Check user capabilities + $has_admin_capability = current_user_can( 'manage_woocommerce' ) || current_user_can( 'manage_options' ); + + if ( ! $has_admin_capability ) { + // If not admin/shop manager, check if user owns the ticket + $ticket = self::get_ticket( $ticket_id ); + if ( ! $ticket || (int) $ticket->user_id !== get_current_user_id() ) { + wp_send_json_error( array( 'message' => __( 'You do not have permission to change this ticket status.', 'wc-support-system' ) ) ); + exit; + } + } + $this->update_ticket( $ticket_id, $update_time, $new_status ); $new_label = self::get_ticket_status_label( $new_status ); @@ -1263,6 +1275,12 @@ if ( isset( $_POST['wss-delete-single-thread-nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wss-delete-single-thread-nonce'] ) ), 'wss-delete-single-thread' ) ) { + // Check user capabilities - only shop managers and administrators can delete threads + if ( ! current_user_can( 'manage_woocommerce' ) && ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'You do not have permission to delete threads.', 'wc-support-system' ) ) ); + exit; + } + $thread_id = isset( $_POST['thread_id'] ) ? sanitize_text_field( wp_unslash( $_POST['thread_id'] ) ) : $thread_id; if ( $thread_id ) { @@ -1332,6 +1350,12 @@ if ( isset( $_POST['wss-delete-single-ticket-nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wss-delete-single-ticket-nonce'] ) ), 'wss-delete-single-ticket' ) ) { + // Check user capabilities - only shop managers and administrators can delete tickets + if ( ! current_user_can( 'manage_woocommerce' ) && ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'You do not have permission to delete tickets.', 'wc-support-system' ) ) ); + exit; + } + $ticket_id = isset( $_POST['ticket_id'] ) ? sanitize_text_field( wp_unslash( $_POST['ticket_id'] ) ) : ''; if ( $ticket_id ) {
Exploit Outline
The exploit target the WordPress AJAX endpoint (/wp-admin/admin-ajax.php) using an authenticated session (Subscriber role or higher). 1. The attacker logs in and visits a page where the plugin's scripts are enqueued (such as the support ticket dashboard). 2. The attacker extracts the required nonces for ticket deletion or status modification from the global 'wssData' JavaScript object localized in the page source. 3. To delete a ticket, the attacker sends a POST request with the 'action' parameter set to 'delete-ticket', the target 'ticket_id', and the 'wss-delete-single-ticket-nonce'. 4. To modify a ticket status, the attacker sends a POST request with the 'action' set to 'change-ticket-status', the target 'ticket_id', the desired 'status' string, and the 'changeTicketStatusNonce' (passed as the 'security' or 'wss-change-ticket-status' parameter depending on implementation details). 5. Because the vulnerable versions lack capability checks, the plugin processes the request for any valid ID regardless of who owns the ticket or whether the user is an administrator.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.