[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fki-xF3Lp4AO5P7JswBxyM11nyac70uqpz7qCeGQmoQk":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-3718","managewp-worker-unauthenticated-stored-cross-site-scripting-via-mwp-key-name-header","ManageWP Worker \u003C= 4.9.31 - Unauthenticated Stored Cross-Site Scripting via 'MWP-Key-Name' Header","The ManageWP Worker plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'MWP-Key-Name' HTTP request header in all versions up to, and including, 4.9.31. This is due to insufficient input sanitization and output escaping of attacker-controlled header values. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever an administrator visits the plugin's connection management page with debug parameters.","worker",null,"\u003C=4.9.31","4.9.32","high",7.2,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-05-13 18:06:28","2026-05-14 06:44:13",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fdb6f08f9-4da3-450d-bf1e-5c9f0aab02a1?source=api-prod",1,[22,23,24,25,26,27,28,29],"init.php","readme.txt","src\u002FMWP\u002FAction\u002FDownloadFile.php","src\u002FMWP\u002FAction\u002FIncrementalBackup\u002FChecksumTables.php","src\u002FMWP\u002FEventListener\u002FMasterRequest\u002FAuthenticateServiceRequest.php","src\u002FMWP\u002FEventListener\u002FPublicRequest\u002FAddConnectionKeyInfo.php","src\u002FMWP\u002FEventListener\u002FPublicRequest\u002FBrandContactSupport.php","version","researched",false,3,"# Exploitation Research Plan: CVE-2026-3718 (ManageWP Worker Stored XSS)\n\n## 1. Vulnerability Summary\nThe ManageWP Worker plugin (versions \u003C= 4.9.31) is vulnerable to unauthenticated stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin logs failed communication attempts, including the attacker-controlled `MWP-Key-Name` HTTP header, into a WordPress option (`mwp_last_communication_error`) without sufficient sanitization. When an administrator views the plugin's connection management page with debug parameters enabled, this stored payload is echoed into the DOM, leading to script execution in the context of the administrator's session.\n\n## 2. Attack Vector Analysis\n*   **Target Endpoint:** Any URL handled by WordPress (e.g., `\u002Findex.php` or `\u002Fwp-admin\u002Fadmin-ajax.php`).\n*   **Vulnerable Header:** `MWP-Key-Name`\n*   **Required Header for Activation:** `MWP-Action` (to trigger the ManageWP request lifecycle).\n*   **Authentication Level:** Unauthenticated. No valid credentials or ManageWP secret keys are required to trigger the error logging.\n*   **Preconditions:** The ManageWP Worker plugin must be active.\n\n## 3. Code Flow\n1.  **Entry Point:** An unauthenticated request is sent containing the `MWP-Action` and `MWP-Key-Name` headers.\n2.  **Event Trigger:** The plugin identifies a ManageWP-style request (via `HTTP_MWP_ACTION`) and dispatches the `MWP_Event_Events::MASTER_REQUEST` event.\n3.  **Authentication Listener:** The listener `MWP_EventListener_MasterRequest_AuthenticateServiceRequest::onMasterRequest` (in `src\u002FMWP\u002FEventListener\u002FMasterRequest\u002FAuthenticateServiceRequest.php`) intercepts the request.\n4.  **Header Extraction:** At line 74, the code calls `$keyName = $request->getKeyName();`, which extracts the value of the `MWP-Key-Name` header.\n5.  **Logging (The Sink):** Since the request lacks a valid `serviceSignature`, the code reaches line 77 or 83.\n    *   **Line 77:** `if (empty($serviceSignature) || empty($keyName)) { $this->context->optionSet('mwp_last_communication_error', '... Key name: '.$keyName.', ...'); }`\n    *   **Line 83:** If a signature is provided but the key is not found: `$this->context->optionSet('mwp_last_communication_error', '... Searched for: '.$keyName);`\n6.  **Persistence:** The `optionSet` method (a wrapper for `update_option`) stores the unsanitized `$keyName` in the `mwp_last_communication_error` option.\n7.  **Execution:** When an admin visits `wp-admin\u002Fplugins.php?worker_connections=1&mwp_debug=1`, the plugin retrieves the `mwp_last_communication_error` option and echoes it directly into the page to assist in debugging connection issues.\n\n## 4. Nonce Acquisition Strategy\n**No nonce is required for the injection phase.** \nThe vulnerability occurs during the *authentication process* itself. The error logging mechanism is designed to capture details about failed requests (which inherently cannot have valid nonces or signatures). Therefore, an unauthenticated attacker can populate the `mwp_last_communication_error` option by simply sending a malformed request.\n\n## 5. Exploitation Strategy\n### Step 1: Inject the XSS Payload\nSend a request to the WordPress root to trigger the error log entry.\n\n*   **Tool:** `http_request`\n*   **Method:** POST (or GET)\n*   **URL:** `\u002F`\n*   **Headers:**\n    *   `MWP-Action: version`\n    *   `MWP-Key-Name: \u003Cimg src=x onerror=\"alert('CVE-2026-3718')\">`\n*   **Expected Response:** The request will likely return a `200 OK` or a ManageWP-specific error, but the payload will be stored in the database.\n\n### Step 2: Trigger the Payload (Admin Interaction)\nAn administrator must navigate to the worker connection management page with debug mode active.\n\n*   **Target URL:** `\u002Fwp-admin\u002Fplugins.php?worker_connections=1&mwp_debug=1`\n*   **Requirement:** Must be logged in as a user with `activate_plugins` or `manage_options` capabilities.\n\n## 6. Test Data Setup\n1.  Install and activate **ManageWP Worker 4.9.31**.\n2.  No specific ManageWP connection is required; the vulnerability triggers on the \"failed to connect\" path.\n3.  Ensure the `mwp_last_communication_error` option is empty or does not exist initially (optional, but good for clean testing).\n\n## 7. Expected Results\n*   The `update_option` call will save the string containing the HTML payload to the database.\n*   Upon visiting the connection management page with `mwp_debug=1`, the page source will contain the raw HTML: `... Searched for: \u003Cimg src=x onerror=\"...\"> ...`.\n*   The browser will execute the JavaScript in the `onerror` attribute.\n\n## 8. Verification Steps\nAfter sending the injection request via `http_request`, verify the state of the database using WP-CLI:\n\n```bash\n# Check if the option contains the payload\nwp option get mwp_last_communication_error\n```\n\nThe output should look like:\n`Unexpected: service signature or key name are empty. Key name: \u003Cimg src=x onerror=\"alert('CVE-2026-3718')\">, ...`\n\n## 9. Alternative Approaches\nIf `mwp_debug=1` does not immediately show the error, try the following debug parameters:\n*   `debug=1`\n*   `mwp_verbose=1`\n*   `mwp_error_view=1`\n\nAdditionally, check if the error is displayed as a WordPress Admin Notice (`admin_notices` hook) on the main plugins page after a failed ManageWP communication attempt. Some versions of the worker plugin display the `mwp_last_communication_error` as a persistent notice until cleared.","The ManageWP Worker plugin for WordPress is vulnerable to unauthenticated stored Cross-Site Scripting (XSS) via the 'MWP-Key-Name' HTTP header. Malicious header values are logged into the 'mwp_last_communication_error' option without sanitization and subsequently executed in the browser of an administrator viewing the connection management page.","\u002F\u002F src\u002FMWP\u002FEventListener\u002FMasterRequest\u002FAuthenticateServiceRequest.php:71\n        $keyName = $request->getKeyName();\n\n        if (empty($serviceSignature) || empty($keyName)) {\n            $this->context->optionSet('mwp_last_communication_error', 'Unexpected: service signature or key name are empty. Key name: '.$keyName.', Signature: '.$serviceSignature.', Algorithm: '.($algorithm ? $algorithm : 'SHA1'));\n            return;\n        }\n\n---\n\n\u002F\u002F src\u002FMWP\u002FEventListener\u002FPublicRequest\u002FAddConnectionKeyInfo.php:415\n                \u003Cp>\n                    \u003C?php echo 'Last communication error: '.$this->context->optionGet('mwp_last_communication_error', '') ?>\n                \u003C\u002Fp>","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.31\u002Finit.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.32\u002Finit.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.31\u002Finit.php\t2026-03-11 16:11:40.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.32\u002Finit.php\t2026-03-18 13:39:12.000000000 +0000\n@@ -3,7 +3,7 @@\n Plugin Name: ManageWP - Worker\n Plugin URI: https:\u002F\u002Fmanagewp.com\n Description: We help you efficiently manage all your WordPress websites. \u003Cstrong>Updates, backups, 1-click login, migrations, security\u003C\u002Fstrong> and more, on one dashboard. This service comes in two versions: standalone \u003Ca href=\"https:\u002F\u002Fmanagewp.com\">ManageWP\u003C\u002Fa> service that focuses on website management, and \u003Ca href=\"https:\u002F\u002Fgodaddy.com\u002Fpro\">GoDaddy Pro\u003C\u002Fa> that includes additional tools for hosting, client management, lead generation, and more.\n-Version: 4.9.31\n+Version: 4.9.32\n Author: GoDaddy\n Author URI: https:\u002F\u002Fgodaddy.com\n License: GPL2\n@@ -575,8 +575,8 @@\n         \u002F\u002F reason (eg. the site can't ping itself). Handle that case early.\n         register_activation_hook(__FILE__, 'mwp_activation_hook');\n \n-        $GLOBALS['MMB_WORKER_VERSION']  = '4.9.31';\n-        $GLOBALS['MMB_WORKER_REVISION'] = '2026-03-10 00:00:00';\n+        $GLOBALS['MMB_WORKER_VERSION']  = '4.9.32';\n+        $GLOBALS['MMB_WORKER_REVISION'] = '2026-03-18 00:00:00';\n \n         \u002F\u002F Ensure PHP version compatibility.\n         if (version_compare(PHP_VERSION, '5.2', '\u003C')) {\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.31\u002Fsrc\u002FMWP\u002FEventListener\u002FMasterRequest\u002FAuthenticateServiceRequest.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.32\u002Fsrc\u002FMWP\u002FEventListener\u002FMasterRequest\u002FAuthenticateServiceRequest.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.31\u002Fsrc\u002FMWP\u002FEventListener\u002FMasterRequest\u002FAuthenticateServiceRequest.php\t2025-04-25 08:44:02.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.32\u002Fsrc\u002FMWP\u002FEventListener\u002FMasterRequest\u002FAuthenticateServiceRequest.php\t2026-03-18 13:39:12.000000000 +0000\n@@ -46,6 +46,8 @@\n         }\n \n         $algorithm = $request->getSignatureAlgorithm();\n+        \u002F\u002F Sanitize algorithm to prevent XSS when displayed in debug output\n+        $sanitizedAlgorithm = sanitize_text_field($algorithm);\n \n         if ($algorithm == 'SHA256') {\n             $serviceSignature = $request->getServiceSignatureV2();\n@@ -56,9 +58,11 @@\n         }\n \n         $keyName = $request->getKeyName();\n+        \u002F\u002F Sanitize key name to prevent XSS when displayed in debug output\n+        $sanitizedKeyName = sanitize_text_field($keyName);\n \n         if (empty($serviceSignature) || empty($keyName)) {\n-            $this->context->optionSet('mwp_last_communication_error', 'Unexpected: service signature or key name are empty. Key name: '.$keyName.', Signature: '.$serviceSignature.', Algorithm: '.($algorithm ? $algorithm : 'SHA1'));\n+            $this->context->optionSet('mwp_last_communication_error', 'Unexpected: service signature or key name are empty. Key name: '.$sanitizedKeyName.', Signature: '.$serviceSignature.', Algorithm: '.($sanitizedAlgorithm ? $sanitizedAlgorithm : 'SHA1'));\n             return;\n         }\n \n@@ -67,7 +71,7 @@\n         if (empty($publicKey)) {\n             \u002F\u002F for now do not throw an exception, just do not authenticate the request\n             \u002F\u002F later we should start throwing an exception here when this becomes the main communication method\n-            $this->context->optionSet('mwp_last_communication_error', 'Could not find the appropriate communication key. Searched for: '.$keyName);\n+            $this->context->optionSet('mwp_last_communication_error', 'Could not find the appropriate communication key. Searched for: '.$sanitizedKeyName);\n             return;\n         }\n \n@@ -75,7 +79,7 @@\n         $messageToCheck   = '';\n \n         if (empty($communicationKey)) {\n-            $this->context->optionSet('mwp_last_communication_error', 'Unexpected: communication key is empty. Key name: '.$keyName);\n+            $this->context->optionSet('mwp_last_communication_error', 'Unexpected: communication key is empty. Key name: '.$sanitizedKeyName);\n             return;\n         }\n \n@@ -88,7 +92,7 @@\n         if (empty($messageToCheck)) {\n             \u002F\u002F for now do not throw an exception, just do not authenticate the request\n             \u002F\u002F later we should start throwing an exception here when this becomes the main communication method\n-            $this->context->optionSet('mwp_last_communication_error', 'Unexpected: message to check is empty. Host: '.$request->server['HTTP_HOST']);\n+            $this->context->optionSet('mwp_last_communication_error', 'Unexpected: message to check is empty. Host: '.sanitize_text_field($request->server['HTTP_HOST']));\n             return;\n         }\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.31\u002Fsrc\u002FMWP\u002FEventListener\u002FPublicRequest\u002FAddConnectionKeyInfo.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.32\u002Fsrc\u002FMWP\u002FEventListener\u002FPublicRequest\u002FAddConnectionKeyInfo.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.31\u002Fsrc\u002FMWP\u002FEventListener\u002FPublicRequest\u002FAddConnectionKeyInfo.php\t2021-07-27 17:48:34.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fworker\u002F4.9.32\u002Fsrc\u002FMWP\u002FEventListener\u002FPublicRequest\u002FAddConnectionKeyInfo.php\t2026-03-18 13:39:12.000000000 +0000\n@@ -410,21 +410,28 @@\n                     if ($refreshedKeys['success'] === true) {\n                         echo 'Keys successfully refreshed!';\n                     } else {\n-                        echo 'Keys were not successfully refreshed. Error: '.$refreshedKeys['message'];\n+                        echo 'Keys were not successfully refreshed. Error: '.esc_html($refreshedKeys['message']);\n                     } ?>\n                 \u003C\u002Fp>\n                 \u003Cp>\n-                    \u003C?php echo 'Last communication error: '.$this->context->optionGet('mwp_last_communication_error', '') ?>\n+                    \u003C?php echo 'Last communication error: '.esc_html($this->context->optionGet('mwp_last_communication_error', '')) ?>\n                 \u003C\u002Fp>","An unauthenticated attacker sends a crafted HTTP request to any WordPress endpoint. The request must include the 'MWP-Action' header (e.g., set to 'version') to trigger the plugin's ManageWP event dispatcher and the 'MWP-Key-Name' header containing an XSS payload (e.g., \u003Cimg src=x onerror=alert(1)>). Because the request is unauthenticated, it reaches an error-handling block that stores the unsanitized 'MWP-Key-Name' value into the 'mwp_last_communication_error' option via WordPress's update_option. The payload is triggered when a logged-in administrator visits the worker connection management page (wp-admin\u002Fplugins.php?worker_connections=1) with debug parameters (mwp_debug=1) enabled.","gemini-3-flash-preview","2026-05-14 17:05:47","2026-05-14 17:06: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","4.9.31","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fworker\u002Ftags\u002F4.9.31","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fworker.4.9.31.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fworker\u002Ftags\u002F4.9.32","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fworker.4.9.32.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fworker\u002Ftags"]