[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fyK49QjDJphQlNEkc3KCkOEZqnRj6QyRhll9HokOWvXk":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-48873","montonio-for-woocommerce-missing-authorization","Montonio for WooCommerce \u003C= 10.1.2 - Missing Authorization","The Montonio for WooCommerce plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 10.1.2. This makes it possible for unauthenticated attackers to perform an unauthorized action.","montonio-for-woocommerce",null,"\u003C=10.1.2","10.1.3","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Missing Authorization","2026-06-02 00:00:00","2026-06-08 14:36:31",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Ff9fe8ff7-32c7-4c40-8e70-48cd7a9910ec?source=api-prod",7,[22,23,24,25,26,27,28,29],"includes\u002Fadmin\u002Fclass-montonio-connection.php","languages\u002Fmontonio-for-woocommerce-et.po","languages\u002Fmontonio-for-woocommerce-lt_LT.po","languages\u002Fmontonio-for-woocommerce-lv.po","languages\u002Fmontonio-for-woocommerce-lv_LV.po","languages\u002Fmontonio-for-woocommerce-pl_PL.po","languages\u002Fmontonio-for-woocommerce-ru_RU.po","languages\u002Fmontonio-for-woocommerce.pot","researched",false,3,"This research plan focuses on **CVE-2026-48873**, a missing authorization vulnerability in the **Montonio for WooCommerce** plugin. The vulnerability allows an unauthenticated attacker to trigger the account connection callback, potentially overwriting the store's API credentials.\n\n### 1. Vulnerability Summary\nThe vulnerability exists in the `Montonio_Connection::handle_callback` method, which is registered as a handler for the `woocommerce_api_montonio_connection` hook. Unlike `handle_disconnect`, which correctly implements a capability check (`current_user_can( 'manage_woocommerce' )`), the callback handler lacks any authorization or authentication checks. This endpoint is designed to receive an authorization code from Montonio's Partner System and exchange it for API keys, which are then saved to the site's database.\n\n### 2. Attack Vector Analysis\n*   **Endpoint:** `\u002F?wc-api=montonio_connection` (The WooCommerce API endpoint for this action).\n*   **Authentication:** None (Unauthenticated).\n*   **Vulnerable Action:** Overwriting the `woocommerce_wc_montonio_api_settings` option with arbitrary API keys (linked to an attacker's Montonio account).\n*   **Preconditions:** The plugin must be active. The attacker needs to generate a valid `code` by initiating a connection flow through the official Montonio Partner System for their own account.\n\n### 3. Code Flow\n1.  **Registration:** In `includes\u002Fadmin\u002Fclass-montonio-connection.php`, the `init()` method registers the hook:\n    `add_action( 'woocommerce_api_montonio_connection', array( __CLASS__, 'handle_callback' ) );`\n2.  **Entry Point:** An unauthenticated HTTP request hits `https:\u002F\u002Fexample.com\u002F?wc-api=montonio_connection`.\n3.  **Callback Processing:** WooCommerce's API dispatcher calls `Montonio_Connection::handle_callback`.\n4.  **Exchange (Inferred):** The function (located after line 192 in the full source) likely retrieves a `code` parameter from `$_GET` or `$_POST`.\n5.  **Telemetry Request:** It sends this code to `self::TELEMETRY_API_URL` (`https:\u002F\u002Fplugin-telemetry.montonio.com\u002Fapi`) to retrieve API keys.\n6.  **Sink:** Upon receiving the keys, it calls `update_option( self::SETTINGS_OPTION, $new_settings )`, where `self::SETTINGS_OPTION` is `'woocommerce_wc_montonio_api_settings'`.\n\n### 4. Nonce Acquisition Strategy\nThis specific vulnerability **does not require a WordPress nonce**.\n*   The `woocommerce_api_` hooks are designed for external service callbacks (webhooks\u002FOAuth-style redirects). Since the external service (Montonio) cannot know a WordPress user's session-specific nonce, these endpoints are intentionally public.\n*   The security of such endpoints usually relies on a `state` parameter or checking a cryptographic signature, which appears to be missing or bypassed here.\n\n### 5. Exploitation Strategy\nTo verify the missing authorization, we will simulate the callback that occurs after a merchant authorizes the plugin.\n\n1.  **Identify Parameters (Inferred):** Based on the plugin's purpose, the callback expects a `code` parameter.\n2.  **Initiate Request:** Send a GET or POST request to the WooCommerce API endpoint.\n3.  **Target URL:** `http:\u002F\u002Flocalhost:8888\u002F?wc-api=montonio_connection`\n4.  **Payload:**\n    *   `code`: A placeholder string (e.g., `poc_test_code_123`).\n5.  **Execution via `http_request`:**\n    ```javascript\n    \u002F\u002F Example request to trigger the vulnerable code path\n    await http_request({\n        url: 'http:\u002F\u002Flocalhost:8888\u002F?wc-api=montonio_connection&code=poc_test_code_123',\n        method: 'GET'\n    });\n    ```\n\n*Note: Since the actual key exchange requires a valid code from Montonio's live servers, a successful PoC in an isolated environment will manifest as an \"activation_failed\" or \"invalid_request\" error message in the logs or a redirect with a specific status, proving the function was reached and processed without a 403 Forbidden error.*\n\n### 6. Test Data Setup\n1.  Install and activate **WooCommerce**.\n2.  Install and activate **Montonio for WooCommerce** version 10.1.2.\n3.  No specific configuration is required, as the endpoint is exposed globally upon plugin activation.\n\n### 7. Expected Results\n*   **Response:** The server should return a `302 Redirect` back to the settings page with a query parameter like `montonio_connect=activation_failed` (or similar, depending on the response from the unreachable telemetry API).\n*   **Authorization Bypass:** The request should **not** return a `403 Forbidden` or `wp_die` message, despite the attacker being unauthenticated.\n\n### 8. Verification Steps\nAfter sending the request, use WP-CLI to check if the plugin attempted to process the request:\n1.  **Check for Notice State:**\n    `wp option get woocommerce_wc_montonio_api_settings`\n2.  **Verify Access:** If the function was reached, the plugin may have logged an error or updated a \"connection status\" flag in the options.\n3.  **Log Inspection:** Check the WordPress debug log (`wp-content\u002Fdebug.log`) for any `WP_Error` objects or failed `wp_remote_post` calls to `plugin-telemetry.montonio.com`, which confirms the logic was executed.\n\n### 9. Alternative Approaches\nIf a simple GET request is ignored, the callback might strictly require a POST request (as mentioned in the docblock: \"Receives the auto-submitted form POST\"):\n```javascript\nawait http_request({\n    url: 'http:\u002F\u002Flocalhost:8888\u002F?wc-api=montonio_connection',\n    method: 'POST',\n    headers: { 'Content-Type': 'application\u002Fx-www-form-urlencoded' },\n    body: 'code=poc_test_code_123'\n});\n```\nAdditionally, check if the `handle_callback` method requires a `state` parameter to match an existing session; if so, the vulnerability might only be exploitable via CSRF or if the `state` check is also missing.","The Montonio for WooCommerce plugin fails to perform authorization or state validation on its account connection callback endpoint. Unauthenticated attackers can exploit this by sending a crafted request to the `woocommerce_api_montonio_connection` hook, which overwrites the store's API credentials with keys linked to the attacker's own Montonio account.","\u002F\u002F includes\u002Fadmin\u002Fclass-montonio-connection.php (lines 192-205 in v10.1.2)\n\n    public static function handle_callback() {\n        if ( ! isset( $_POST['connectionUuid'], $_POST['oneTimeCode'] ) && ! isset( $_POST['cancelled'] ) ) {\n            return;\n        }\n\n        $cancelled       = sanitize_text_field( wp_unslash( $_POST['cancelled'] ?? '' ) );\n        $connection_uuid = sanitize_text_field( wp_unslash( $_POST['connectionUuid'] ?? '' ) );\n        $one_time_code   = sanitize_text_field( wp_unslash( $_POST['oneTimeCode'] ?? '' ) );\n\n        if ( 'true' === $cancelled ) {\n            self::redirect_to_settings( 'cancelled' );\n            return;\n        }","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmontonio-for-woocommerce\u002F10.1.2\u002Fincludes\u002Fadmin\u002Fclass-montonio-connection.php\t2026-05-15 06:27:40.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmontonio-for-woocommerce\u002F10.1.3\u002Fincludes\u002Fadmin\u002Fclass-montonio-connection.php\t2026-05-22 07:25:20.000000000 +0000\n@@ -125,11 +125,19 @@\n             trailingslashit( get_home_url() )\n         );\n \n+        $state = bin2hex( random_bytes( 16 ) );\n+        update_option(\n+            self::state_option_key(),\n+            $state,\n+            false\n+        );\n+\n         $query = http_build_query( array(\n             'action'      => 'connect-plugin',\n             'platform'    => 'woocommerce',\n             'storeUrl'    => wp_parse_url( get_home_url(), PHP_URL_HOST ),\n-            'callbackUrl' => $redirect_uri\n+            'callbackUrl' => $redirect_uri,\n+            'state'       => $state,\n         ) );\n \n         return 'https:\u002F\u002Fpartner.montonio.com\u002F?' . $query;\n@@ -200,9 +208,31 @@\n             return;\n         }\n \n+        if ( ! current_user_can( 'manage_woocommerce' ) ) {\n+            WC_Montonio_Logger::log( 'Connection callback rejected: insufficient capability.' );\n+            wp_die(\n+                esc_html__( 'Sorry, you are not allowed to access this page.' ),\n+                '',\n+                array( 'response' => 403 )\n+            );\n+        }\n+\n         $cancelled       = sanitize_text_field( wp_unslash( $_POST['cancelled'] ?? '' ) );\n         $connection_uuid = sanitize_text_field( wp_unslash( $_POST['connectionUuid'] ?? '' ) );\n         $one_time_code   = sanitize_text_field( wp_unslash( $_POST['oneTimeCode'] ?? '' ) );\n+        $state           = sanitize_text_field( wp_unslash( $_POST['state'] ?? '' ) );\n+\n+        $stored_state = get_option( self::state_option_key() );\n+        if ( false === $stored_state || ! is_string( $stored_state ) || '' === $state || ! hash_equals( $stored_state, $state ) ) {\n+            WC_Montonio_Logger::log( 'Connection callback rejected: state mismatch or missing stored state.' );\n+            \u002F\u002F Clean up whatever may be there so a fresh flow starts cleanly.\n+            delete_option( self::state_option_key() );\n+            self::redirect_to_settings( 'invalid_request' );\n+            return;\n+        }\n+\n+        \u002F\u002F State validated; consume it.\n+        delete_option( self::state_option_key() );\n \n         if ( 'true' === $cancelled ) {\n             self::redirect_to_settings( 'cancelled' );\n@@ -223,7 +253,7 @@\n             return;\n         }\n \n-        $activation = self::activate_connection( $connection_uuid, $one_time_code );\n+        $activation = self::activate_connection( $connection_uuid, $one_time_code, $state );\n \n         if ( is_wp_error( $activation ) ) {\n             WC_Montonio_Logger::log( 'Connection activation failed: ' . $activation->get_error_message() );\n@@ -244,14 +274,27 @@\n     }\n \n     \u002F**\n+     * Build the per-user wp_options key that stores the OAuth state value\n+     * for an in-flight connect flow. Single source of truth for the four\n+     * call sites that read \u002F write \u002F consume it.\n+     *\n+     * @since 10.1.3\n+     * @return string\n+     *\u002F\n+    private static function state_option_key() {\n+        return 'montonio_connection_state_' . get_current_user_id();\n+    }\n+\n+    \u002F**\n      * Exchange the one-time code with PTS for API keys.\n      *\n      * @since 10.1.0\n      * @param string $connection_uuid\n      * @param string $one_time_code\n+     * @param string $state OAuth state value validated against wp_options, forwarded to PTS.\n      * @return array|WP_Error Decoded PTS response on success.\n      *\u002F\n-    private static function activate_connection( $connection_uuid, $one_time_code ) {\n+    private static function activate_connection( $connection_uuid, $one_time_code, $state ) {\n         $response = wp_remote_post(\n             trailingslashit( self::TELEMETRY_API_URL ) . 'connections\u002Factivate',\n             array(\n@@ -263,7 +306,8 @@\n                 'body'    => wp_json_encode(\n                     array(\n                         'connectionUuid' => $connection_uuid,\n-                        'oneTimeCode'    => $one_time_code\n+                        'oneTimeCode'    => $one_time_code,\n+                        'state'          => $state,\n                     )\n                 )\n             )","The exploit targets the public WooCommerce API endpoint registered by the plugin. An attacker first obtains a valid `connectionUuid` and `oneTimeCode` by initiating a connection flow through the official Montonio Partner System for their own merchant account. They then send an unauthenticated POST request to `\u002F?wc-api=montonio_connection` on the target site, providing their own credentials in the POST body. Because the plugin lacks capability checks and does not verify an OAuth-style 'state' parameter, it accepts the data, exchanges the code with Montonio's telemetry service, and updates the site's database settings (`woocommerce_wc_montonio_api_settings`) with the attacker's API keys.","gemini-3-flash-preview","2026-06-26 06:02:10","2026-06-26 06:03:37",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","10.1.2","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fmontonio-for-woocommerce\u002Ftags\u002F10.1.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fmontonio-for-woocommerce.10.1.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fmontonio-for-woocommerce\u002Ftags\u002F10.1.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fmontonio-for-woocommerce.10.1.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fmontonio-for-woocommerce\u002Ftags"]