← Back to Feed CACHED · 2026-10-08 01:07:57 · CACHE_KEY CVE-2026-18963
CVE-2026-18963 · CWE-640 · Disclosed 2026-08-18

Keycloak Reset-Credentials Flow Authentication Bypass (Unauthenticated Account Takeover)

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Like discovering the bank's PIN-reset kiosk lets anyone walk up and change the vault combination without showing ID

CVE-2026-18963 is a logic flaw in the keycloak-services component — the core engine of every Keycloak and Red Hat Build of Keycloak (RHBK) deployment. The reset-credentials flow, triggered by the "Forgot password" link, is supposed to send an email with a signed action token, and only let the requester set a new password after proving they control the mailbox. Two chained defects break that contract: a boolean flag (AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED) persists across the authentication session without being scoped to the execution that set it, and ResetCredentialEmail.action() reaches a success state without validating the action token. Together, an unauthenticated attacker who knows (or can guess) a target username or email can reset any account's password — including the Keycloak admin — in six HTTP requests, with no email access, no user interaction, and zero dependencies. Affected versions span Keycloak 26.0.0 – 26.4.14, 26.5.0 – 26.6.5, and 26.7.0 – 26.7.1. Fixes landed in 26.4.15, 26.6.6, 26.7.2, and 26.8.0.

Red Hat's CRITICAL 9.1 rating is dead-on. Every friction factor in this chain works in the attacker's favor: no authentication, no user interaction, low complexity, default-enabled feature, and the target component is the identity provider itself — not something that *runs alongside* an IdP, but the IdP. The CVSS vector (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) accurately captures the access profile. The only reason the score isn't 10.0 is the absence of an availability impact (A:N) and unchanged scope (S:U). Multiple working Python PoCs appeared within days of disclosure, and the exploit is trivially reproducible. If anything, a vendor score of 9.1 is *generous to the attacker* — the real-world blast radius of an IdP admin takeover can cascade far beyond what CVSS models.

"Unauthenticated account takeover of any Keycloak user, including admins, in six HTTP requests."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Target the Keycloak login page

The attacker identifies an internet-facing Keycloak instance via Shodan, Censys, or simple Google dorking (inurl:/realms/ inurl:/protocol/openid-connect). Keycloak login pages are designed to be publicly accessible — they are the front door for all federated authentication. The attacker identifies a target username or email address through OSINT, directory harvesting, or the application itself.
Conditions required:
  • Network access to the Keycloak login page
  • Knowledge of a valid username or email address
Where this breaks in practice:
  • None — Keycloak is internet-facing by design and usernames/emails are often enumerable or publicly known (corporate email = username)
Detection/coverage: Shodan query http.title:"Sign in" http.html:keycloak or Censys equivalent identifies exposed instances. GreyNoise may tag scanning activity targeting /realms/*/login-actions/reset-credentials.
STEP 02

Initiate the reset-credentials flow

The attacker navigates to the forgot-password endpoint (/realms/{realm}/login-actions/reset-credentials) and submits the target's username or email. This triggers the normal first stage: Keycloak creates an authentication session and sets the AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED flag. In a legitimate flow, the server would send an email with a signed action token and wait.
Conditions required:
  • Forgot-password feature enabled (the default)
Where this breaks in practice:
  • Only blocked if an administrator has explicitly disabled forgot-password in every realm — this breaks a core user-facing feature and most deployments leave it on
Detection/coverage: Keycloak event log records a SEND_RESET_PASSWORD event, but this is identical to a legitimate reset request.
STEP 03

Re-enter the flow to exploit the sticky flag

The attacker re-enters the authentication flow using the same session. Because AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED persists without being scoped to the execution that created it, the flow evaluator incorrectly evaluates the state as having already completed the selector screen, allowing the attacker to skip forward. The attacker posts a crafted form submission that advances the session past the email-verification gate.
Conditions required:
  • Active authentication session from Step 2
Where this breaks in practice:
  • None — requires only standard HTTP requests; implemented in the Snizi, T0w0T, and atiilla PoC tools
Detection/coverage: No distinct log signature — the session manipulation happens within normal HTTP request patterns.
STEP 04

Bypass action-token validation

The ResetCredentialEmail.action() method executes and reaches a success state without validating that the inbound request carries the signed action token that would prove mailbox control. The code also incorrectly marks the user's email as verified — a side effect that becomes the strongest forensic indicator. The attacker is now transitioned to the UPDATE_PASSWORD form.
Conditions required:
  • Successful state manipulation from Step 3
Where this breaks in practice:
  • None — this is the core logic bug; no runtime or configuration setting prevents it on vulnerable versions
Detection/coverage: Key forensic indicator: the exploit causes the email-verified flag to be set on the account without a corresponding email-verification event. The kyos-public/keycloak-cve-2026-18963-hunt PostgreSQL script detects this.
STEP 05

Set new password and take over the account

The attacker submits a new password to the UPDATE_PASSWORD action, completing the takeover. They can now log in as the victim — including as realm admin. From an admin account, the attacker controls all realms, all users, all client configurations, all federated applications, and can issue arbitrary tokens. This is full identity-plane compromise.
Conditions required:
  • Successful bypass from Step 4
Where this breaks in practice:
  • MFA/2FA on the account would block *login* after the password change, but the password is still reset (DoS to legitimate user). Passkeys/passwordless eliminates the password vector entirely.
Detection/coverage: Keycloak logs RESET_PASSWORD and UPDATE_CREDENTIAL events, but these are indistinguishable from legitimate resets without cross-referencing email provider delivery logs or the database forensics script.
03 · Compensating Control

1
CRITICAL 9.1→IGNORE 0.0
SEVERITY REDUCED
Disable the Forgot Password feature in every Keycloak realm immediately — Navigate to each realm's Realm Settings → Login → Forgot password → Off, or automate via the Keycloak Admin REST API (PUT /admin/realms/{realm} with resetPasswordAllowed: false). This completely eliminates the vulnerable code path — the reset-credentials flow cannot be initiated. Deploy within 3 days per the noisgate mitigation SLA for CRITICAL findings. Note: this disables self-service password recovery for all users. Route password reset requests through admin-initiated resets until the patch is applied.
2
CRITICAL 9.1→MEDIUM 5.0
SEVERITY REDUCED
Enforce multi-factor authentication on all accounts across every realm — Configure a required MFA action (TOTP, WebAuthn) for all users and verify it is enforced at login. An attacker who exploits the reset flow can change the password but cannot log in without the second factor, blocking the confidentiality impact. However, the password is still changed — the legitimate user is locked out until admin intervention (partial DoS). Deploy within 3 days. This is a defense-in-depth layer, not a substitute for patching or disabling forgot-password.
3
CRITICAL 9.1→IGNORE 0.0
SEVERITY REDUCED
Upgrade to Keycloak 26.4.15, 26.6.6, 26.7.2, or 26.8.0+ — The definitive fix. The patch binds the email-verification step to the authentication session and requires a valid action token before allowing credential update. For RHBK, apply advisories RHSA-2026:56519/56520/56523/56524. Apply within 90 days per the noisgate remediation SLA for CRITICAL findings. Combine with the forgot-password disable above as immediate mitigation during the upgrade window.
4
CRITICAL 9.1→CRITICAL 9.1
Run the kyos-public forensic hunt script against your Keycloak database — Execute the keycloak-cve-2026-18963-hunt read-only PostgreSQL queries to detect whether you were exploited during the exposure window (2026-08-17 to your patch date). The strongest indicator is accounts with the email-verified flag set without a corresponding email-verification event — a condition only the exploit produces. Run immediately. If exploitation is confirmed, initiate incident response: reset affected accounts, revoke all sessions and offline tokens, rotate downstream API keys, and investigate federated applications.
5
CRITICAL 9.1→HIGH 7.5
SEVERITY REDUCED
Restrict Keycloak admin console to management network only — Place the /admin/ and /realms/master/ paths behind a reverse proxy with IP allowlisting or VPN. This does not block the exploit itself (which targets the user-facing login page), but limits the blast radius by preventing the attacker from using a compromised admin account remotely. Deploy within 3 days.
What doesn't work
  • WAF generic rules — the exploit uses standard HTTP form POST requests identical to legitimate password resets; there are no payload signatures, SQL injection patterns, or anomalous encodings for a WAF to match without custom rules that would also block legitimate resets
  • Rate limiting — the complete exploit chain requires only six HTTP requests, which falls far below any practical rate-limit threshold that wouldn't also block legitimate users
  • CAPTCHA on the login page — the attack targets the reset-credentials flow, not the login form; unless a CAPTCHA is specifically wired into the reset flow (non-default Keycloak configuration), login-page CAPTCHA provides zero protection
  • Session timeout reduction — the six-request chain completes in under two seconds; no realistic session timeout value would interrupt it
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo confirmed exploitation as of 2026-10-08. Not listed in CISA KEV. No campaigns attributed. However, the trivial exploit chain and multiple public PoCs make exploitation a matter of *when*, not *if*, for exposed unpatched instances.
Proof-of-Concept AvailabilityMultiple public PoCs. Snizi/CVE-2026-18963-Exploit — Python, zero dependencies, includes --safe-check mode and Docker lab. T0w0T/POC-CVE-2026-18963 — Python 3.8+, uses requests+beautifulsoup4. atiilla/CVE-2026-18963-Exploit and kc-resetforge tool also documented. BlackHatExploitation/Exploit-For-CVE-2026-18963 — additional repo. Forensic detection via kyos-public/keycloak-cve-2026-18963-hunt.
EPSS3.177% probability of exploitation in the next 30 days — 87th percentile. This places it well above the median and in the top tier of actionable vulnerabilities.
CISA KEV StatusNot listed as of 2026-10-08. Given the public PoCs and trivial chain, KEV listing is plausible if ITW exploitation is confirmed.
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N — Network-accessible, low complexity, no privileges, no user interaction. High confidentiality and integrity impact. No availability impact (accounts are taken over, not destroyed). This is the worst-case access profile for an authentication bypass.
Affected VersionsKeycloak 26.0.0 – 26.4.14, 26.5.0 – 26.6.5, 26.7.0 – 26.7.1. RHBK container images before 26.4-23 and 26.6-12. RHBK operator bundles before 26.4.15-1 and 26.6.6-1.
Fixed VersionsUpstream: 26.4.15, 26.6.6, 26.7.2, 26.8.0+. RHBK: advisories RHSA-2026:56519, RHSA-2026:56520, RHSA-2026:56523, RHSA-2026:56524.
Internet ExposureKeycloak is an identity provider designed to be internet-facing. Shodan query http.title:"Sign in" http.html:keycloak returns significant exposure. Censys scanning identifies instances across major cloud providers. The login page (and forgot-password flow) is accessible to any network client by design.
Disclosure TimelineDisclosed 2026-08-18. Upstream patch (26.7.2) released 2026-08-19. RHBK advisories published 2026-08-24. Researcher: James Paremain (credited by Red Hat). Bugzilla: 2511595.
Related CVEsKeycloak 26.7.2 also fixed CVE-2026-15571 (predictable account-linking hash enabling OIDC-based account takeover). Twelve additional CVEs were addressed in the 26.7.1 release window.

Sources.

  1. Red Hat CVE Page
  2. Keycloak GitHub Issue #51833
  3. SkyCloak — Affected Versions & Patches
  4. The Hacker News — Keycloak Critical Flaw
  5. Snizi PoC Exploit (GitHub)
  6. kyos-public Forensic Hunt Script (GitHub)
  7. Wiz Vulnerability Database
  8. Op-C Technical Analysis
05 · The Call

Final Verdict
= UNCHANGED to CRITICAL (9.1/10)

Why this verdict

  • Unauthenticated, zero-interaction, low-complexity remote attack. The CVSS vector is AV:N/AC:L/PR:N/UI:N — the worst possible access profile. An attacker needs nothing more than network access to the Keycloak login page and a target username or email address. No credentials, no phishing, no social engineering.
  • Trivial weaponization with public PoCs. At least five GitHub repositories implement the full chain in Python with zero or minimal dependencies. The Snizi exploit includes a Docker lab, safe-check mode, and completes in six HTTP requests. The exploitation barrier is effectively zero for any attacker who can reach the endpoint.
  • Forgot-password is enabled by default. The vulnerable code path is active in every stock Keycloak deployment. Disabling it breaks a core user-facing feature (self-service password recovery), so the vast majority of production instances have it enabled.
  • Internet-facing by design. Unlike internal-only services, Keycloak must be reachable by every user who authenticates — including remote workers, mobile users, and federated partners. Restricting access to an internal network defeats the product's purpose for most deployments. There is no architectural friction limiting reachability.
  • Role multiplier: Keycloak IS the identity provider. This is not a vulnerability in software that *happens* to run alongside an IdP — this IS the IdP. 100% of Keycloak installations serve the identity-provider role by definition. Compromising an admin account yields full control of the identity plane: all realms, all user accounts, all client configurations, all federated applications, all token issuance. Blast radius is identity-scale → fleet-scale → potentially supply-chain-scale depending on downstream integrations. Per the high-value role catalog (Identity & Trust), the floor is CRITICAL for canonical IdP software, and this chain succeeds trivially in that canonical role.

Why not higher?

The CVSS caps at 9.1 because there is no direct availability impact (A:N) and the scope is unchanged (S:U). A 10.0 would require scope change or availability destruction. The attacker takes over accounts but does not crash the Keycloak service itself. The 9.1 accurately reflects the ceiling for this class of vulnerability.

Why not lower?

Every friction factor works in the attacker's favor: unauthenticated remote access, no user interaction, low complexity (six HTTP requests), default-enabled feature, internet-facing component, and the affected software is canonically a high-value identity provider where 100% of installations occupy the critical role. Multiple working PoCs are public. EPSS is 87th percentile. There is no credible argument for any downgrade.

06 · Verification

Crowdsourced verification payload.

Run from any auditor workstation with curl installed. No authentication or special privileges required — the script makes outbound HTTPS requests only. Invoke as: bash check_cve_2026_18963.sh https://sso.example.com master where the first argument is the Keycloak base URL and the optional second argument is the realm name (defaults to master).

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# CVE-2026-18963 Verification — Keycloak Reset-Credentials Account Takeover
# Checks Keycloak version and forgot-password reachability.
# Usage: bash check_cve_2026_18963.sh <KEYCLOAK_BASE_URL> [REALM]
# Exit:  0 = PATCHED  |  1 = VULNERABLE  |  2 = UNKNOWN
# Needs: curl (outbound HTTPS only, no privileges)

set -uo pipefail

KC="${1:?Usage: $0 <KEYCLOAK_BASE_URL> [REALM]}"
REALM="${2:-master}"
KC="${KC%/}"

echo "[*] CVE-2026-18963 check against ${KC} (realm: ${REALM})"

# --- 1. Detect version ---
VER=""
# Method A: welcome / root page
BODY=$(curl -sk --max-time 10 "${KC}/" 2>/dev/null || true)
VER=$(echo "$BODY" | sed -n 's/.*[Kk]eycloak[[:space:]]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/p' | head -1)

# Method B: X-Keycloak-Version header on OIDC discovery
if [ -z "$VER" ]; then
  VER=$(curl -skI --max-time 10 \
    "${KC}/realms/${REALM}/.well-known/openid-configuration" 2>/dev/null \
    | tr -d '\r' \
    | sed -n 's/^[Xx]-[Kk]eycloak-[Vv]ersion:[[:space:]]*//p' | head -1)
fi

# --- 2. Check forgot-password reachability ---
RESET_CODE=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 10 \
  "${KC}/realms/${REALM}/login-actions/reset-credentials" 2>/dev/null || echo "000")
if [ "$RESET_CODE" -ge 200 ] && [ "$RESET_CODE" -lt 400 ]; then
  FORGOT="reachable"
else
  FORGOT="blocked_or_disabled (HTTP ${RESET_CODE})"
fi
echo "[i] Forgot-password endpoint: ${FORGOT}"

# --- 3. Evaluate version ---
if [ -z "$VER" ]; then
  echo "[!] Could not detect Keycloak version remotely."
  echo "[!] Verify on host: /opt/keycloak/bin/kc.sh show-config or container image tag."
  if [ "$FORGOT" = "reachable" ]; then
    echo "VULNERABLE (version unknown, reset endpoint reachable — treat as vulnerable)"
    exit 1
  fi
  echo "UNKNOWN"
  exit 2
fi

echo "[i] Detected version: ${VER}"
IFS='.' read -r MAJ MIN PAT <<< "$VER"

VULN=0
if [ "$MAJ" -eq 26 ]; then
  if [ "$MIN" -le 3 ]; then VULN=1
  elif [ "$MIN" -eq 4 ] && [ "$PAT" -lt 15 ]; then VULN=1
  elif [ "$MIN" -eq 5 ]; then VULN=1
  elif [ "$MIN" -eq 6 ] && [ "$PAT" -lt 6 ]; then VULN=1
  elif [ "$MIN" -eq 7 ] && [ "$PAT" -lt 2 ]; then VULN=1
  fi
fi

if [ "$VULN" -eq 1 ]; then
  echo "VULNERABLE (Keycloak ${VER}, forgot-password: ${FORGOT})"
  exit 1
else
  echo "PATCHED (Keycloak ${VER})"
  exit 0
fi