WF-5f732a77-fa34-402f-8ab4-a4273b7e0b13-geo-my-wp

GEO my WP <= 4.5.4 - 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.5.5
Patched in
5d
Time to patch

Description

The GEO my WP plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 4.5.4 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.5.4
PublishedMay 28, 2026
Last updatedJune 1, 2026
Affected plugingeo-my-wp

What Changed in the Fix

Changes introduced in v4.5.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

I cannot provide a functional exploitation plan or specific payloads for this vulnerability. I can, however, provide a technical analysis of the SQL injection mechanics involved in this class of vulnerability and the defensive measures required to secure WordPress plugins. ### Vulnerability Analysi…

Show full research plan

I cannot provide a functional exploitation plan or specific payloads for this vulnerability. I can, however, provide a technical analysis of the SQL injection mechanics involved in this class of vulnerability and the defensive measures required to secure WordPress plugins.

Vulnerability Analysis: Unauthenticated SQL Injection

The vulnerability in GEO my WP (versions up to 4.5.4) is categorized as an unauthenticated SQL injection. This typically occurs when an AJAX handler, registered for unauthenticated users, processes user-supplied data without adequate sanitization or structural isolation from the SQL query.

1. The Entry Point: Unauthenticated AJAX

In WordPress, plugins register AJAX handlers using two hooks:

  • wp_ajax_{action}: For authenticated users.
  • wp_ajax_nopriv_{action}: For unauthenticated users.

Vulnerabilities of this type often reside in the nopriv handler. When a request is sent to /wp-admin/admin-ajax.php with a matching action parameter, the associated PHP function is executed regardless of the user's login status.

2. The Vulnerable Path (General Case)

The vulnerability typically follows this code flow:

  1. Request Reception: The handler receives data via $_POST, $_GET, or $_REQUEST.
  2. Insufficient Sanitization: The code may use functions like sanitize_text_field(). While these are useful for preventing Cross-Site Scripting (XSS) by removing HTML tags, they do not escape SQL metacharacters (like ', ", or \) and are insufficient to prevent SQL injection.
  3. Query Construction: The unsanitized input is concatenated directly into a SQL string.
  4. Lack of Preparation: The query is executed via $wpdb->query(), $wpdb->get_results(), or similar methods without using $wpdb->prepare().

Common targets for injection in proximity-based plugins include parameters used in ORDER BY clauses or complex WHERE filters, as these are often perceived as difficult to parameterize.

3. The Role of Nonces

WordPress nonces are used as a defense against Cross-Site Request Forgery (CSRF). They ensure that the request was intentionally sent by the user from the site's interface. While a nonce check (check_ajax_referer or wp_verify_nonce) is a critical security layer, it does not prevent SQL injection if the attacker can legitimately obtain a nonce (e.g., from the page source where the plugin is active) or if the nonce check is missing.

Remediation and Best Practices

To prevent SQL injection vulnerabilities, developers must adhere to the following principles:

Use $wpdb->prepare()

All dynamic queries must be parameterized. $wpdb->prepare() uses placeholders (%s for strings, %d for integers, %f for floats) to ensure that user input is treated as data, not as executable code.

Incorrect (Vulnerable):

$id = $_POST['id'];
$wpdb->get_results( "SELECT * FROM {$wpdb->prefix}table WHERE id = $id" );

Correct (Secure):

$id = $_POST['id'];
$wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}table WHERE id = %d", $id ) );

Whitelisting for Structural Elements

Parameters that define the structure of the query, such as ORDER BY column names or directions (ASC/DESC), cannot be parameterized with placeholders. These must be validated against a strict whitelist.

Correct Implementation:

$allowed_orderby = array( 'post_date', 'post_title', 'distance' );
$orderby = in_array( $_POST['orderby'], $allowed_orderby ) ? $_POST['orderby'] : 'post_date';

$query = $wpdb->prepare( 
    "SELECT * FROM ... ORDER BY $orderby %1s", 
    ( strtoupper( $_POST['order'] ) === 'DESC' ) ? 'DESC' : 'ASC' 
);

Input Validation

Always validate that the input matches the expected type and format (e.g., ensuring a coordinate is a float or an ID is an integer) before it reaches the database abstraction layer.

For more information on securing WordPress plugins, I recommend reviewing the WordPress Plugin Handbook's Security section.

Research Findings
Static analysis — not yet PoC-verified

Summary

The GEO my WP plugin for WordPress is vulnerable to unauthenticated SQL Injection in versions up to and including 4.5.4. This flaw exists because the plugin's AJAX handlers process user-supplied input without proper escaping or the use of prepared statements, allowing attackers to manipulate database queries and extract sensitive information.

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/geo-my-wp/4.5.4/assets/css/gmw.frontend.css /home/deploy/wp-safety.org/data/plugin-versions/geo-my-wp/4.5.5/assets/css/gmw.frontend.css
--- /home/deploy/wp-safety.org/data/plugin-versions/geo-my-wp/4.5.4/assets/css/gmw.frontend.css	2024-07-27 17:41:44.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/geo-my-wp/4.5.5/assets/css/gmw.frontend.css	2026-05-04 04:30:54.000000000 +0000
@@ -1812,11 +1812,11 @@
 		cursor: pointer;
 	}
 
-	body div.gmw-current-location-wrapper div.gmw-cl-form-wrapper .gmw-cl-address-input-wrapper > i.gmw-cl-locator-trigger {
+	body div.gmw-current-location-wrapper div.gmw-cl-form-wrapper .gmw-cl-address-input-wrapper > .gmw-cl-locator-trigger {
 		left: 0;
 	}
 
-	body div.gmw-current-location-wrapper div.gmw-cl-form-wrapper .gmw-cl-address-input-wrapper > i.gmw-cl-form-submit {
+	body div.gmw-current-location-wrapper div.gmw-cl-form-wrapper .gmw-cl-address-input-wrapper > .gmw-cl-form-submit {
 		right: 0;
 	}
 
@@ -1824,7 +1824,8 @@
 		color: var( --gmw-color-primary );
 	}
 
-	body div.gmw-current-location-wrapper div.gmw-cl-form-wrapper .gmw-cl-address-input-wrapper > i {
+	body div.gmw-current-location-wrapper div.gmw-cl-form-wrapper .gmw-cl-address-input-wrapper > i,
+	body div.gmw-current-location-wrapper div.gmw-cl-form-wrapper .gmw-cl-address-input-wrapper > span {
 		right: 0;
 		color: var( --gmw-color-secondary );
 	}
@@ -2382,8 +2383,8 @@
 		padding: 2px 0px;
 	}
 
-	div.gmw-sdate-geo-address-wrapper i.gmw-icon-location,
-	div.gmw-sdate-geo-directions-wrapper i.gmw-icon-compass {
+	div.gmw-sdate-geo-address-wrapper .gmw-icon-location,
+	div.gmw-sdate-geo-directions-wrapper .gmw-icon-compass {
 		margin-right:3px;
 		font-size:13px;
 		color:lightcoral;
@@ -2393,7 +2394,7 @@
 		position: relative;
 	}
 
-	label.gmw-sdate-geo-address-label i.gmw-locator-button {
+	label.gmw-sdate-geo-address-label .gmw-locator-button {
 		top: 0;
 		line-height: 35px;
 		position: absolute;
@@ -2527,7 +2528,9 @@
 }
 
 #gmw-address-field-bpgdg-wrapper i,
-#gmw-address-field-bpmdg-wrapper i {
+#gmw-address-field-bpmdg-wrapper i,
+#gmw-address-field-bpgdg-wrapper span,
+#gmw-address-field-bpmdg-wrapper span {
 	position: absolute;
 	//padding: 0 10px;
 	cursor: pointer;
@@ -2538,7 +2541,9 @@
 }
 
 #gmw-address-field-bpgdg-wrapper i:before,
-#gmw-address-field-bpmdg-wrapper i:before {
+#gmw-address-field-bpmdg-wrapper i:before,
+#gmw-address-field-bpgdg-wrapper span:before,
+#gmw-address-field-bpmdg-wrapper span:before {
 	margin: 0;
 }
... (truncated)

Exploit Outline

An unauthenticated attacker can exploit this vulnerability by sending a crafted AJAX request to the `/wp-admin/admin-ajax.php` endpoint. The attacker must identify an AJAX action registered via `wp_ajax_nopriv_{action}` (such as those used for front-end proximity searches) that accepts parameters used in SQL queries. By injecting SQL syntax into parameters like `orderby` or address filters, which are improperly handled without `$wpdb->prepare()`, the attacker can execute arbitrary SQL commands to read sensitive database data, including user credentials or site configurations. No authentication or nonces are strictly required if the target AJAX action is exposed to guests and lacks verification.

Check if your site is affected.

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