[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fl_3mJk1aLa5smXCme3G9nOzZz_6TlSxuPvtoExDKbxE":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":29,"research_verified":30,"research_rounds_completed":31,"research_plan":32,"research_summary":33,"research_vulnerable_code":34,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"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":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":40},"CVE-2026-40796","wppizza-a-restaurant-plugin-authenticated-subscriber-information-exposure","WPPizza – A Restaurant Plugin \u003C= 3.19.9 - Authenticated (Subscriber+) Information Exposure","The WPPizza – A Restaurant Plugin plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 3.19.9. This makes it possible for authenticated attackers, with Subscriber-level access and above, to extract sensitive user or configuration data.","wppizza",null,"\u003C=3.19.9","3.20","medium",4.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Exposure of Sensitive Information to an Unauthorized Actor","2026-04-29 00:00:00","2026-05-04 13:54:19",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Feda25531-3f5f-4f66-8c6a-fcb17342eac0?source=api-prod",6,[22,23,24,25,26,27,28],"ajax\u002Fadmin.ajax.wppizza.php","ajax\u002Fajax.wppizza.php","classes\u002Fadmin\u002Fclass.wppizza.admin.helpers.php","classes\u002Fadmin\u002Fclass.wppizza.install_update.php","classes\u002Fadmin\u002Fclass.wppizza.register_gateways.php","classes\u002Fadmin\u002Fclass.wppizza.user_caps.inc.php","classes\u002Fadmin\u002Fclass.wppizza.wp_admin.php","researched",false,3,"This research plan outlines the exploitation of **CVE-2026-40796**, an Information Exposure vulnerability in the **WPPizza** plugin. The vulnerability stems from the lack of capability checks in the plugin's administrative AJAX handlers, allowing any authenticated user (including **Subscribers**) to access sensitive sales data or order information by utilizing a nonce that is globally available in the WordPress admin footer.\n\n### 1. Vulnerability Summary\nThe vulnerability exists in the handling of the `wppizza_admin_ajax` action. While the plugin implements a WordPress nonce check to prevent CSRF, it fails to perform a capability check (e.g., `current_user_can('manage_options')`) to verify that the user has administrative privileges. \n\nCrucially, the nonce required for this check (`wppizza_ajax_nonce`) is rendered in the admin footer for **all** logged-in users who can access the dashboard, including those with the Subscriber role. Once a Subscriber obtains this nonce, they can trigger administrative AJAX functions that expose sensitive sales reports and configuration data.\n\n### 2. Attack Vector Analysis\n*   **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **AJAX Action:** `wppizza_admin_ajax` (registered in `classes\u002Fadmin\u002Fclass.wppizza.wp_admin.php`)\n*   **Vulnerable File:** `ajax\u002Fadmin.ajax.wppizza.php`\n*   **HTTP Method:** POST\n*   **Authentication:** Authenticated (Subscriber+)\n*   **Required Parameters:**\n    *   `action`: `wppizza_admin_ajax`\n    *   `vars[nonce]`: A valid `wppizza_ajax_nonce`.\n    *   `vars[field]`: `update-dashboard-widget` (triggers sales report exposure).\n\n### 3. Code Flow\n1.  **Registration:** In `classes\u002Fadmin\u002Fclass.wppizza.wp_admin.php`, the hook `wp_ajax_wppizza_admin_ajax` is registered to the method `set_admin_ajax`. This hook is only for logged-in users.\n2.  **Nonce Exposure:** The same class registers `wppizza_ajax_nonce` on the `admin_footer` hook (Line 42), ensuring the nonce is present in the HTML for any user viewing the admin area.\n3.  **Entry Point:** When a request is sent to `admin-ajax.php?action=wppizza_admin_ajax`, the function `set_admin_ajax` requires `ajax\u002Fadmin.ajax.wppizza.php`.\n4.  **Verification (Insufficient):** `ajax\u002Fadmin.ajax.wppizza.php` checks the nonce (Lines 26-36) using `wp_verify_nonce( $_POST['vars']['nonce'] , $wppizza_ajax_nonce )`. It **does not** check user capabilities.\n5.  **Execution:** After verification, it triggers `do_action('wppizza_ajax_admin', $wppizza_options)`.\n6.  **Sink:** The class `WPPIZZA_WP_ADMIN` listens to this action and executes `admin_ajax` (Line 35). Inside `admin_ajax`, if `vars[field]` is set to `update-dashboard-widget`, it instantiates `WPPIZZA_DASHBOARD_WIDGETS` and calls `wppizza_do_dashboard_widget_sales()`, which prints sales summaries to the output.\n\n### 4. Nonce Acquisition Strategy\nThe nonce is rendered in the footer of any admin page (e.g., `\u002Fwp-admin\u002Fprofile.php` or `\u002Fwp-admin\u002Findex.php`).\n\n1.  Log in as a **Subscriber**.\n2.  Navigate to `\u002Fwp-admin\u002Findex.php`.\n3.  The plugin enqueues a script or prints the nonce in the footer. Use `browser_eval` to find it. Based on the source, it is likely inside a script tag or localized object.\n4.  **JS Search Strategy:**\n    *   Check for `wppizza_admin_vars` or similar localization keys.\n    *   Check for the string `wppizza_ajax_nonce` in the page source.\n    *   Example: `browser_eval(\"window.wppizza_admin_vars?.nonce\")` or inspecting the HTML for a hidden input\u002Fscript.\n\n### 5. Exploitation Strategy\nExecute the following steps using the `http_request` tool:\n\n**Step 1: Obtain Nonce**\n*   Navigate to `\u002Fwp-admin\u002Findex.php` as a Subscriber.\n*   Search the HTML response for the `wppizza_ajax_nonce`. \n*   *Note:* The nonce action is `wppizza_ajax_nonce`.\n\n**Step 2: Request Sensitive Information**\n*   **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **Method:** POST\n*   **Content-Type:** `application\u002Fx-www-form-urlencoded`\n*   **Body:**\n    ```text\n    action=wppizza_admin_ajax&vars[field]=update-dashboard-widget&vars[nonce]=[EXTRACTED_NONCE]\n    ```\n\n### 6. Test Data Setup\n1.  **Plugin Configuration:** Ensure WPPizza is installed and activated.\n2.  **Order Data:** Use WP-CLI to create at least one dummy order so the sales widget has data to return:\n    *   `wp wppizza order create ...` (if available) or manually through the UI as admin.\n3.  **Attacker User:** Create a Subscriber user:\n    *   `wp user create attacker attacker@example.com --role=subscriber --user_pass=password`\n\n### 7. Expected Results\n*   The response should be an HTTP 200 OK.\n*   The body should contain HTML markup generated by `wppizza_do_dashboard_widget_sales()`, including sales statistics, revenue totals, or order counts which should be restricted to administrators.\n\n### 8. Verification Steps\n*   **Log Check:** Check the response for keywords like \"Sales\", \"Total\", or currency symbols formatted by `wppizza_format_price`.\n*   **Access Comparison:** Confirm that a Subscriber normally cannot see the \"WPPizza Sales\" widget on the dashboard.\n\n### 9. Alternative Approaches\nIf `update-dashboard-widget` does not return enough data, attempt to access order history via the frontend AJAX file `ajax\u002Fajax.wppizza.php`:\n*   **Action:** `wppizza_ajax`\n*   **Parameters:** `vars[type]=admin-view-order&vars[id]=1&vars[nonce]=[EXTRACTED_NONCE]`\n*   *Note:* `ajax\u002Fajax.wppizza.php` (Line 68) explicitly lists `admin-view-order` and `admin-order-history` as types that check the `wppizza_ajax_nonce`. If this nonce is shared, a Subscriber can view full details of any order by iterating IDs.","The WPPizza plugin is vulnerable to information exposure because its AJAX handlers for administrative functions check only for a valid nonce but fail to verify user capabilities. Since the required nonce is globally exposed in the WordPress admin footer to all logged-in users, including Subscribers, an attacker can extract sensitive sales data, revenue statistics, and full order details.","\u002F* ajax\u002Fadmin.ajax.wppizza.php Lines 26-36 *\u002F\n$wppizza_ajax_nonce = '' . WPPIZZA_PREFIX . '_ajax_nonce';\n\u002F* --- skip nonce check for all '...nag_dismiss' notices --- *\u002F\nif( isset( $_POST['vars']['type'] ) && stristr($_POST['vars']['type'], 'nag_dismiss') !== false ){\n\t\u002F\u002Fskip nonce check\n}else{\n\tif (! isset( $_POST['vars']['nonce'] ) || !wp_verify_nonce(  $_POST['vars']['nonce'] , $wppizza_ajax_nonce ) ) {\n\t\theader('HTTP\u002F1.0 403 Forbidden [A]', true, 403);\n\t\tprint\"Forbidden [A]. Invalid Nonce.\";\n\t\texit; \u002F\u002Fjust for good measure\n\t}\n}\n\n---\n\n\u002F* ajax\u002Fajax.wppizza.php Lines 65-73 *\u002F\nif(isset($_POST['vars']['type']) && in_array( $_POST['vars']['type'], array('admin-delete-order', 'admin-change-status', 'admin-view-order', 'admin-order-history') ) ){\n\t$wppizza_ajax_nonce = '' . WPPIZZA_PREFIX . '_ajax_nonce';\n\tif (! isset( $_POST['vars']['nonce'] ) || !wp_verify_nonce(  $_POST['vars']['nonce'] , $wppizza_ajax_nonce ) ) {\n\t\theader('HTTP\u002F1.0 403 Forbidden [F]', true, 403);\n\t\tprint\"Forbidden [F]. Invalid Nonce.\";\n\t\texit; \u002F\u002Fjust for good measure\n\t}\n}\n\n---\n\n\u002F* classes\u002Fadmin\u002Fclass.wppizza.wp_admin.php Lines 42-50 *\u002F\n\t    \u002F******************\n\t    \tajax nonce in footer for all admin pages\n\t    \t\n\t    \tNote: also needed for non-wppizza admin pages for: \n\t    \t-\tdashboard widgets, \n\t    \t-\torder notifications on non-wppizza pages, \n\t    \t-\tdismissal of install notices\n\t    \tgetc\n\t \t******************\u002F \n\t\tadd_action('admin_footer', array($this, 'wppizza_ajax_nonce'));","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwppizza\u002F3.19.9\u002Fajax\u002Fadmin.ajax.wppizza.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwppizza\u002F3.20\u002Fajax\u002Fadmin.ajax.wppizza.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwppizza\u002F3.19.9\u002Fajax\u002Fadmin.ajax.wppizza.php\t2026-02-16 12:01:54.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwppizza\u002F3.20\u002Fajax\u002Fadmin.ajax.wppizza.php\t2026-04-14 17:39:04.000000000 +0000\n@@ -1,7 +1,7 @@\n \u003C?php\n-if(!defined('DOING_AJAX') || !DOING_AJAX){\n+if( !defined('DOING_AJAX') || !DOING_AJAX || !defined('ABSPATH') ){\n \theader('HTTP\u002F1.0 400 Bad Request', true, 400);\n-\tprint\"you cannot call this script directly\";\n+\tprint\"You cannot call this script directly.\";\n   exit; \u002F\u002Fjust for good measure\n }\n \u002F**testing variables ***********************\u002F\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwppizza\u002F3.19.9\u002Fajax\u002Fajax.wppizza.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwppizza\u002F3.20\u002Fajax\u002Fajax.wppizza.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwppizza\u002F3.19.9\u002Fajax\u002Fajax.wppizza.php\t2025-08-18 17:16:36.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwppizza\u002F3.20\u002Fajax\u002Fajax.wppizza.php\t2026-04-14 17:39:04.000000000 +0000\n@@ -2,9 +2,9 @@\n \u002F**************************************************\n \t[ajax only]\n **************************************************\u002F\n-if(!defined('DOING_AJAX') || !DOING_AJAX){\n+if( !defined('DOING_AJAX') || !DOING_AJAX || !defined('ABSPATH') ){\n \theader('HTTP\u002F1.0 400 Bad Request', true, 400);\n-\tprint\"you cannot call this script directly\";\n+\tprint\"You cannot call this script directly.\";\n   exit; \u002F\u002Fjust for good measure\n }\n \u002F**************************************************\n@@ -40,23 +40,69 @@\n \u002F**************************************************\n \t[add globals to use]\n **************************************************\u002F\n-global $wppizza_options, $blog_id;\n+global $wppizza_options, $blog_id, $current_user;\n \n \n-\u002F**************************************************\n+\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\n+*\n+*\n+*\n+*\tNonce\u002FAuth\u002FCredentials\u002FCaps checks \n+*\n+*\n+*\n+*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\**\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\**\\\u002F*\\\u002F*\\\u002F*\\\u002F*\\\u002F*\u002F\n+\n+\u002F*-------------------------------------------------\n \t[some frontend ajax calls should check the nonce too]\n \tto be expanded if needs be.....\n-**************************************************\u002F\n+-------------------------------------------------*\u002F\n if(isset($_POST['vars']['type']) && in_array( $_POST['vars']['type'], array('admin-delete-order', 'admin-change-status', 'admin-view-order', 'admin-order-history') ) ){\n \t$wppizza_ajax_nonce = '' . WPPIZZA_PREFIX . '_ajax_nonce';\n \tif (! isset( $_POST['vars']['nonce'] ) || !wp_verify_nonce(  $_POST['vars']['nonce'] , $wppizza_ajax_nonce ) ) {\n \t\theader('HTTP\u002F1.0 403 Forbidden [F]', true, 403);\n \t\tprint\"Forbidden [F]. Invalid Nonce.\";\n-\t\texit; \u002F\u002Fjust for good measure\n+\t\texit() ; \u002F\u002Fjust for good measure\n \t}\n }\n \n+\u002F*-------------------------------------------------\n+\tadditional auth\u002Fcapability checks \n+\tfor certain order (history) related ajax calls\n+-------------------------------------------------*\u002F\n+if(isset($_POST['vars']['type']) && in_array( $_POST['vars']['type'], array('admin-delete-order', 'admin-order-history', 'admin-view-order', 'admin-change-status') ) ){\n+\t\u002F\u002Flogged in user only with wppizza_cap_orderhistory privileges\n+\tif (!is_user_logged_in() || empty($current_user->allcaps['wppizza_cap_orderhistory'])){\t\t\n+\t\t$obj = array();\n+\t\t$obj['access_prohibited'] = __('Sorry, you are not allowed to access this page.', 'default' );\n+\t\tprint\"\".json_encode($obj).\"\";\n+\t\texit();\n+\t}\n+}\n+\u002F*-------------------------------------------------\n+\tDelete order needs additional credentials \n+-------------------------------------------------*\u002F\n+if( isset($_POST['vars']['type']) && $_POST['vars']['type']=='admin-delete-order' && !empty($_POST['vars']['uoKey']) ){\n+\n+\t\u002F* missing wppizza_cap_delete_order capabilities *\u002F\n+\tif(!current_user_can('wppizza_cap_delete_order')){\n+\t\t$obj['update_prohibited'] = __('Error: You need order delete permissions to perform this action.', 'wppizza-admin');\n+\t\tprint\"\".json_encode($obj).\"\";\n+\texit();\n+\t}\n+}\n+\n+\u002F*-------------------------------------------------\n+\tsaving\u002Fupdate disabled by constant\n+\tfor selected actions\n+-------------------------------------------------*\u002F\n+if(isset($_POST['vars']['type']) && in_array( $_POST['vars']['type'], array('admin-delete-order', 'admin-change-status') ) && !empty($_POST['vars']['uoKey']) ){\n+\tif(WPPIZZA_DEV_ADMIN_NO_SAVE){\n+\t\t$obj['update_prohibited'] = __('Update Prohibited', 'wppizza-admin');\n+\t\tprint\"\".json_encode($obj).\"\";\n+\texit();\n+\t}\n+}\n+","1. Log in to the WordPress site as a user with Subscriber privileges.\n2. Access the dashboard (e.g., `\u002Fwp-admin\u002Findex.php`) and locate the `wppizza_ajax_nonce` within the page source or localized JS variables (it is automatically added to the footer for all logged-in users).\n3. To extract sales information: Send a POST request to `\u002Fwp-admin\u002Fadmin-ajax.php` with the parameters `action=wppizza_admin_ajax`, `vars[nonce]=[EXTRACTED_NONCE]`, and `vars[field]=update-dashboard-widget`. The response will contain the HTML for the sales widget, including revenue and order totals.\n4. To extract specific order details: Send a POST request to `\u002Fwp-admin\u002Fadmin-ajax.php` with the parameters `action=wppizza_ajax`, `vars[nonce]=[EXTRACTED_NONCE]`, `vars[type]=admin-view-order`, and `vars[id]=[TARGET_ORDER_ID]`. The response will contain detailed PII and order content for the specified ID.","gemini-3-flash-preview","2026-05-04 18:08:34","2026-05-04 18:09:19",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","3.19.9","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwppizza\u002Ftags\u002F3.19.9","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwppizza.3.19.9.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwppizza\u002Ftags\u002F3.20","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwppizza.3.20.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwppizza\u002Ftags"]