OSM – OpenStreetMap <= 6.1.12 - Missing Authorization
Description
The OSM – OpenStreetMap plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 6.1.12. This makes it possible for authenticated attackers, with Contributor-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v6.1.13
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-25323 (OSM - OpenStreetMap) ## 1. Vulnerability Summary The **OSM – OpenStreetMap** plugin for WordPress (up to 6.1.12) contains a missing authorization vulnerability in its AJAX handling logic. Specifically, the function `saveGeotagAndPic` (and potentially a…
Show full research plan
Exploitation Research Plan - CVE-2026-25323 (OSM - OpenStreetMap)
1. Vulnerability Summary
The OSM – OpenStreetMap plugin for WordPress (up to 6.1.12) contains a missing authorization vulnerability in its AJAX handling logic. Specifically, the function saveGeotagAndPic (and potentially associated registration hooks) verifies a nonce but fails to perform a capability check (e.g., current_user_can( 'edit_post', $post_id )). This allows authenticated users with Contributor-level privileges to modify geographical metadata (geotags) for any post or page on the site, regardless of ownership or status.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
osm_save_geotag(inferred from function namesaveGeotagAndPicand plugin naming conventions) - Parameters:
action:osm_save_geotag(inferred)lat: Latitude (e.g.,51.5074)lon: Longitude (e.g.,-0.1278)icon: Icon filename (e.g.,marker_blue.png)post_id: The ID of the target post to modify.geotag_nonce: The CSRF token for the action.
- Authentication: Authenticated (Contributor level or higher).
- Preconditions: The attacker must be logged in as a Contributor and obtain a valid nonce for the
osm_geotag_nonceaction.
3. Code Flow
- Entry Point: The plugin likely registers the AJAX action in the
initoradmin_inithook (not visible in snippet but required for AJAX).- Hook:
add_action( 'wp_ajax_osm_save_geotag', 'saveGeotagAndPic' );
- Hook:
- Execution: The
saveGeotagAndPicfunction inosm.phpis called. - Nonce Verification: The function checks
wp_verify_nonce( $_POST['geotag_nonce'], 'osm_geotag_nonce' ). - Processing: It sanitizes input using
sanitize_text_fieldandwp_unslash. - Vulnerable Sink: The function (based on its purpose) calls
update_post_metaor a similar database update function using the provided$post_idwithout verifying if the current user has permission to edit that specific post.- Note: A Contributor can only edit their own posts. By providing an Admin's
post_id, the Contributor performs an unauthorized modification.
- Note: A Contributor can only edit their own posts. By providing an Admin's
4. Nonce Acquisition Strategy
The nonce osm_geotag_nonce is typically generated for the post editor meta box.
- Identify Script Localization: Look for
wp_localize_scriptin the full plugin source that includesosm_geotag_nonce. - Access Editor: Log in as a Contributor and navigate to the "Add New Post" page (
/wp-admin/post-new.php). - Extract Nonce:
- Use
browser_evalto extract the nonce from the global JavaScript object where the plugin stores its settings. - Likely Variable:
window.OSM_Data?.geotag_nonceor similar (based onosm_geotag_nonceaction). - Manual Check: Inspect the page source for
osm_geotag_nonce.
- Use
5. Exploitation Strategy
- Prerequisite: Create a "Protected Post" as an Admin (e.g., Post ID 123).
- Authentication: Authenticate as a Contributor user.
- Nonce Extraction:
- Navigate to
/wp-admin/post-new.phpas the Contributor. - Extract the
geotag_noncefrom the page source or JS context.
- Navigate to
- Forge Request: Use the
http_requesttool to send an AJAX POST request to modify the Admin's post.- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method: POST
- Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=osm_save_geotag&lat=99.99&lon=99.99&icon=malicious.png&post_id=123&geotag_nonce=[EXTRACTED_NONCE]
- URL:
- Verify Success: Check if the response contains "Location (geotag) saved successfully".
6. Test Data Setup
- Admin User: Created by default.
- Admin Post: Create a published post.
wp post create --post_title="Admin Secret Post" --post_status=publish --post_author=1- Capture the returned Post ID.
- Contributor User: Create a user with the contributor role.
wp user create attacker attacker@example.com --role=contributor --user_pass=password
7. Expected Results
- The AJAX request should return a
200 OKstatus. - The response body should include the string:
Location (geotag) saved successfully. - The metadata for the Admin's post (ID 123) should be updated with the malicious latitude and longitude.
8. Verification Steps
After performing the exploit, verify the database state using WP-CLI:
- Check Post Meta:
wp post meta list [ADMIN_POST_ID]
- Look for Keys:
- Check for meta keys like
osm_geo_dataorosm_lat_lon(the exact key names depend on the truncated part ofsaveGeotagAndPic). - If the latitude
99.99is found in the metadata of the Admin's post, the exploit is successful.
- Check for meta keys like
9. Alternative Approaches
If osm_save_geotag is not the correct action name:
- Search the plugin folder for all
add_action( 'wp_ajax_registrations:grep -r "wp_ajax_" .
- Search for where
osm_geotag_nonceis localized to find the associated JS logic and action name:grep -r "osm_geotag_nonce" .
- If the Contributor cannot access the nonce on
post-new.php, check if the OSM widget or shortcode generator on the frontend exposes it.
Summary
The OSM – OpenStreetMap plugin for WordPress is vulnerable to unauthorized modification of post metadata due to missing capability checks in its AJAX handlers. Authenticated attackers with Contributor-level access or higher can exploit this to change geographical tags (geotags) and map markers for any post or page, including those they do not own.
Vulnerable Code
// File: osm.php, around line 170 function saveGeotagAndPic() { if ( isset( $_POST['lat'], $_POST['lon'], $_POST['icon'], $_POST['post_id'], $_POST['geotag_nonce'] ) ) { $latlon = sanitize_text_field( wp_unslash( $_POST['lat'] ) ) . ',' . sanitize_text_field( wp_unslash( $_POST['lon'] ) ); $icon = sanitize_text_field( wp_unslash( $_POST['icon'] ) ); $post_id = sanitize_text_field( wp_unslash( $_POST['post_id'] ) ); $nonce = sanitize_text_field( wp_unslash( $_POST['geotag_nonce'] ) ); if ( ! wp_verify_nonce( $nonce, 'osm_geotag_nonce' ) ) { echo "Error: Bad ajax request"; } else { // ... execution continues to update_post_meta using $post_id without current_user_can check --- // File: osm.php, around line 215 function savePostMarker() { if ( isset( $_POST['MarkerId'], $_POST['MarkerLat'], $_POST['MarkerLon'], $_POST['MarkerIcon'], $_POST['MarkerName'], $_POST['post_id'], $_POST['marker_nonce'], $_POST['MarkerText'] ) ) { $MarkerId = sanitize_text_field( wp_unslash( $_POST['MarkerId'] ) ); $MarkerLatLon = sanitize_text_field( wp_unslash( $_POST['MarkerLat'] ) ) . ',' . sanitize_text_field( wp_unslash( $_POST['MarkerLon'] ) ); $MarkerIcon = sanitize_text_field( wp_unslash( $_POST['MarkerIcon'] ) ); $MarkerName = sanitize_text_field( wp_unslash( $_POST['MarkerName'] ) ); $post_id = sanitize_text_field( wp_unslash( $_POST['post_id'] ) ); $nonce = sanitize_text_field( wp_unslash( $_POST['marker_nonce'] ) ); // ... // Nonce check only, no capability check if ( ! wp_verify_nonce( $nonce, 'osm_marker_nonce' ) ) { echo "Error: Bad ajax request"; } else { // ... execution continues to update_post_meta using $post_id }
Security Fix
@@ -169,9 +169,17 @@ function saveGeotagAndPic() { if ( isset( $_POST['lat'], $_POST['lon'], $_POST['icon'], $_POST['post_id'], $_POST['geotag_nonce'] ) ) { + + $post_id = absint( wp_unslash( $_POST['post_id'] ) ); + + // SECURITY-FIX: Berechtigungsprüfung + if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) { + echo "Error: Unauthorized access."; + wp_die(); + } + $latlon = sanitize_text_field( wp_unslash( $_POST['lat'] ) ) . ',' . sanitize_text_field( wp_unslash( $_POST['lon'] ) ); $icon = sanitize_text_field( wp_unslash( $_POST['icon'] ) ); - $post_id = sanitize_text_field( wp_unslash( $_POST['post_id'] ) ); $nonce = sanitize_text_field( wp_unslash( $_POST['geotag_nonce'] ) ); if ( ! wp_verify_nonce( $nonce, 'osm_geotag_nonce' ) ) { @@ -215,37 +222,49 @@ function savePostMarker() { if ( isset( $_POST['MarkerId'], $_POST['MarkerLat'], $_POST['MarkerLon'], $_POST['MarkerIcon'], $_POST['MarkerName'], $_POST['post_id'], $_POST['marker_nonce'], $_POST['MarkerText'] ) ) { - $MarkerId = sanitize_text_field( wp_unslash( $_POST['MarkerId'] ) ); - $MarkerLatLon = sanitize_text_field( wp_unslash( $_POST['MarkerLat'] ) ) . ',' . sanitize_text_field( wp_unslash( $_POST['MarkerLon'] ) ); - $MarkerIcon = sanitize_text_field( wp_unslash( $_POST['MarkerIcon'] ) ); - $MarkerName = sanitize_text_field( wp_unslash( $_POST['MarkerName'] ) ); - $post_id = sanitize_text_field( wp_unslash( $_POST['post_id'] ) ); - $nonce = sanitize_text_field( wp_unslash( $_POST['marker_nonce'] ) ); - - $allowed_html = array( + // post_id korrekt behandeln + $post_id = absint( wp_unslash( $_POST['post_id'] ) ); + + // SECURITY-FIX: Berechtigungsprüfung + if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) { + echo "Error: Unauthorized access."; + wp_die(); + } + + // restliche Felder wie gehabt + $MarkerId = absint( wp_unslash( $_POST['MarkerId'] ) ); + $MarkerLatLon = sanitize_text_field( wp_unslash( $_POST['MarkerLat'] ) ) . ',' . + sanitize_text_field( wp_unslash( $_POST['MarkerLon'] ) ); + $MarkerIcon = sanitize_file_name( wp_unslash( $_POST['MarkerIcon'] ) ); + $MarkerName = sanitize_text_field( wp_unslash( $_POST['MarkerName'] ) ); + $nonce = sanitize_text_field( wp_unslash( $_POST['marker_nonce'] ) );
Exploit Outline
To exploit this vulnerability, an attacker must have Contributor-level access to the WordPress dashboard. 1. Access the post editor (e.g., /wp-admin/post-new.php) to extract the localized AJAX nonces 'osm_geotag_nonce' or 'osm_marker_nonce'. 2. Identify a target post ID that the attacker does not have permission to edit (e.g., a published post by an administrator). 3. Send a POST request to /wp-admin/admin-ajax.php using the action 'osm_save_geotag' or 'osm_save_post_marker'. 4. Include the extracted nonce, the target post_id, and the desired latitude/longitude/icon metadata in the request body. Because the plugin only verifies the nonce and not the user's permission to edit the specific post_id, the metadata for the target post will be updated.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.