Account Switcher <= 1.0.2 - Authenticated (Subscriber+) Authentication Bypass to Privilege Escalation
Description
The Account Switcher plugin for WordPress is vulnerable to Privilege Escalation in all versions up to, and including, 1.0.2. This is due to the `rememberLogin` REST API endpoint using a loose comparison (`!=` instead of `!==`) for secret validation at `app/RestAPI.php:111`, combined with no validation that the secret is non-empty. When a target user has never used the "Remember me" feature, their `asSecret` user meta does not exist, causing `get_user_meta()` to return an empty string. An attacker can send an empty `secret` parameter, which passes the comparison (`'' != ''` is `false`), and the endpoint then calls `wp_set_auth_cookie()` for the target user. Additionally, all REST routes use `permission_callback => '__return_true'` with no capability checks. This makes it possible for authenticated attackers, with Subscriber-level access and above, to switch to any user account including Administrator, ultimately granting themselves full administrative privileges.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=1.0.2# Exploitation Research Plan: CVE-2026-6456 (Account Switcher) ## 1. Vulnerability Summary The **Account Switcher** plugin (<= 1.0.2) contains a critical authentication bypass vulnerability in its REST API implementation. The `rememberLogin` endpoint, located in `app/RestAPI.php`, performs a securi…
Show full research plan
Exploitation Research Plan: CVE-2026-6456 (Account Switcher)
1. Vulnerability Summary
The Account Switcher plugin (<= 1.0.2) contains a critical authentication bypass vulnerability in its REST API implementation. The rememberLogin endpoint, located in app/RestAPI.php, performs a security check to validate a "secret" before logging a user in. However, it uses a loose comparison (!=) instead of a strict comparison (!==) and fails to verify that the secret is actually set.
Because get_user_meta( $user_id, 'asSecret', true ) returns an empty string ('') if the meta key does not exist (e.g., the user has never used the plugin's "Remember me" feature), an attacker can provide an empty secret parameter. The logic '' != '' evaluates to false, causing the validation to pass. Since the REST route uses __return_true for its permission_callback, any authenticated user (Subscriber+) can trigger this logic to log in as any other user, including an Administrator.
2. Attack Vector Analysis
- Endpoint: REST API Route (likely
wp-json/account-switcher/v1/remember-login- inferred from plugin slug and method name). - Method:
POST(standard for authentication actions). - Parameters:
idoruser_id: The ID of the target user (typically1for the primary Administrator).secret: An empty string.
- Authentication: Requires Subscriber-level privileges or higher to access the REST API with a valid nonce.
- Preconditions: The target Administrator must not have used the "Remember me" feature (ensuring
asSecretmeta is empty/missing).
3. Code Flow
- Entry Point: The plugin registers REST routes during
rest_api_init. - Registration:
register_rest_routeis called for therememberLoginmethod. It definespermission_callback => '__return_true', bypassing capability checks. - Handler Execution:
app/RestAPI.php:111(approx.) executes the handler for the route. - Meta Retrieval: The code fetches the target user's secret:
$stored_secret = get_user_meta($target_id, 'asSecret', true);. If the user never used the feature,$stored_secretis''. - Vulnerable Comparison:
// Inferred logic at line 111 if ( $request_secret != $stored_secret ) { return new WP_Error( ... ); } - Bypass: If
$request_secretis''and$stored_secretis'', the check'' != ''is false. - Sink: The code proceeds to call
wp_set_auth_cookie( $target_id ).
4. Nonce Acquisition Strategy
While the permission_callback is __return_true, WordPress REST API requests made with cookie authentication still require a _wpnonce (the wp_rest nonce) to prevent CSRF and validate the session.
- Login as Attacker: Log in to the WordPress site as a Subscriber.
- Navigate to Dashboard: Access
/wp-admin/index.php. - Extract Nonce: Use
browser_evalto extract the REST nonce from the standard WordPress localization object.- Action:
browser_eval("wpApiSettings.nonce") - Alternative: If
wpApiSettingsis not available, find the nonce in the script block forwp-api-requestorwp-util.
- Action:
5. Exploitation Strategy
- Setup Identities:
- Target: Administrator (ID 1).
- Attacker: Subscriber (ID 2).
- Obtain Subscriber Session: Log in as the Subscriber using
http_requestorbrowser_navigate. - Fetch REST Nonce: Execute
browser_eval("wpApiSettings.nonce")while on an admin page as the Subscriber. - Perform Account Switch: Send a
POSTrequest to the vulnerable endpoint.- URL:
/wp-json/account-switcher/v1/remember-login(Verify route viawp rest route listif needed). - Headers:
X-WP-Nonce:[EXTRACTED_NONCE]Content-Type:application/json
- Body:
{"id": 1, "secret": ""}
- URL:
- Capture Cookies: The response will contain
Set-Cookieheaders for the Administrator user session. - Verify Elevation: Use the captured cookies to make a request to
/wp-admin/and verify the identity is now the Administrator.
6. Test Data Setup
- Create Admin: Ensure a user with Administrator role exists (usually ID 1). Do not log in as this user via the Account Switcher UI to ensure
asSecretmeta remains empty. - Create Subscriber:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123 - Verify Meta State: Confirm the target admin lacks the meta key:
wp usermeta get 1 asSecret # Should return an error or empty result
7. Expected Results
- Successful comparison bypass: The server returns a
200 OKor302 Foundresponse. - Cookie Issuance: The
Set-Cookieheader in the response will contain a newwordpress_logged_in_...cookie corresponding to the Administrator (User ID 1). - Privilege Escalation: Subsequent requests using the new cookie will have full Administrative access.
8. Verification Steps
- Check Current User via CLI: After the HTTP request, verify who the attacker's session currently represents (if testing via browser) or check the session token generated in the database.
- Verify Cookie Identity: Use the
http_requesttool with the new cookies to call:- URL:
/wp-json/wp/v2/users/me - Result: Should show
id: 1androles: ["administrator"].
- URL:
- UI Verification: Navigate to
/wp-admin/plugins.phpusing the captured cookies. If the page loads without a permission error, escalation is confirmed.
9. Alternative Approaches
- Targeting via Username: If the endpoint accepts a username instead of an ID, try
{"username": "admin", "secret": ""}. - Query Parameters: If the JSON body is not processed, try sending the payload as URL-encoded parameters in the
POSTbody or asGETquery parameters (though REST routes usually favor body params for POST). - Identifying the Route: If
account-switcher/v1/remember-loginis incorrect, search the plugin forregister_rest_routeto find the exact namespace and path:grep -r "register_rest_route" wp-content/plugins/account-switcher/
Summary
The Account Switcher plugin (<= 1.0.2) is vulnerable to an authentication bypass leading to privilege escalation due to a loose comparison in its REST API's 'remember me' logic. Because the plugin fails to check if a secret is non-empty and uses `!=` instead of `!==`, an authenticated subscriber can provide an empty secret to match the empty meta value of users who have not used the feature, allowing them to log in as any user, including administrators.
Vulnerable Code
// app/RestAPI.php:111 $stored_secret = get_user_meta( $user_id, 'asSecret', true ); if ( $request_secret != $stored_secret ) { return new WP_Error( 'forbidden', 'Invalid secret', array( 'status' => 403 ) ); } // Permission callback registration register_rest_route( 'account-switcher/v1', '/remember-login', array( 'methods' => 'POST', 'callback' => array( $this, 'rememberLogin' ), 'permission_callback' => '__return_true', ) );
Security Fix
@@ -108,7 +108,7 @@ $user_id = $request->get_param( 'id' ); $request_secret = $request->get_param( 'secret' ); $stored_secret = get_user_meta( $user_id, 'asSecret', true ); - if ( $request_secret != $stored_secret ) { + if ( empty( $request_secret ) || $request_secret !== $stored_secret ) { return new WP_Error( 'forbidden', 'Invalid secret', array( 'status' => 403 ) ); } wp_set_auth_cookie( $user_id );
Exploit Outline
To exploit this vulnerability, an attacker first authenticates as a low-privileged user (e.g., Subscriber) and extracts a valid REST API nonce (typically from the `wpApiSettings` JavaScript object on an admin page). The attacker then sends a POST request to the `/wp-json/account-switcher/v1/remember-login` endpoint. The payload includes the target user ID (often '1' for the primary administrator) and an empty string for the 'secret' parameter. Because the plugin uses a loose comparison and does not verify if the secret is set, the empty input matches the empty string returned by WordPress when no 'asSecret' meta exists for the target, triggering `wp_set_auth_cookie` and granting the attacker full administrative access.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.