CVE-2026-25323

OSM – OpenStreetMap <= 6.1.12 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
6.1.13
Patched in
96d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=6.1.12
PublishedJanuary 29, 2026
Last updatedMay 4, 2026
Affected pluginosm

What Changed in the Fix

Changes introduced in v6.1.13

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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 name saveGeotagAndPic and 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_nonce action.

3. Code Flow

  1. Entry Point: The plugin likely registers the AJAX action in the init or admin_init hook (not visible in snippet but required for AJAX).
    • Hook: add_action( 'wp_ajax_osm_save_geotag', 'saveGeotagAndPic' );
  2. Execution: The saveGeotagAndPic function in osm.php is called.
  3. Nonce Verification: The function checks wp_verify_nonce( $_POST['geotag_nonce'], 'osm_geotag_nonce' ).
  4. Processing: It sanitizes input using sanitize_text_field and wp_unslash.
  5. Vulnerable Sink: The function (based on its purpose) calls update_post_meta or a similar database update function using the provided $post_id without 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.

4. Nonce Acquisition Strategy

The nonce osm_geotag_nonce is typically generated for the post editor meta box.

  1. Identify Script Localization: Look for wp_localize_script in the full plugin source that includes osm_geotag_nonce.
  2. Access Editor: Log in as a Contributor and navigate to the "Add New Post" page (/wp-admin/post-new.php).
  3. Extract Nonce:
    • Use browser_eval to extract the nonce from the global JavaScript object where the plugin stores its settings.
    • Likely Variable: window.OSM_Data?.geotag_nonce or similar (based on osm_geotag_nonce action).
    • Manual Check: Inspect the page source for osm_geotag_nonce.

5. Exploitation Strategy

  1. Prerequisite: Create a "Protected Post" as an Admin (e.g., Post ID 123).
  2. Authentication: Authenticate as a Contributor user.
  3. Nonce Extraction:
    • Navigate to /wp-admin/post-new.php as the Contributor.
    • Extract the geotag_nonce from the page source or JS context.
  4. Forge Request: Use the http_request tool 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]
      
  5. Verify Success: Check if the response contains "Location (geotag) saved successfully".

6. Test Data Setup

  1. Admin User: Created by default.
  2. 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.
  3. 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 OK status.
  • 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:

  1. Check Post Meta:
    • wp post meta list [ADMIN_POST_ID]
  2. Look for Keys:
    • Check for meta keys like osm_geo_data or osm_lat_lon (the exact key names depend on the truncated part of saveGeotagAndPic).
    • If the latitude 99.99 is found in the metadata of the Admin's post, the exploit is successful.

9. Alternative Approaches

If osm_save_geotag is not the correct action name:

  1. Search the plugin folder for all add_action( 'wp_ajax_ registrations:
    • grep -r "wp_ajax_" .
  2. Search for where osm_geotag_nonce is localized to find the associated JS logic and action name:
    • grep -r "osm_geotag_nonce" .
  3. If the Contributor cannot access the nonce on post-new.php, check if the OSM widget or shortcode generator on the frontend exposes it.
Research Findings
Static analysis — not yet PoC-verified

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

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/osm/6.1.12/osm.php /home/deploy/wp-safety.org/data/plugin-versions/osm/6.1.13/osm.php
--- /home/deploy/wp-safety.org/data/plugin-versions/osm/6.1.12/osm.php	2026-01-25 15:47:46.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/osm/6.1.13/osm.php	2026-01-25 18:49:30.000000000 +0000
@@ -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.