Simple JWT Login <= 3.6.6 - Authenticated (Subscriber+) Authentication Bypass to Privilege Escalation via 'payload' Parameter
Description
The Simple JWT Login – Allows you to use JWT on REST endpoints. plugin for WordPress is vulnerable to Authentication Bypass to Privilege Escalation in all versions up to, and including, 3.6.6 via the `payload` parameter. The vulnerability exists because `AuthenticateService::generatePayload()` only overwrites JWT payload keys whose names appear in the admin-configured `jwt_payload` list — leaving any attacker-supplied identity claims such as `email`, `id`, or `username` intact and signed into the JWT with the site's HS256 secret. This makes it possible for authenticated attackers, with subscriber-level access and above, to escalate their privileges to that of an Administrator by injecting a target administrator's email address into the `payload` parameter at the `/wp-json/simple-jwt-login/v1/auth` endpoint, then redeeming the resulting JWT at the `/autologin` endpoint to obtain a fully authenticated session as that administrator.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=3.6.6What Changed in the Fix
Changes introduced in v3.6.7
Source Code
WordPress.org SVNI cannot fulfill this request. I am prohibited from generating actionable exploitation plans, specific attack payloads, or detailed procedures for bypassing security controls on specific software vulnerabilities. I can, however, provide general information about secure JWT implementation or discuss …
Show full research plan
I cannot fulfill this request. I am prohibited from generating actionable exploitation plans, specific attack payloads, or detailed procedures for bypassing security controls on specific software vulnerabilities. I can, however, provide general information about secure JWT implementation or discuss common privilege management vulnerabilities in web applications from a defensive perspective.
For information on how to secure your WordPress installation or more details on this specific vulnerability from a remediation standpoint, I recommend consulting the official WordPress security documentation or the plugin developer's patch notes for version 3.6.7.
Summary
The Simple JWT Login plugin allows authenticated users to inject arbitrary claims into a signed JWT by providing a JSON-encoded 'payload' parameter during authentication. Because the plugin fails to unset sensitive identity claims from the user-supplied payload before signing, a subscriber can generate a valid JWT for an administrator and use it to bypass authentication via the autologin feature.
Vulnerable Code
// src/Services/AuthenticateService.php public static function generatePayload( $payload, $wordPressData, $jwtSettings, $user ) { $payload[AuthenticationSettings::JWT_PAYLOAD_PARAM_IAT] = time(); foreach ($jwtSettings->getAuthenticationSettings()->getJwtPayloadParameters() as $parameter) { if ($parameter === AuthenticationSettings::JWT_PAYLOAD_PARAM_IAT || $jwtSettings->getAuthenticationSettings()->isPayloadDataEnabled($parameter) === false ) { continue; } switch ($parameter) { case AuthenticationSettings::JWT_PAYLOAD_PARAM_EXP: $ttl = (int)$jwtSettings->getAuthenticationSettings()->getAuthJwtTtl() * 60; $payload[$parameter] = time() + $ttl; break; case AuthenticationSettings::JWT_PAYLOAD_PARAM_ID: $payload[$parameter] = $wordPressData->getUserProperty($user, 'ID'); break; case AuthenticationSettings::JWT_PAYLOAD_PARAM_EMAIL: $payload[$parameter] = $wordPressData->getUserProperty($user, 'user_email'); break; case AuthenticationSettings::JWT_PAYLOAD_PARAM_SITE: $payload[$parameter] = $wordPressData->getSiteUrl(); break; case AuthenticationSettings::JWT_PAYLOAD_PARAM_USERNAME: $payload[$parameter] = $wordPressData->getUserProperty($user, 'user_login'); break; case AuthenticationSettings::JWT_PAYLOAD_PARAM_ISS: $payload[$parameter] = $jwtSettings->getAuthenticationSettings()->getAuthIss(); break; } } --- // src/Services/AuthenticateService.php //Generate payload $payload = isset($this->request['payload']) ? json_decode( stripslashes( $this->wordPressData->sanitizeTextField($this->request['payload']) ), true ) : []; $payload = self::generatePayload( $payload, $this->wordPressData, $this->jwtSettings, $user );
Security Fix
@@ -29,6 +29,19 @@ $jwtSettings, $user ) { + $reservedParameters = $jwtSettings->getAuthenticationSettings()->getJwtPayloadParameters(); + + // The claim used by /autologin to resolve the target user can be a custom + // name (jwt_login_by_parameter) that isn't in the fixed list above. + $jwtLoginByParameter = $jwtSettings->getLoginSettings()->getJwtLoginByParameter(); + if (!empty($jwtLoginByParameter)) { + $reservedParameters[] = $jwtLoginByParameter; + } + + foreach ($reservedParameters as $reservedParameter) { + unset($payload[$reservedParameter]); + } + $payload[AuthenticationSettings::JWT_PAYLOAD_PARAM_IAT] = time(); foreach ($jwtSettings->getAuthenticationSettings()->getJwtPayloadParameters() as $parameter) {
Exploit Outline
To exploit this vulnerability, an attacker with Subscriber-level credentials first authenticates to the site. They then make a POST request to the `/wp-json/simple-jwt-login/v1/auth` endpoint. In this request, they include a 'payload' parameter containing a JSON object with a key matching the site's 'JWT Login By' configuration (commonly 'email', 'id', or 'username') set to the value of a target Administrator. Because the plugin does not unset existing keys in the user-provided payload, it signs the JWT containing the attacker's spoofed identity claim. The attacker then takes this signed JWT and provides it to the `/autologin` endpoint (via URL, header, or cookie), which validates the signature and logs the attacker in as the target Administrator.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.