CVE-2026-54826

SupportCandy – Helpdesk & Customer Support Ticket System <= 3.4.6 - Authenticated (Subscriber+) Insecure Direct Object Reference

mediumAuthorization Bypass Through User-Controlled Key
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.4.7
Patched in
9d
Time to patch

Description

The SupportCandy – Helpdesk & Customer Support Ticket System plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 3.4.6 due to missing validation on a user controlled key. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.

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.4.6
PublishedJune 17, 2026
Last updatedJune 25, 2026
Affected pluginsupportcandy

What Changed in the Fix

Changes introduced in v3.4.7

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan focuses on an **Authenticated Insecure Direct Object Reference (IDOR)** vulnerability in SupportCandy (<= 3.4.6). The vulnerability allows a user with **Subscriber-level access** (typically a "Customer" in SupportCandy) to perform unauthorized actions, specifically modifying or cl…

Show full research plan

This research plan focuses on an Authenticated Insecure Direct Object Reference (IDOR) vulnerability in SupportCandy (<= 3.4.6). The vulnerability allows a user with Subscriber-level access (typically a "Customer" in SupportCandy) to perform unauthorized actions, specifically modifying or closing tickets belonging to other users, due to a failure in the backend to verify ticket ownership.


1. Vulnerability Summary

  • Vulnerability: Insecure Direct Object Reference (IDOR).
  • Affected Component: AJAX actions that process ticket-related tasks, specifically wpsc_it_close_ticket.
  • Cause: The plugin validates the existence of a WordPress nonce but fails to verify if the ticket_id provided in the request belongs to the authenticated user's customer_id.
  • Impact: Integrity modification (I:L). An attacker can close tickets belonging to any other user if they know the ticket_id.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wpsc_it_close_ticket
  • Vulnerable Parameter: ticket_id
  • Authentication: Authenticated (Subscriber+)
  • Preconditions: The attacker must be logged in as a Subscriber (Customer) and have the ID of a victim's ticket.

3. Code Flow

  1. Entry Point: The client sends a POST request to admin-ajax.php with action=wpsc_it_close_ticket.
  2. JS Implementation (framework/scripts.js):
    function wpsc_it_close_ticket(ticket_id, nonce) {
      // ... confirms and checks for draft reply ...
      var data = { action: "wpsc_it_close_ticket", ticket_id, _ajax_nonce: nonce };
      jQuery.post(supportcandy.ajax_url, data, function (response) { ... });
    }
    
  3. Backend Processing: The backend handler for wpsc_it_close_ticket likely calls check_ajax_referer('wpsc_it_close_ticket', '_ajax_nonce').
  4. Vulnerable Sink: After nonce verification, the code proceeds to update the status of ticket_id in the psmsc_tickets table without checking if the current user is the "customer" associated with that ticket.

4. Nonce Acquisition Strategy

SupportCandy uses localized objects for its nonces. Since the attacker is an authenticated Subscriber, they can obtain a valid nonce for the wpsc_it_close_ticket action by interacting with their own ticket.

  1. Create a Personal Ticket: The attacker creates a legitimate ticket to gain access to the ticket-viewing interface.
  2. Access Ticket UI: Navigate to the page containing the [supportcandy] shortcode.
  3. Extract Nonce:
    • The nonce for specific ticket actions is often localized in the supportcandy object or embedded in the "Close" button's attributes.
    • Strategy: Use browser_eval to extract the general nonce from the supportcandy object or locate the "Close" button for the attacker's own ticket and parse its onclick handler.
    • Variable: window.supportcandy?.nonce (General nonce) or specific action nonces found in the DOM.

5. Exploitation Strategy

The exploit will demonstrate closing a victim's ticket from a Subscriber account.

Step-by-Step:

  1. Preparation: Identify a Victim ticket ID (e.g., 1).
  2. Session Initiation: Log in as the Attacker (Subscriber).
  3. Nonce Retrieval:
    • Navigate to the SupportCandy portal.
    • Create a dummy ticket (e.g., ID 2).
    • View the dummy ticket.
    • Extract the nonce for the wpsc_it_close_ticket action.
  4. Forged Request: Send a POST request via the http_request tool targeting the victim's ticket ID.

HTTP Request Payload:

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=wpsc_it_close_ticket&ticket_id=[VICTIM_TICKET_ID]&_ajax_nonce=[ATTACKER_NONCE]
    

6. Test Data Setup

  1. Victim User: Create a user victim_user (Subscriber).
  2. Attacker User: Create a user attacker_user (Subscriber).
  3. Victim Ticket: Log in as victim_user and create a ticket with subject "Confidential Issue". Note the ID.
  4. Portal Page: Create a WordPress page with the shortcode [supportcandy] so the UI is accessible.

7. Expected Results

  • Response: The server returns a success response (typically empty or a small HTML fragment/JSON).
  • Database Change: The status of the victim's ticket in the psmsc_tickets table changes to the ID corresponding to "Closed" (often 3 or 4 depending on configuration).

8. Verification Steps

  1. Check Ticket Status via WP-CLI:
    # Query the psmsc_tickets table for the victim's ticket status
    wp db query "SELECT status FROM wp_psmsc_tickets WHERE id = [VICTIM_TICKET_ID]"
    
  2. Confirm Status: Compare the status ID before and after the attack. A change indicates a successful IDOR.

9. Alternative Approaches

  • Action wpsc_it_get_duplicate_ticket: If closing is not possible, check if a Subscriber can duplicate any ticket ID, which might reveal contents in the duplication form (I:L, potentially C:L).
  • Action wpsc_get_individual_ticket: Check if this action (which asset/js/admin.js shows has no nonce
Research Findings
Static analysis — not yet PoC-verified

Summary

The SupportCandy plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) in versions up to 3.4.6. This allows authenticated attackers with Subscriber-level access (customers) to perform unauthorized actions, such as closing tickets belonging to other users, because the plugin fails to verify ticket ownership before processing AJAX requests.

Vulnerable Code

// framework/scripts.js @ 3.4.6
/**
 * Get close current ticket modal UI
 */
function wpsc_it_close_ticket(ticket_id, nonce) {
  if (wpsc_is_description_text()) {
    if (!confirm(supportcandy.translations.warning_message)) return;
  }

  var flag = confirm(supportcandy.translations.confirm);
  if (flag) {
    if (wpsc_is_description_text()) {
      wpsc_clear_saved_draft_reply(ticket_id);
    }
  } else {
    return;
  }

  // Vulnerable action: Sends ticket_id and nonce without sufficient backend ownership validation
  var data = { action: "wpsc_it_close_ticket", ticket_id, _ajax_nonce: nonce };
  jQuery.post(supportcandy.ajax_url, data, function (response) {
    wpsc_run_ajax_background_process();
    wpsc_after_close_ticket(ticket_id);
  });
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/supportcandy/3.4.6/asset/js/admin.js /home/deploy/wp-safety.org/data/plugin-versions/supportcandy/3.4.7/asset/js/admin.js
--- /home/deploy/wp-safety.org/data/plugin-versions/supportcandy/3.4.6/asset/js/admin.js	2026-03-23 04:55:58.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/supportcandy/3.4.7/asset/js/admin.js	2026-05-26 07:12:22.000000000 +0000
@@ -3600,6 +3600,59 @@
 }
 
 /**
+ * Get edit Agent Collision widget
+ */
+function wpsc_get_tw_agent_collision() {
+
+	wpsc_show_modal();
+	var data = { action: 'wpsc_get_tw_agent_collision' };
+	jQuery.post(
+		supportcandy.ajax_url,
+		data,
+		function (response) {
+
+			// Set to modal.
+			jQuery( '.wpsc-modal-header' ).text( response.title );
+			jQuery( '.wpsc-modal-body' ).html( response.body );
+			jQuery( '.wpsc-modal-footer' ).html( response.footer );
+			// Display modal.
+			wpsc_show_modal_inner_container();
+		}
+	);
+}
+
+/**
+ * Set edit Agent Collision widget
+ */
+function wpsc_set_tw_agent_collision(el) {
+	var form     = jQuery( '.wpsc-frm-edit-ac' )[0];
+	var dataform = new FormData( form );
+	
+	if (dataform.get( 'label' ).trim() == '') {
+		alert( supportcandy.translations.req_fields_missing );
+		return;
+	}
+	
+	jQuery( '.wpsc-modal-footer button' ).attr( 'disabled', true );
+	jQuery( el ).text( supportcandy.translations.please_wait );
+
+	jQuery.ajax(
+		{
+			url: supportcandy.ajax_url,
+			type: 'POST',
+			data: dataform,
+			processData: false,
+			contentType: false
+		}
+	).done(
+		function (res) {
+			wpsc_close_modal();
+			wpsc_get_ticket_widget();
+		}
+	);
+}
+
+/**
  * Ticket widget status
  */
 function wpsc_get_tw_ticket_status() {
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/supportcandy/3.4.6/changelog.txt /home/deploy/wp-safety.org/data/plugin-versions/supportcandy/3.4.7/changelog.txt
--- /home/deploy/wp-safety.org/data/plugin-versions/supportcandy/3.4.6/changelog.txt	2026-03-24 07:33:20.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/supportcandy/3.4.7/changelog.txt	2026-05-26 07:12:22.000000000 +0000
@@ -1,5 +1,12 @@
 == Changelog ==
 
+= 3.4.7 (May 26, 2026) =
+* New: Option to disable email notifications for specific tickets
+* New: Improved form validation to highlight incomplete fields on submission
+* New: Agent collision widget added
+* Fix: Security vulnerability resolved
+* Fix: Email notifications not sent to agents assigned via workflows
+
 = 3.4.6 (March 23, 2026) =
 * New: Added Merge Ticket feature (Pro) in Productivity Suite.
 * New: Added AI Assistant feature (Pro) in Productivity Suite.

Exploit Outline

1. Identification: The attacker identifies a target ticket ID belonging to another user. 2. Nonce Acquisition: The attacker logs into their own Subscriber account and creates or views their own ticket to extract a valid AJAX nonce for the `wpsc_it_close_ticket` action from the localized `supportcandy` JavaScript object or the ticket UI. 3. Payload Construction: The attacker prepares an unauthenticated AJAX request targeting `/wp-admin/admin-ajax.php`. 4. Execution: The attacker sends a POST request with the following body: `action=wpsc_it_close_ticket&ticket_id=[VICTIM_ID]&_ajax_nonce=[ATTACKER_NONCE]`. 5. Outcome: Because the backend handler (e.g., in `includes/admin/tickets/class-wpsc-individual-ticket.php`) validates the nonce but fails to verify that the requesting user's `customer_id` matches the `customer` field of the ticket, the victim's ticket is closed.

Check if your site is affected.

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