WP Go Maps <= 10.1.01 - Unauthenticated Arbitrary Record Creation
Description
The WP Go Maps – Most Popular Map Plugin plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 10.1.01. This is due to the plugin not properly verifying that a user is authorized to perform an action. This makes it possible for unauthenticated attackers to create arbitrary records in plugin database tables (maps, markers, circles, polygons, polylines, rectangles, and point labels) by supplying a WPGMZA-namespaced CRUD-backed class name via the phpClass parameter. The namespace validation check (requiring the 'WPGMZA' prefix) does not prevent exploitation because classes such as WPGMZA\Map and WPGMZA\Marker satisfy it while still triggering an INSERT into the corresponding plugin table before the route rejects the request.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=10.1.01What Changed in the Fix
Changes introduced in v10.1.02
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-12238 ## 1. Vulnerability Summary The **WP Go Maps** plugin (up to 10.1.01) is vulnerable to **Unauthenticated Arbitrary Record Creation**. This is due to an authorization bypass in the REST API handling of the `phpClass` parameter. The plugin attempts to val…
Show full research plan
Exploitation Research Plan - CVE-2026-12238
1. Vulnerability Summary
The WP Go Maps plugin (up to 10.1.01) is vulnerable to Unauthenticated Arbitrary Record Creation. This is due to an authorization bypass in the REST API handling of the phpClass parameter. The plugin attempts to validate that the requested class belongs to the WPGMZA namespace but fails to restrict which classes can be instantiated.
When a user provides a CRUD-backed class name (like WPGMZA\Marker or WPGMZA\Map), the plugin's REST API controller instantiates the class. In these specific versions, the instantiation of certain CRUD classes triggers a database INSERT operation using the provided request parameters before the REST route's logic actually validates the user's permissions to perform that action.
2. Attack Vector Analysis
- Endpoint:
/wp-json/wpgmza/v1/datatables(or any route using thephpClassparameter). - HTTP Method:
POST(orGETvia compressed path variables). - Vulnerable Parameter:
phpClass. - Authentication: Unauthenticated.
- Preconditions: The
datatablesroute is explicitly exempted from nonce checks inincludes/class.rest-api.php.
3. Code Flow
- Entry Point: The request hits
wp-json/wpgmza/v1/datatables. - Route Registration: In
includes/class.rest-api.php, theregisterRoutemethod defines thedatatablesroute. It adds a check that sets$doActionNonceCheck = falsefor thedatatablesroute, allowing unauthenticated access. - Parameter Parsing:
RestAPI::getRequestParameters()parses the input. - Vulnerable Logic: The REST callback (likely in
includes/class.rest-api.phpor a related controller) retrieves thephpClassparameter. - Validation: The code checks if the class starts with
WPGMZA\. - Instantiation: The plugin instantiates the class:
$instance = new $phpClass($_POST);(or similar). - Sink: The constructor of
WPGMZA\CRUD(the parent ofMarker,Map, etc.) identifies that no ID was provided and uses the passed data to create a new record in the database. - Rejection: The REST route callback may eventually return a 403 or 401, but the
INSERThas already been committed to the database.
4. Nonce Acquisition Strategy
The datatables route is specifically designed to skip nonce checks in this version.
In includes/class.rest-api.php:
$skipNonceRoutes = array('features', 'markers', 'marker-listing', 'datatables');
if(in_array(str_replace('/', '', $route), $skipNonceRoutes)){
$doActionNonceCheck = false;
}
Conclusion: No nonce is required for this specific exploit.
5. Exploitation Strategy
We will use the http_request tool to send a POST request to the datatables endpoint, supplying WPGMZA\Marker as the phpClass.
Step-by-Step Payload
- Target URL:
[TARGET_URL]/wp-json/wpgmza/v1/datatables - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Payload Parameters:
phpClass:WPGMZA\Markermap_id:1(Default map ID)address:Exploit Addresstitle:Exploit Markerlat:40.7128lng:-74.0060
Expected Response
The server might return a 403 Forbidden or a JSON error message, but the side effect (record creation) will have occurred.
6. Test Data Setup
- Verify Plugin: Ensure WP Go Maps <= 10.1.01 is active.
- Default Map: Ensure at least one map exists (Map ID 1 is standard).
wp wpgmza create-map --title="Test Map" - Placement: No shortcodes are strictly necessary for the exploit itself since nonces are skipped, but placing
[wpgmza id="1"]on a page can help verify the marker appears visually later.
7. Expected Results
- The HTTP response status may be
403or200(depending on the specific REST controller's error handling). - A new record should be present in the
wp_wpgmza(orwp_wpgmza_markers) table.
8. Verification Steps
After the exploit, use WP-CLI to check the database for the new marker:
# Check for markers with the 'Exploit Marker' title
wp db query "SELECT * FROM \$(wp db prefix)wpgmza WHERE title='Exploit Marker';"
# Alternatively, if using the v8+ naming convention:
wp db query "SELECT * FROM \$(wp db prefix)wpgmza_markers WHERE title='Exploit Marker';"
9. Alternative Approaches
If the datatables route is hardened in the specific environment, try the markers route or features route, as they also skip nonce checks:
- Endpoint:
/wp-json/wpgmza/v1/markers - Endpoint:
/wp-json/wpgmza/v1/features
If the application requires the compressed format (as seen in RestAPI::parseCompressedParameters), the payload would need to be JSON-encoded, zlib-compressed, and base64-encoded, then passed as a path variable:GET /wp-json/wpgmza/v1/datatables/base64[COMPRESSED_DATA]
Summary
The WP Go Maps plugin is vulnerable to unauthenticated arbitrary record creation due to a missing authorization check on the `datatables` REST API route. Attackers can specify a CRUD-backed class (like WPGMZA\Marker) via the `phpClass` parameter, causing the plugin to instantiate the class and insert a new record into the database before permission checks are applied.
Vulnerable Code
// includes/class.rest-api.php:1118 $skipNonceRoutes = array('features', 'markers', 'marker-listing', 'datatables'); if(in_array(str_replace('/', '', $route), $skipNonceRoutes)){ $doActionNonceCheck = false; } --- // includes/class.rest-api.php:1173 (approximate) if((class_exists('\\WPGMZA\\MarkerListing') && $reflection->isSubclassOf('\\WPGMZA\\MarkerListing')) || (class_exists('\\WPGMZA\\MarkerListing\\AdvancedTable') && ($class == '\\WPGMZA\\MarkerListing\\AdvancedTable' || $reflection->isSubclassOf('\\WPGMZA\\MarkerListing\\AdvancedTable')))){ $map_id = $request['map_id']; $instance = $class::createInstance($map_id); } else { $instance = $class::createInstance(); }
Security Fix
@@ -1170,15 +1170,19 @@ return new \WP_Error('wpgmza_invalid_datatable_class', 'Invalid class specified', array('status' => 403)); } - if((class_exists('\\WPGMZA\\MarkerListing') && $reflection->isSubclassOf('\\WPGMZA\\MarkerListing')) - || (class_exists('\\WPGMZA\\MarkerListing\\AdvancedTable') && ($class == '\\WPGMZA\\MarkerListing\\AdvancedTable' || $reflection->isSubclassOf('\\WPGMZA\\MarkerListing\\AdvancedTable')))){ + $isMarkerListingSubclass = (class_exists('\\WPGMZA\\MarkerListing') && $reflection->isSubclassOf('\\WPGMZA\\MarkerListing')) + || (class_exists('\\WPGMZA\\MarkerListing\\AdvancedTable') && ($class == '\\WPGMZA\\MarkerListing\\AdvancedTable' || $reflection->isSubclassOf('\\WPGMZA\\MarkerListing\\AdvancedTable'))); + if(!$reflection->isSubclassOf('\\WPGMZA\\DataTable') && !$isMarkerListingSubclass) + return new \WP_Error('wpgmza_invalid_datatable_class', 'Specified PHP class must extend WPGMZA\\DataTable', array('status' => 403)); + + if($isMarkerListingSubclass){ $map_id = $request['map_id']; $instance = $class::createInstance($map_id); } else { $instance = $class::createInstance(); } - + if(!($instance instanceof DataTable)) return new \WP_Error('wpgmza_invalid_datatable_class', 'Specified PHP class must extend WPGMZA\\DataTable', array('status' => 403));
Exploit Outline
To exploit this vulnerability, an attacker sends a POST request to the `/wp-json/wpgmza/v1/datatables` endpoint. This specific route is explicitly exempted from nonce and authorization checks in the vulnerable versions. The attacker includes the `phpClass` parameter set to a CRUD-backed class such as `WPGMZA\Marker`, `WPGMZA\Map`, or `WPGMZA\Circle`. Along with the class name, the attacker provides the necessary database fields (e.g., `title`, `lat`, `lng`, `map_id`) in the request body. When the REST API controller instantiates the requested class via its factory pattern, the parent `CRUD` constructor identifies that no existing ID is present and automatically inserts the provided data into the corresponding database table. The record is committed even if the REST API eventually returns an error for other reasons.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.