CVE-2026-9594

WP Maps <= 4.9.4 - Authenticated (Admin+) Stored Cross-Site Scripting via 'location_messages' Parameter

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
4.9.5
Patched in
1d
Time to patch

Description

The WP Maps – Google Maps,OpenStreetMap,Mapbox,Store Locator,Listing,Directory & Filters plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'location_messages' parameter in all versions up to, and including, 4.9.4 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. Exploitation requires the attacker to hold the custom wpgmp_manage_location capability, which is granted to administrators by default but can be assigned to lower-privileged roles via the plugin's Permissions screen.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
High
Privileges Required
High
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=4.9.4
PublishedJune 5, 2026
Last updatedJune 6, 2026
Affected pluginwp-google-map-plugin

What Changed in the Fix

Changes introduced in v4.9.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-9594 (WP Maps Stored XSS) ## 1. Vulnerability Summary The **WP Maps** plugin (up to 4.9.4) is vulnerable to **Stored Cross-Site Scripting (XSS)** via the `location_messages` parameter. The vulnerability exists in the `WPGMP_Model_Location` class within `module…

Show full research plan

Exploitation Research Plan: CVE-2026-9594 (WP Maps Stored XSS)

1. Vulnerability Summary

The WP Maps plugin (up to 4.9.4) is vulnerable to Stored Cross-Site Scripting (XSS) via the location_messages parameter. The vulnerability exists in the WPGMP_Model_Location class within modules/location/model.location.php. Specifically, the write_to_db_backup() method accepts the location_messages POST parameter and saves it to the database using only wp_unslash(), omitting any sanitization (e.g., wp_kses or sanitize_text_field). When these locations are retrieved for display (e.g., on a map), the unsanitized content is rendered, allowing execution of arbitrary JavaScript in the context of any user viewing the map.

2. Attack Vector Analysis

  • Endpoint: Admin form submission (typically admin.php?page=wpgmp_form_location or a related AJAX action).
  • Vulnerable Parameter: location_messages
  • Authentication: Required. An attacker must have the wpgmp_manage_location capability (granted to Administrators by default).
  • Preconditions: The plugin must be active, and at least one map must be created to display the malicious location.

3. Code Flow

  1. Entry Point: An authenticated user submits a request to add or edit a location. This triggers the saving logic, likely calling WPGMP_Model_Location::write_to_db_backup().
  2. Storage (Sink): In modules/location/model.location.php, write_to_db_backup() takes $_POST['location_messages']:
    if ( isset( $_POST['location_messages'] ) )
        $data['location_messages'] = wp_unslash( $_POST['location_messages'] );
    
    The data is then stored in the {$wpdb->prefix}map_locations table via FlipperCode_Database::insert_or_update.
  3. Processing: When a map is rendered via shortcode, modules/shortcode/views/put-wpgmp.php calls $location_obj->fetch().
  4. Transformation: In fetch(), the plugin attempts to decode location_messages:
    if ( ! is_null( $object->location_messages ) ) {
        $decoded = base64_decode( $object->location_messages );
        $parsed  = maybe_serialize( $decoded ); // Note: possible typo in source, likely intended maybe_unserialize
        if ( is_array( $parsed ) && isset( $parsed['googlemap_infowindow_message_one'] ) ) {
            $object->location_messages = $parsed['googlemap_infowindow_message_one'];
        }
    }
    
    If base64_decode fails or the result isn't a serialized array, the original payload persists.
  5. Output (Sink): The fetched locations are passed to the frontend JavaScript (Leaflet/Google Maps), which renders the location_messages inside an InfoWindow without proper output escaping.

4. Nonce Acquisition Strategy

The plugin uses nonces for admin operations. The update_loc function in modules/location/model.location.php explicitly checks for wpgmp-nonce.

Steps to obtain the nonce:

  1. Identify the shortcode: Use [put_wpgmp id='1'].
  2. Create a page with the shortcode using WP-CLI.
  3. Navigate to the Admin Location page: /wp-admin/admin.php?page=wpgmp_form_location.
  4. Use browser_eval to extract the nonce from the localized script. The plugin typically localizes data under a global object.
  5. Target JS Variable: window.wpgmp_local?.nonce or similar. Check the page source for wp_localize_script calls associated with wpgmp-nonce.

5. Exploitation Strategy

The goal is to inject an XSS payload into the location_messages field.

Step 1: Data Setup

Create a Map and a Location to identify the correct entityID and map_id.

# Create a map
wp eval "new WPGMP_Model_Map();" # Simplified: Usually done via UI

Step 2: Payload Delivery

Use http_request to send a POST request to the location save handler.

Request Details:

  • URL: http://localhost:8080/wp-admin/admin.php?page=wpgmp_form_location (or the AJAX handler if confirmed)
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • _wpnonce: [EXTRACTED_NONCE]
    • location_title: XSS Test
    • location_address: 123 Test St
    • location_messages: <script>alert(document.domain)</script>
    • location_settings[extensions_fields]: ...
    • page: wpgmp_form_location
    • action: wpgmp_save_location (Inferred from model class methods)

Note: If the base64_decode in fetch() interferes, the payload should be a base64-encoded serialized array:
location_messages = YToxOntzOjMyOiJnb29nbGVtYXBfaW5mb3dpbmRvd19tZXNzYWdlX29uZSI7czoyNToiPHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0PiI7fQ==

Step 3: Triggering the XSS

Navigate to the page containing the [put_wpgmp] shortcode. The payload will execute when the location's InfoWindow is triggered or the map loads.

6. Test Data Setup

  1. Plugin: Install and activate wp-google-map-plugin v4.9.4.
  2. User: Create an admin user to perform the injection.
  3. Map: Create a map with ID 1.
  4. Shortcode Page:
    wp post create --post_type=page --post_title="Map Page" --post_status=publish --post_content="[put_wpgmp id='1']"
    

7. Expected Results

  • The POST request should return a success message (e.g., "Location added successfully").
  • When viewing the "Map Page", the browser should execute the injected script (e.g., show an alert box).

8. Verification Steps

After the HTTP request, verify the database entry via WP-CLI:

wp db query "SELECT location_messages FROM wp_map_locations ORDER BY location_id DESC LIMIT 1;"

Confirm the output contains the raw <script> tag or the crafted base64 string.

9. Alternative Approaches

If the direct POST to the admin page fails, check for an AJAX entry point:

  • Action: wp_ajax_wpgmp_save_location
  • Endpoint: /wp-admin/admin-ajax.php
  • Parameter: fc-location-new-set (observed in update_loc() as a JSON string). This method might require injecting into the JSON object properties like messages.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Maps plugin (up to 4.9.4) is vulnerable to Stored Cross-Site Scripting (XSS) via the 'location_messages' parameter. Authenticated attackers with the 'wpgmp_manage_location' capability (administrators by default) can inject arbitrary web scripts that execute in the context of any user viewing a map containing the malicious location.

Vulnerable Code

// modules/location/model.location.php
public function write_to_db_backup(){

    $entityID = '';
    if ( isset( $_POST['entityID'] ) )
    $entityID = intval( wp_unslash( $_POST['entityID'] ) );

    if ( isset( $_POST['location_messages'] ) )
    $data['location_messages'] = wp_unslash( $_POST['location_messages'] );

Security Fix

--- modules/location/model.location.php
+++ modules/location/model.location.php
@@ -187,2 +187,2 @@
 			if ( isset( $_POST['location_messages'] ) )
-			$data['location_messages'] = wp_unslash( $_POST['location_messages'] );
+			$data['location_messages'] = wp_kses_post( wp_unslash( $_POST['location_messages'] ) );

Exploit Outline

1. Authenticate with Administrator privileges (or a role with the 'wpgmp_manage_location' capability). 2. Navigate to the map location management screen and extract the 'wpgmp-nonce' from the page source or localized script variables. 3. Send a POST request to '/wp-admin/admin.php?page=wpgmp_form_location' with the 'location_messages' parameter containing a JavaScript payload (e.g., <script>alert(1)</script>). 4. Ensure the request includes required fields: '_wpnonce', 'location_title', and 'location_address'. 5. Navigate to a frontend page containing the '[put_wpgmp]' shortcode that displays the affected map. The script will execute when the map loads or when the location's InfoWindow is opened.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.