Coordinated disclosureCVE-2026-16534WordPress · PHPWPScan · 7.2 High

Proof/CVE-2026-16534

CS.07 — Published CVE

The importer never asked who was promoting whom.

WordPress blocks a lesser role from minting an administrator through the admin screens. The Import and Export Users and Customers plugin took the same actor's CSV file and wrote the rows straight into wp_insert_user — so a role that could only create users could hand itself an administrator, or rewrite an existing admin's password.

CVECVE-2026-16534
Componentimport-users-from-csv-with-meta
Affected< 2.4.2
Fixed2.4.2
WeaknessCWE-269
WPScan CVSS7.2 High
ReporterCharles Vosburgh
A user who could only create accounts imported a CSV, and the import path assigned the administrator role and overwrote an existing admin's credentials.
Weakness
CWE-269
Trigger
CSV role column
Control
wp-admin promotion blocked
Outcome
Administrator minted / hijacked
Public-safety boundary. This page explains the access-control failure, the validation method, and the remediation boundary. It omits a copy-paste weaponized CSV. Version 2.4.2 fixes the issue; the plugin author scheduled fuller PoC disclosure for 2026-08-06.

WordPress does not treat "create a user" and "promote a user to administrator" as the same permission. Core maps them to distinct capabilities — create_users, promote_users, edit_users — and adds a hard rule that a non-administrator can never edit or elevate an administrator. A bulk-import feature is only safe if it re-checks those same capabilities for the person clicking Import.

A user who holds only the user-creation capability must not be able to assign the administrator role, nor edit an account that already holds it.

The bug was not that the plugin could import users. The bug was that the import path trusted the CSV's role, user_pass and target-identity columns as data, while WordPress core treats acting on those same fields as privileged operations gated by the current user's capabilities.

The import handler confirmed the actor could reach the import screen and create users, then looped over rows and called WordPress's user APIs with the CSV's own field values. What it never did was re-map the role change or the edit of an existing account against the acting user — the check core performs on every admin-screen equivalent.

Condensed import pathCSV row → wp_insert_user / wp_update_user
import screen:
  require current_user_can('create_users')   // gate present

per row:
  role      = row['role']            // taken verbatim
  target    = existing user? update : insert
  wp_insert_user([... 'role' => role])       // no promote_users check
  wp_update_user([... 'user_pass' => row['user_pass'],
                      'user_email' => row['user_email']])
                                             // no edit_user(target) check

// missing: current_user_can('promote_users')
// missing: current_user_can('edit_user', target_admin_id)

Core's own map_meta_cap() would have refused both operations for a lesser role: promoting to administrator requires promote_users, and editing an existing administrator is denied outright to anyone who is not one. By calling the low-level functions directly, the importer skipped the layer that enforces those rules.

01 · Actor

User-creation role

A custom role with import access and create_users, nothing more.

02 · Input

Crafted CSV

Role column set to administrator; or an admin's login plus a new password.

03 · Handler

Direct write

Rows passed to wp_insert_user / wp_update_user unchecked.

04 · Result

Admin control

New administrator, or an existing admin's credentials overwritten.

1

Actor holds only user creation

A custom or vendor role (shop manager, staff onboarding, LMS instructor) that can reach the plugin's import screen and create accounts, but cannot promote users or edit administrators through wp-admin.

2

Admin-screen control confirmed active

The same actor tries to set a new user's role to administrator in the normal Users screen and is refused. This proves core's capability boundary is present and enforced.

3

CSV carries the privileged fields

A row sets role=administrator for a new account, or names an existing administrator by login/ID and supplies a new user_pass / user_email.

4

Import writes without re-checking

The handler passes each row to wp_insert_user / wp_update_user. No promote_users check on the role, no per-target edit check on the administrator.

5

Administrator obtained or hijacked

The attacker logs in to the freshly minted administrator, or authenticates as the existing admin whose password they just overwrote. Full site control follows.

The finding was promoted only after the vulnerable path, the negative control and the fixed control were retained together against the same actor and site.

Negative control

Admin screen

The user-creation role is refused when it tries to set administrator or edit an admin in wp-admin.

Vulnerable path

CSV import

The same role imports a CSV; an administrator is created and an existing admin's credentials are overwritten.

Fixed control

2.4.2 import

The privileged row is rejected because the import now enforces role-assignment and per-user edit capabilities.

CONTROL role sets administrator via wp-admin Users screen: DENIED (expected) IMPORT actor holds create_users only, no promote_users ROW role=administrator accepted by import handler ROW existing admin user_pass overwritten by import handler PRIVILEGE-ESCALATION to administrator: CONFIRMED MARKER=CSV_IMPORT_PRIVESC

A capability check that guards the screen is not the same as a capability check that guards the operation. The import screen asked "can this user create accounts?" and then performed operations — promotion, and editing an administrator — that demand far stronger permissions.

The durable engineering rule is that bulk and API paths must re-map every privileged field against the acting user with the same current_user_can() checks the interactive path uses. A CSV column is attacker-controlled input, not an authorization decision.

The SecHive research loop

Agents mapped the capability gap. Evidence proved it crossed a role boundary.

The workflow did not stop at "the import writes users." Each stage had to produce evidence, or a refutation, before the candidate could move forward.

SK.01

Source mapping

Trace the CSV role and target-identity columns into the user-write calls and isolate every capability check on the path.

SK.02

Capability hypothesis

Challenge the assumption that a create-users gate is sufficient to authorize a promote-to-administrator write.

SK.03

Runtime construction

Stand up a non-admin role and the smallest CSV that both mints an administrator and rewrites an existing one.

SK.04

Skeptic gate

Admin-screen negative control, exact privilege marker, and re-run against 2.4.2 as the fixed control.

HUMAN

Promotion and disclosure

Review source, replay, impact, version range, wording and public-safety boundary before reporting to the vendor.

WPScan scores CVE-2026-16534 at 7.2 High on CVSS 3.1. The PR:H term reflects that the attacker must already hold a privileged role — one with import access and user creation — not that the impact is limited. Once that foothold exists, confidentiality, integrity and availability are all fully compromised at the administrator level.

7.2
WPScan · CVSS 3.1 High
CWE-269
Improper privilege management
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

The realistic actor is an inside role or a lower-tier account on a multi-author, membership, e-commerce or LMS site — exactly the deployments that hand out custom roles with user-management slices. Any such role that can reach this import screen becomes an administrator on demand.

PluginAffected rangeFixed
import-users-from-csv-with-meta≤ 2.4.1 (all < 2.4.2)2.4.2

Marketed as Import and Export Users and Customers. Sites that never delegate user management to a non-administrator role are not directly exploitable, but should still upgrade.

The fix is not "block the administrator string in CSV." The durable rule is that the import must authorize each privileged field against the acting user exactly as the interactive path does.

  1. Upgrade to Import and Export Users and Customers 2.4.2 or later.
  2. Enforce current_user_can('promote_users') before honoring any role in a row, and reject or downgrade rows that request roles the actor cannot grant.
  3. Enforce a per-target edit check (current_user_can('edit_user', $id)) before updating an existing account, so administrators cannot be edited by lesser roles.
  4. Until patched, restrict the import screen to full administrators and audit custom roles that carry create_users or list-user capabilities.
  5. Review recently created administrators and any unexpected email/password changes on existing admin accounts.
JULY 2026
Runtime validation completed against the released plugin; negative and fixed controls retained.
JULY 2026
Coordinated disclosure to the plugin author.
2026-07-23
CVE-2026-16534 published; Charles Vosburgh credited as researcher.
2026-07-23
Added to the WordPress vulnerability database (WPScan).
2026-08-06
Vendor-scheduled fuller proof-of-concept disclosure.

The bug lived between two ideas of "who can do this." WordPress core said a lesser role may not promote users or touch an administrator. The plugin said, in effect, "if you reached the import screen, your rows are trusted." A CSV column carried the privileged instruction across that gap.

The CVE is the identifier. The admin-screen negative control is what proved the role boundary was real before the import erased it.
  1. WPScan / WPVDB record for CVE-2026-16534
  2. NVD record for CVE-2026-16534
  3. Import and Export Users and Customers plugin page
  4. WordPress map_meta_cap() — promote_users / edit_users mapping
  5. WordPress wp_insert_user() reference

Credit: Discovered and reported by Charles Vosburgh. Research was AI-assisted through source mapping, hypothesis generation, runtime construction and skeptical validation; final review and disclosure ownership remained human.