[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f8wY6s8VD3U-HpzO9ZNTAjN0RbfyfKe44Y7VOnO8ZUrk":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-4268","wp-go-maps-formerly-wp-google-maps-missing-authorization-to-authenticated-subscriber-stored-cross-site-scripting-via-adm","WP Go Maps (formerly WP Google Maps) \u003C= 10.0.05 - Missing Authorization to Authenticated (Subscriber+) Stored Cross-Site Scripting via admin_post_wpgmza_save_settings","The WP Go Maps (formerly WP Google Maps) plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the ‘wpgmza_custom_js’ parameter in all versions up to, and including, 10.0.05 due to insufficient input sanitization and output escaping and missing capability check in the 'admin_post_wpgmza_save_settings' hook anonymous function. This makes it possible for authenticated attackers, with Subscriber-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.","wp-google-maps",null,"\u003C=10.0.05","10.0.06","medium",6.4,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-03-17 13:24:29","2026-03-18 01:24:48",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fd5599101-a7bd-4aa7-91da-f11694bdc000?source=api-prod",1,[22,23,24,25,26,27,28,29],"includes\u002Fclass.admin-ui.php","includes\u002Fclass.settings-page.php","js\u002Fv8\u002Fgoogle-maps\u002Fgoogle-marker.js","js\u002Fv8\u002Fleaflet\u002Fleaflet-marker.js","js\u002Fv8\u002Fwp-google-maps.combined.js","js\u002Fv8\u002Fwp-google-maps.min.js","js\u002Fv8\u002Fwp-google-maps.min.js.map","readme.txt","researched",false,3,"# Exploitation Research Plan: CVE-2026-4268 (WP Go Maps Stored XSS)\n\n## 1. Vulnerability Summary\nThe **WP Go Maps (formerly WP Google Maps)** plugin (up to 10.0.05) contains a stored cross-site scripting (XSS) vulnerability due to missing authorization and insufficient sanitization in the `admin_post_wpgmza_save_settings` action handler. The plugin registers a global admin post hook that allows any authenticated user (Subscriber level and above) to update plugin settings, including the `wpgmza_custom_js` field, which is explicitly exempted from sanitization. This allows an attacker to inject arbitrary JavaScript that executes whenever a map is rendered on the site or when an administrator visits the plugin settings page.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `wp-admin\u002Fadmin-post.php`\n- **Action:** `wpgmza_save_settings` (triggered via the `admin_post_wpgmza_save_settings` hook)\n- **Vulnerable Parameter:** `wpgmza_custom_js`\n- **Required Authentication:** Subscriber level (any logged-in user).\n- **Required Nonce:** A nonce for the action is required, but it is exposed to logged-in users via the WordPress Heartbeat API or potentially the settings page itself if access levels are misconfigured.\n\n## 3. Code Flow\n1. **Entry Point:** The plugin registers the action in `includes\u002Fclass.settings-page.php`:\n   ```php\n   add_action('admin_post_wpgmza_save_settings', function() {\n       $settingsPage = SettingsPage::createInstance();\n   });\n   ```\n2. **Initialization:** `SettingsPage::createInstance()` instantiates the `SettingsPage` class.\n3. **Execution Sink:** The `SettingsPage::__construct()` method (in `includes\u002Fclass.settings-page.php`) handles the request:\n   - It checks `if(empty($_POST))`. Since we are sending a POST request, it enters the `else` block.\n   - It performs a nonce check: `if(!$this->isNonceValid($this->form, $_POST['nonce']))`.\n   - It iterates through `$_POST` data and performs sanitization **except** for `wpgmza_custom_js`:\n     ```php\n     if($key === \"wpgmza_custom_js\"){\n         \u002F\u002F Skip custom javascript, they should be used with user caution, we can't fully clean these\n         continue;\n     }\n     ```\n   - It saves the data into the global `$wpgmza->settings` object:\n     ```php\n     foreach($data as $key => $value){\n         $wpgmza->settings->{$key} = $value;\n     }\n     ```\n   - **Crucially**, there is no call to `current_user_can()` or any other capability check before these settings are persisted to the database.\n\n## 4. Nonce Acquisition Strategy\nThe plugin uses a nonce to protect the settings form. Based on `includes\u002Fclass.admin-ui.php`, the plugin registers a filter to refresh nonces via the Heartbeat API.\n\n### Heartbeat API Leak\nThe function `onAdminRefreshNonces` in `includes\u002Fclass.admin-ui.php` reveals the nonce action:\n```php\npublic function onAdminRefreshNonces($nonces){\n    if(!empty($_POST) && !empty($_POST['screen_id'])){\n        if(strpos($_POST['screen_id'], 'wp-google-maps') !== FALSE){\n            \u002F* Looking at a WP Go Maps Related page *\u002F\n            $action = admin_url('admin-post.php');\n            $nonces['wpgmza_nonce'] = wp_create_nonce(\"wpgmza_$action\");\n        }\n    }\n    return $nonces;\n}\n```\n**Strategy:**\n1. Login as a Subscriber user.\n2. Navigate to `wp-admin\u002Findex.php`.\n3. Use `http_request` to send a POST request to `wp-admin\u002Fadmin-ajax.php` with:\n   - `action=heartbeat`\n   - `screen_id=wp-google-maps-settings` (to trigger the `strpos` check)\n   - `_nonce` (the standard WordPress heartbeat nonce, visible in the `wp-admin` source)\n4. Extract `wpgmza_nonce` from the JSON response.\n\n*Alternatively:* Check if the Subscriber user can access `wp-admin\u002Fadmin.php?page=wp-google-maps-menu-settings` directly. If the plugin's default access level is low, the nonce will be directly in the HTML.\n\n## 5. Exploitation Strategy\n1. **Login:** Authenticate as a Subscriber.\n2. **Obtain Nonce:**\n   - Navigate to `\u002Fwp-admin\u002Findex.php`.\n   - Execute JS via `browser_eval` to fetch the heartbeat nonce: `wp.heartbeat.post('wpgmza', {screen_id: 'wp-google-maps'}, (response) => { console.log(response.wpgmza_nonce); })`.\n   - Or manually fetch `admin-ajax.php` using `http_request`.\n3. **Inject Payload:**\n   - Send a POST request to `\u002Fwp-admin\u002Fadmin-post.php` using `http_request`.\n   - **Body:**\n     - `action=wpgmza_save_settings`\n     - `nonce=[EXTRACTED_NONCE]`\n     - `wpgmza_custom_js=alert(\"XSS_EXPLOITED_\" + document.domain);`\n   - **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n4. **Trigger XSS:**\n   - Navigate to any page containing a map shortcode `[wp-google-maps]` or the plugin's settings page as an administrator.\n\n## 6. Test Data Setup\n1. **Users:** Create a Subscriber user (e.g., `attacker`\u002F`attacker`).\n2. **Map Content:** Ensure at least one map exists.\n   - `wp google-map create --map_title=\"Test Map\"`\n3. **Trigger Page:** Create a public page displaying the map.\n   - `wp post create --post_type=page --post_title=\"Map Page\" --post_status=publish --post_content='[wp-google-maps id=\"1\"]'`\n\n## 7. Expected Results\n- The POST request to `admin-post.php` should return a `302 Redirect` back to the referrer (indicating success) or a `200 OK` without error.\n- The `wpgmza_settings` option in the database will now contain the malicious JavaScript.\n- When any user visits the \"Map Page\", an alert box with `XSS_EXPLOITED_[domain]` will appear.\n\n## 8. Verification Steps\n1. **Database Check:**\n   `wp option get wpgmza_settings --format=json`\n   Check if the `wpgmza_custom_js` key contains the payload.\n2. **Frontend Verification:**\n   Navigate to the \"Map Page\" using `browser_navigate` and check for the presence of the script or the alert.\n\n## 9. Alternative Approaches\n- **Access Level Manipulation:** In some configurations, the plugin allows administrators to lower the \"Access Level\" for map management. If this is set to `read`, the Subscriber can access the settings UI directly.\n- **CSRF:** Since the authorization check is missing, if the nonce can be bypassed or predicted, this becomes a CSRF vector against an Administrator to change plugin settings.\n- **Heartbeat Spoofing:** If `wp_refresh_nonces` is active, any `admin-ajax.php` request that triggers the heartbeat action with the correct `screen_id` will leak the nonce required for the settings update.","The WP Go Maps plugin for WordPress is vulnerable to Stored Cross-Site Scripting because it fails to perform an authorization check in the 'admin_post_wpgmza_save_settings' handler and explicitly exempts the 'wpgmza_custom_js' parameter from sanitization. Authenticated attackers with Subscriber-level access or higher can update plugin settings to inject malicious JavaScript that executes whenever a map is rendered or an administrator visits the settings page.","\u002F\u002F includes\u002Fclass.settings-page.php (around line 144)\nadd_action('admin_post_wpgmza_save_settings', function() {\n\t$settingsPage = SettingsPage::createInstance();\n});\n\n---\n\n\u002F\u002F includes\u002Fclass.settings-page.php (around line 84 in constructor)\nif(empty($_POST)) {\n    $this->document->populate($wpgmza->settings);\n    $this->addFormNonces();\n    $wpgmza->scriptLoader->enqueueCodeMirror();\n} else {\n    if(!$this->isNonceValid($this->form, $_POST['nonce']))\n        throw new \\Exception(\"Invalid nonce\");\n    \n    $oldPullMethod\t= $wpgmza->settings->wpgmza_settings_marker_pull;\n    $data\t\t\t= array_map('stripslashes', $_POST);\n\n    foreach($data as $key => $value){\n        if(is_string($value)){\n            if($key === \"wpgmza_custom_js\"){\n                \u002F\u002F Skip custom javascript, they should be used with user caution, we can't fully clean these\n                continue;\n            } else if($key === \"wpgmza_custom_css\"){\n                $data[$key] = wp_strip_all_tags($value);\n                continue;\n            }\n            $data[$key] = wp_kses_post($value);\n        }\n    }\n    \n    $this->document->populate($data);\n    $data = $this->form->serializeFormData();\n    \n    foreach($data as $key => $value){\n        $wpgmza->settings->{$key} = $value;\n    }\n    \u002F\u002F ... (persists to database)\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-google-maps\u002F10.0.05\u002Fincludes\u002Fclass.admin-ui.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-google-maps\u002F10.0.06\u002Fincludes\u002Fclass.admin-ui.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-google-maps\u002F10.0.05\u002Fincludes\u002Fclass.admin-ui.php\t2026-03-17 12:24:24.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-google-maps\u002F10.0.06\u002Fincludes\u002Fclass.admin-ui.php\t2026-04-15 08:36:52.000000000 +0000\n@@ -26,11 +26,14 @@\n \t}\n \n \tpublic function onAdminRefreshNonces($nonces){\n+\t\tglobal $wpgmza;\n \t\tif(!empty($_POST) && !empty($_POST['screen_id'])){\n \t\t\tif(strpos($_POST['screen_id'], 'wp-google-maps') !== FALSE){\n-\t\t\t\t\u002F* Looking at a WP Go Maps Related page *\u002F\n-\t\t\t\t$action = admin_url('admin-post.php');\n-\t\t\t\t$nonces['wpgmza_nonce'] = wp_create_nonce(\"wpgmza_$action\");\n+\t\t\t\tif(!empty($wpgmza) && $wpgmza->isUserAllowedToEdit()){\n+\t\t\t\t\t\u002F* Looking at a WP Go Maps Related page *\u002F\n+\t\t\t\t\t$action = admin_url('admin-post.php');\n+\t\t\t\t\t$nonces['wpgmza_nonce'] = wp_create_nonce(\"wpgmza_$action\");\n+\t\t\t\t}\n \t\t\t}\n \t\t}\n \t\treturn $nonces;\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-google-maps\u002F10.0.05\u002Fincludes\u002Fclass.settings-page.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-google-maps\u002F10.0.06\u002Fincludes\u002Fclass.settings-page.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-google-maps\u002F10.0.05\u002Fincludes\u002Fclass.settings-page.php\t2026-03-17 12:24:24.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-google-maps\u002F10.0.06\u002Fincludes\u002Fclass.settings-page.php\t2026-04-15 08:36:52.000000000 +0000\n@@ -66,6 +66,10 @@\n \t\t} else {\n \t\t\tif(!$this->isNonceValid($this->form, $_POST['nonce']))\n \t\t\t\tthrow new \\Exception(\"Invalid nonce\");\n+\n+\t\t\tif(!$wpgmza->isUserAllowedToEdit()){\n+\t\t\t\tthrow new \\Exception(\"You do not have permission to perform this action\");\n+\t\t\t}\n \t\t\t\n \t\t\t$oldPullMethod\t= $wpgmza->settings->wpgmza_settings_marker_pull;","1. Authenticate as a Subscriber-level user.\n2. Obtain a valid security nonce for the WP Go Maps admin action. This can be done by sending a POST request to \u002Fwp-admin\u002Fadmin-ajax.php with action=heartbeat and screen_id=wp-google-maps-settings. The plugin's Heartbeat filter will return a 'wpgmza_nonce' in the JSON response.\n3. Send a POST request to \u002Fwp-admin\u002Fadmin-post.php with the 'action' parameter set to 'wpgmza_save_settings', the 'nonce' parameter set to the extracted nonce, and the 'wpgmza_custom_js' parameter containing a malicious script (e.g., alert(document.domain)).\n4. The plugin will save the malicious script into the 'wpgmza_settings' option in the database because it lacks a capability check and explicitly skips sanitization for that specific parameter.\n5. The payload will execute whenever a map is loaded on the frontend or when an administrator visits the plugin's settings page.","gemini-3-flash-preview","2026-04-18 02:53:06","2026-04-18 02:53: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","10.0.05","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-google-maps\u002Ftags\u002F10.0.05","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-google-maps.10.0.05.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-google-maps\u002Ftags\u002F10.0.06","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-google-maps.10.0.06.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-google-maps\u002Ftags"]