CVE-2026-18963 Inside the Keycloak Reset-Credentials Bypass and Pre-Auth Account Takeover
A pre-authentication logic flaw in Keycloak 26.0.0–26.7.1 allows an attacker who knows only a victim’s username to change their password – without credentials, phishing, or any victim interaction. Fixed in Keycloak 26.7.2.
Keycloak is the IAM backbone of a significant fraction of enterprise infrastructure. Deployed at the center of SSO architectures, it federates access to SaaS applications, internal tooling, cloud workloads, and developer environments alike. When a single Keycloak realm signs tokens for dozens of downstream services, compromising one account in that realm is rarely contained to one service.
CVE-2026-18963 is exactly that kind of vulnerability. Two logic defects in Keycloak’s password-reset flow – introduced together in version 26.0.0 – allow an unauthenticated attacker who knows only a victim’s username or email address to permanently change their password. No credentials, no phishing, no interaction from the victim. The reset email arrives in the victim’s inbox unread; the password has already been changed.
The vulnerability affects every Keycloak deployment running 26.0.0 through 26.7.1. ‘Forgot Password’ is enabled by default in every realm, so most installations running these versions are vulnerable out of the box.
That shifts the enterprise question from:
“Can an attacker reach our password-reset page?”
to:
“Which of our realm accounts can be taken over with only a username – and what can those accounts access?”
Vulnerability at a Glance
| Field | Detail |
| CVE | CVE-2026-18963 |
| Affected component | Keycloak – org.keycloak:keycloak-services (DefaultAuthenticationFlow.java, ResetCredentialEmail.java) |
| CWE | CWE-640 – Weak Password Recovery Mechanism for Forgotten Password |
| Severity | CRITICAL |
| CVSS score | 9.1 |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N |
| Privileges required | None |
| User interaction | None |
| Primary prerequisite | “Forgot Password” enabled in realm (on by default) |
| Potential impact | Full account takeover; SSO lateral movement to all connected applications |
| Affected versions | Keycloak 26.0.0 – 26.7.1 |
| Exploitation status | Public PoC available; active exploitation reported in the wild |
| Fixed version | Keycloak 26.7.2 (community) / 26.6.6 / 26.4.15 (RHBK) |
What Actually Breaks
The vulnerability lives in the reset-credentials authentication flow and involves two independent code defects that only become critical in combination.
Defect 1 – DefaultAuthenticationFlow.java (introduced Keycloak 26.0.0)
When a user clicks “Try Another Way” during the reset-credentials flow, the code stores a session note to remember the credential-selector screen was shown. Prior to Keycloak 26.0.0, this note stored the execution model ID – a scoped, per-execution identifier. In commit 6a9e60b, it was changed to store the plain string “true”:
// VULNERABLE (Keycloak 26.0.0 – 26.7.1)
// Stores a boolean string - not execution-scoped; persists across flow restarts
authSession.setAuthNote(
AuthenticationProcessor.AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED,
"true"
);
// FIXED (Keycloak 26.7.2, PR #51844)
// Stores the execution ID – note is now execution-scoped
authSession.setAuthNote(
AuthenticationProcessor.AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED,
model.getId()
);
Because the note is no longer tied to a specific execution, it persists across flow restarts. When the auth flow is restarted and the same session re-enters the reset-credentials flow, that stale “true” note causes the code to skip the username-entry screen and jump directly to the credential-selector form.
Defect 2 – ResetCredentialEmail.java
The action() method in ResetCredentialEmail.java is supposed to verify that the user has actually clicked the password-reset email link before advancing the flow. It does this by checking whether ACTION_TOKEN_USER_ID – a session note set only when a valid action-token link is clicked – matches the session’s current user. The defect: this check was missing. action() called context.success() unconditionally. Reaching the method was sufficient to succeed, with or without the email link being clicked.
Combined, the two defects allow an attacker to navigate the reset flow without triggering the email-link checkpoint:
Expected Reset Flow:
GET /reset-credentials -> Username form -> POST username
-> Email sent -> User clicks link -> action-token verified -> UPDATE_PASSWORD form
Vulnerable Flow (CVE-2026-18963):
Step 1 POST tryAnotherWay=on
=> AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED = “true”
(plain bool, NOT execution-scoped) [Defect 1]
Step 2 POST username=victim
=> Reset email sent to victim (never clicked)
=> restart_path returned in response
Step 3 GET restart_path
=> Stale “true” note PERSISTS across flow restart
Step 4 GET /reset-credentials again
=> Stale note -> selector appears (username step SKIPPED)
Step 5 POST username=victim to stale selector
=> action() called -> context.success() (no token check) [Defect 2]
=> UPDATE_PASSWORD form served WITHOUT action-token
Step 6 POST new password -> ACCOUNT TAKEN OVER
Impact: From a Stolen Username to Full SSO Access
Account takeover via CVE-2026-18963 is fast, fully automated, and leaves no trace in the expected event sequence. Impact extends well beyond the Keycloak account itself.
Account Takeover
The attacker sets a new password on the victim’s account. The original password stops working immediately. The victim receives no warning – the reset email contains a link that was never clicked, and most users will not recognize it as evidence of compromise. Account access is fully transferred to the attacker in under a minute, with no network prerequisites beyond reaching the Keycloak endpoint.

Figure 1. Terminal output of the automated exploit (exploit.py) against a Keycloak 26.7.1 lab instance. Steps 1–8 complete in under two seconds; the victim’s original password is rejected immediately after.
Lateral Movement via SSO
Keycloak’s value to an attacker scales with how many services the compromised realm federates. A typical enterprise realm issues tokens for dozens of downstream applications – SaaS platforms, internal developer tools, cloud management consoles, CI/CD systems – all of which accept Keycloak-issued JWTs without additional credential checks. One taken-over account becomes access to everything that account’s SSO scope covers.
Compromised Keycloak account
|
|-- SaaS applications (Jira, Confluence, Slack)
|-- Internal developer tooling (GitLab, Jenkins, Vault)
|-- Cloud management portals (AWS SSO, GCP Workforce Identity)
|-- Monitoring & observability (Grafana, Datadog SSO)
+-- Any service trusting this realm's JWTs
Privilege Escalation
The exploit requires only a username – not an administrative one. An attacker targeting a realm admin account gains the ability to reconfigure the realm itself: add identity providers, disable MFA policies, create backdoor accounts, or grant roles to attacker-controlled users. In environments where Keycloak’s master realm administers other realms, the blast radius expands to the entire IAM infrastructure.
Why This Matters Beyond the Application Layer
Pre-authentication account takeover against an IAM platform is qualitatively different from most application vulnerabilities. Every downstream application that trusts the compromised realm’s tokens inherits the exposure – without a vulnerability of its own. The attack surface is the sum of every integrated service, and exploitability is bounded only by which usernames an attacker can enumerate.
Username enumeration (OSINT / email guess / public Keycloak admin API)
|
v
CVE-2026-18963 exploit (unauthenticated, ~1 second per account)
|
v
Victim password changed -> Attacker authenticates as victim
|
v
JWT issued by Keycloak -> Accepted by all downstream services
|
|-- Data exfiltration from connected applications
|-- Code execution (CI/CD pipeline access)
|-- Supply-chain position (artifact signing, deployment keys)
+-- Persistence (backdoor realm admin, disabled MFA)
Attack Scenarios
Scenario 1 – External attacker → Internet-facing Keycloak → Enterprise SSO
The most common scenario. An organization exposes Keycloak on a public endpoint for external users or remote employees. An attacker enumerates usernames via OSINT (LinkedIn, breach data, email-format guessing) and runs the exploit against each – requiring only HTTP access to the Keycloak URL and the target username.
| Step | Attacker → Keycloak | Keycloak behavior / response | Victim inbox |
| 1 | GET /reset-credentials | Returns username form; sets AUTH_SESSION_ID + KC_RESTART cookies | – |
| 2 | POST tryAnotherWay=on | Stores AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED = “true” [Defect 1] | – |
| 3 | POST username=victim | Sends password-reset email to victim@domain | Email arrives (link never clicked) |
| 4 | GET restart_path | Auth flow restarted – stale boolean note persists in session | – |
| 5 | GET /reset-credentials (2nd time) | Stale note → selector shown directly; username entry step skipped | – |
| 6 | POST username=victim (to stale selector) |
UPDATE_PASSWORD form served without action-token check [Defect 2] | – |
| 7 | POST password-new=Pwned! password-confirm=Pwned! |
Password changed – 302 redirect to account page | – |
| 8 | POST /token (new password) | JWT issued – account takeover confirmed | Account under attacker control |
Scenario 2 – Internal attacker → Internal Keycloak → Privilege escalation
An attacker with any level of internal network access – a compromised workstation, a foothold via unrelated vulnerability, or a malicious insider – can use CVE-2026-18963 to take over a realm administrator account. From there, they can reconfigure the entire IAM infrastructure: disable MFA policies, add identity providers, create persistent backdoor accounts, and grant elevated roles to attacker-controlled identities.
| Step | Action | Effect on IAM infrastructure |
| 1 | Enumerate realm admin username (OSINT, breach data, Keycloak admin API) |
Target identified – only the username is required, no password |
| 2 | Run CVE-2026-18963 exploit against internal Keycloak URL |
Realm admin password changed; attacker authenticates as realm admin |
| 3 | Add attacker-controlled LDAP / identity provider to realm |
Persistent backdoor identity source – survives eviction of the original admin account |
| 4 | Create hidden realm-admin user with attacker-controlled credentials |
Second admin account; resists remediation even if original takeover is detected |
| 5 | Disable MFA enforcement on all realm users |
Downgrades security posture across all realm accounts simultaneously |
| 6 | Assign admin roles to attacker-controlled service accounts |
Elevated access granted to all downstream SSO-integrated services |

Figure 2. ProjectDiscovery Nuclei scan result using the official CVE-2026-18963 template (author: DhiyaneshDk). The template is a multi-step active exploit chain – not a version fingerprint – and confirms reachability of kc-passwd-update-form without an action token.

Figure 3. Mailpit inbox after the exploit completes. Keycloak sent exactly one password-reset email (Step 4) to the victim’s address. The attacker never opened or clicked the link – the account takeover was complete before the victim had any opportunity to act.
Affected Products and Fixed Versions
| Product / Component | Affected Version | Fixed Version |
| Keycloak (community) – keycloak-services | 26.0.0 – 26.7.1 | 26.7.2 |
| Red Hat Build of Keycloak (RHBK) – 26.x stream | 26.0.0 – 26.6.5 | 26.6.6 |
| Red Hat Build of Keycloak (RHBK) – 24.x stream | 24.0.0 – 26.4.14 | 26.4.15 |
| Keycloak <= 25.x (all prior versions) | Not affected | Bug introduced in 26.0.0 (commit 6a9e60b) |
Fix details: PR #51844 (commit dc2d4e5, rmartinc) addresses both defects. DefaultAuthenticationFlow.java now stores model.getId() and validates the execution ID on re-entry. ResetCredentialEmail.java now retrieves ACTION_TOKEN_USER_ID and throws AuthenticationFlowException(INVALID_USER) if it does not match the session user.
How Enterprises Should Respond
- Patch first.
Upgrade to Keycloak 26.7.2 (community) or 26.6.6 / 26.4.15 (RHBK). This is the only complete fix. Treat this as a P0 – the attack is unauthenticated, fast, fully scriptable, and public PoC is available.
- Apply interim mitigation if patching is delayed.
Disable “Forgot Password” in every realm: Realm Settings → Login → Forgot password → OFF. This eliminates the attack surface at the cost of the password-reset feature. Do not treat this as a permanent fix – it is a stopgap only.
- Hunt for exploitation.
Query Keycloak’s audit event log for UPDATE_PASSWORD events not preceded by a GET /login-actions/action-token request from the same IP. The exploit leaves a detectable gap: SEND_RESET_PASSWORD → UPDATE_PASSWORD with no intervening action-token GET. Check the event_entity table using the details_json_long_value column (not details_json, which is NULL in Keycloak 26.x). The kyos-public forensics script automates this query (github.com/kyos-public/keycloak-cve-2026-18963-hunt).
- Force password resets for accounts that may have been compromised.
If exploitation evidence is found – or cannot be ruled out for the exposure period – require affected users to reset via an out-of-band channel. Invalidate existing sessions.
- Reduce privilege of high-value Keycloak accounts.
Audit realm-admin assignments and apply least-privilege. Realm-admin accounts are the highest-value targets. Consider requiring hardware MFA for realm-admin accounts even after patching.
- Monitor for the detection signal going forward.
Add the missing-action-token anomaly to your SIEM: SEND_RESET_PASSWORD → UPDATE_PASSWORD without an intervening action-token GET is abnormal in any patched instance. Keep this signal in your detection library.
Closing: Username Is Not a Credential – Until It Is
CVE-2026-18963 is a reminder that authentication flow logic is hard to get right, and that even small changes – swapping an execution-scoped ID for a plain boolean – can introduce critical vulnerabilities in a widely deployed security product.
The vulnerability is unauthenticated, requires no network positioning, and has no interaction requirement. Public PoC code is available and exploitation has been observed in the wild. The combination of a 9.1 CVSS score, a trivial attack chain, and Keycloak’s central role in enterprise SSO architectures makes this one of the more consequential IAM vulnerabilities in recent memory.
Patch clients to Keycloak 26.7.2 or later, disable unnecessary Forgot Password flows while upgrading, and add the missing-action-token signal to your detection stack. The prioritization formula is simple:
Exposed reset-credentials flow
x Keycloak 26.0 – 26.7.1
x realm account privilege
= upgrade urgency