Video Gallery <= 4.0.3 - Authenticated (Subscriber+) Arbitrary Function Call via 'path' Parameter
Description
The Youtube Showcase plugin for WordPress is vulnerable to Arbitrary Function Call in versions up to and including 4.0.3. This is due to insufficient validation of the 'path' parameter in the emd_delete_file() AJAX handler in includes/common-functions.php. The user-supplied value is passed through sanitize_text_field(), has its trailing '_PLUGIN_DIR' substring stripped, and is then invoked as a PHP function name with no arguments via `$sess_name()`. The handler is gated only by a nonce — no current_user_can() check is present — and the nonce is emitted on any front-end page that renders a form shortcode containing file fields. This makes it possible for authenticated attackers, with Subscriber-level access and above, to invoke arbitrary zero-argument PHP functions (such as phpinfo, phpversion, get_defined_vars, error_get_last), resulting in sensitive information disclosure and potential further compromise depending on the functions available in the environment.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=4.0.3What Changed in the Fix
Changes introduced in v4.0.4
Source Code
WordPress.org SVNThis research plan outlines the technical steps required to demonstrate the arbitrary function call vulnerability (CVE-2026-12923) in the **Youtube Showcase** plugin. ### 1. Vulnerability Summary The `Youtube Showcase` plugin (up to 4.0.3) contains an authenticated arbitrary function call vulnerabi…
Show full research plan
This research plan outlines the technical steps required to demonstrate the arbitrary function call vulnerability (CVE-2026-12923) in the Youtube Showcase plugin.
1. Vulnerability Summary
The Youtube Showcase plugin (up to 4.0.3) contains an authenticated arbitrary function call vulnerability in its AJAX handler emd_delete_file(). The vulnerability stems from a lack of validation on the path parameter. The user-controlled string is processed to remove a specific suffix and then executed directly as a PHP function name. Because the handler lacks capability checks (e.g., current_user_can()), any authenticated user with Subscriber-level access can invoke zero-argument PHP functions.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
emd_delete_file(inferred from function name) - HTTP Method: POST
- Vulnerable Parameter:
path - Authentication: Required (Subscriber+)
- Precondition: A valid nonce must be obtained from a front-end page rendering a form with file fields.
3. Code Flow
- Entry Point: The AJAX action
wp_ajax_emd_delete_file(and potentiallywp_ajax_nopriv_emd_delete_file) is registered, pointing to the functionemd_delete_fileinincludes/common-functions.php. - Input Handling: The function retrieves
$_POST['path']. - Sanitization: The input is passed through
sanitize_text_field(). - Transformation: The code performs a string replacement:
$sess_name = str_replace('_PLUGIN_DIR', '', $path);(as per the vulnerability description). - The Sink: The resulting variable is invoked as a dynamic function call:
$sess_name();. - Constraint: The function called must accept zero arguments to avoid a
ArgumentCountErrorin PHP 7.1+.
4. Nonce Acquisition Strategy
The vulnerability description states the nonce is emitted on front-end pages containing form shortcodes with file fields. Based on the plugin structure (includes/entities/emd-video-shortcodes.php), the relevant shortcode is likely used for video submissions.
- Identify Shortcode: The plugin likely uses
[youtube_showcase_video_form]or[emd_video_form](inferred). - Setup Page: Create a public page containing this shortcode.
wp post create --post_type=page --post_title="Submit Video" --post_status=publish --post_content='[youtube_showcase_video_form]' - Navigate and Extract:
- Login as a Subscriber.
- Navigate to the newly created page.
- Use
browser_evalto locate the nonce. In eMarket Design plugins, AJAX variables are typically localized under a global object likeemd_ajax_varsorwpas_ajax_vars. - Likely Key:
window.emd_ajax_vars?.nonceorwindow.wpas_ajax_vars?.nonce. - Specific Search: Check the page source for
wp_create_nonce('emd_delete_file')or similar.
5. Exploitation Strategy
The goal is to trigger phpinfo() to prove arbitrary code execution/information disclosure.
- Request URL:
http://<target>/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Payload:
action:emd_delete_filepath:phpinfo_PLUGIN_DIR(The_PLUGIN_DIRsuffix will be stripped, leavingphpinfo)nonce:[EXTRACTED_NONCE]
HTTP Request Example:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Cookie: wordpress_logged_in_...
action=emd_delete_file&path=phpinfo_PLUGIN_DIR&nonce=a1b2c3d4e5
6. Test Data Setup
- Install Plugin: Install and activate
youtube-showcaseversion 4.0.3. - Create User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Identify Form Page: If the plugin’s "Setup Assistant" was used, it may have already created a "Video Submission" or "Add Video" page. If not, create one using the steps in Section 4.
7. Expected Results
- Successful Exploit: The server response should contain the standard PHP Information (
phpinfo) HTML output. - Alternative PoC: Using
path=get_defined_vars_PLUGIN_DIRshould return a serialized or dumped list of defined variables (depending on how the plugin handles the function's return value).
8. Verification Steps
Since this is an arbitrary function call, verification is best done by observing the HTTP response:
- Verify the presence of the string
PHP Versionin the response body. - Verify the presence of the string
ConfigurationorEnvironmentin the response body.
9. Alternative Approaches
If phpinfo() is disabled or filtered, attempt to trigger functions that provide confirmation without extensive output:
path=error_get_last_PLUGIN_DIR: Might disclose sensitive file paths if errors occurred.path=phpversion_PLUGIN_DIR: Returns the PHP version string.path=wp_get_current_user_PLUGIN_DIR: May return user object data if the plugin echoes the result of the dynamic call.
Note on _PLUGIN_DIR: If the str_replace logic is str_replace('_PLUGIN_DIR', '', $path), sending phpinfo_PLUGIN_DIR is mandatory. If the logic simply permits any string and _PLUGIN_DIR happens to be part of a path the developer intended to use, then path=phpinfo alone might work. The researcher should test both.
Summary
The YouTube Showcase plugin for WordPress is vulnerable to an authenticated arbitrary function call due to the unsafe use of dynamic function names in the emd_delete_file() AJAX handler. An attacker with Subscriber-level permissions or higher can provide a crafted path parameter which is processed and then executed as a PHP function call. This allows for sensitive information disclosure (e.g., via phpinfo) or other actions depending on the environment's available PHP functions.
Vulnerable Code
// includes/common-functions.php $path = sanitize_text_field($_POST['path']); $myapp = strtolower(preg_replace('/_PLUGIN_DIR$/','',$path)); $sess_name = strtoupper($myapp); $session_class = $sess_name();
Security Fix
@@ -1062,24 +1062,35 @@ $ret = check_ajax_referer('emd_delete_file', 'nonce', false); if ($ret === false) { echo '<div class="text-danger"><a href="' . wp_get_referer() . '">' . esc_html__('Please refresh the page and try again.', 'youtube-showcase') . '</a></div>'; - die(); + wp_die(); } - $path = sanitize_text_field($_POST['path']); - $myapp = strtolower(preg_replace('/_PLUGIN_DIR$/','',$path)); + $myapp = 'youtube_showcase'; $sess_name = strtoupper($myapp); - $session_class = $sess_name(); + if ( function_exists($sess_name) ) { + $session_class = $sess_name(); + } else { + echo '<div class="text-danger">' . esc_html__('System configuration error.', 'youtube-showcase') . '</div>'; + wp_die(); + } + if ( ! $session_class || ! isset($session_class->session) ) { + echo '<div class="text-danger">' . esc_html__('Session handler unavailable.', 'youtube-showcase') . '</div>'; + wp_die(); + } $sess_files = $session_class->session->get('uploads'); - $field = sanitize_text_field($_POST['field']); - if(!empty($sess_files[$field])){ - foreach($sess_files[$field] as $kattch => $myattch){ - if($myattch['name'] == sanitize_text_field($_POST['del_file'])){ + $field = isset($_POST['field']) ? sanitize_text_field($_POST['field']) : ''; + if ( ! empty( $sess_files[$field] ) && isset( $_POST['del_file'] ) ) { + $del_file_target = sanitize_text_field($_POST['del_file']); + + foreach ( $sess_files[$field] as $kattch => $myattch ) { + if ( isset($myattch['full_path']) && $myattch['full_path'] === $del_file_target ) { unset($sess_files[$field][$kattch]); } } - $session_class->session->set('uploads',$sess_files); + // Update the user's specific session + $session_class->session->set('uploads', $sess_files); } echo 1; - die(); + wp_die(); } } if(!function_exists('emd_get_attachment_layout')){ @@ -3,7 +3,7 @@ Tags: video gallery, youtube, playlist, youtube gallery, youtube channel Requires at least: 5.8 Tested up to: 7.0 -Stable tag: 4.0.3 +Stable tag: 4.0.4 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Plugin URI: https://emdplugins.com/youtube-showcase @@ -103,6 +103,8 @@ 9. CSV Import/Export – Bulk manage your library with the eMD CSV Extension for high-volume sites. == Changelog == += 4.0.4 = +* FIXED Function call vulnerability in the file deletion AJAX handler. = 4.0.3 = * FIXED Patched an unrestricted file upload vulnerability. = 4.0.2 = @@ -89,7 +89,7 @@ * @return void */ private function define_constants() { - define('YOUTUBE_SHOWCASE_VERSION', '4.0.3'); + define('YOUTUBE_SHOWCASE_VERSION', '4.0.4'); define('YOUTUBE_SHOWCASE_AUTHOR', 'eMarket Design'); define('YOUTUBE_SHOWCASE_NAME', 'Youtube Showcase'); define('YOUTUBE_SHOWCASE_PLUGIN_FILE', __FILE__);
Exploit Outline
To exploit this vulnerability, an attacker first authenticates with Subscriber-level privileges and visits a page containing the plugin's form shortcode to obtain a valid security nonce for the emd_delete_file action. The attacker then sends a POST request to the /wp-admin/admin-ajax.php endpoint with the action parameter set to emd_delete_file, the nonce parameter set to the extracted value, and the path parameter set to a desired function name followed by the _PLUGIN_DIR suffix (e.g., phpinfo_PLUGIN_DIR). The server-side code strips the suffix and executes the resulting string as a PHP function, returning the function's output in the HTTP response.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.