Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
keycloak-cve-2026-18963-hunt — Hunt for CVE-2026-18963 exploitation traces (Keycloak unauthenticated account takeover) in the Keycloak database | Kitploit
Tools/GitHubGitHub/kyos-public/keycloak-cve-2026-18963-hunt
Vulnerability AnalysisDigital ForensicsAuthenticationIncident ResponseDatabase SecurityLog Analysis
GitHubkyos-public/keycloak-cve-2026-18963-hunt

keycloak-cve-2026-18963-hunt

Hunt for CVE-2026-18963 exploitation traces (Keycloak unauthenticated account takeover) in the Keycloak database

View Repository
161291 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-18963: Keycloak account takeover, exploitation trace hunt

A psql script that searches a Keycloak PostgreSQL database for traces of CVE-2026-18963 exploitation (unauthenticated account takeover through the reset-credentials flow).

Published by KYOS. We wrote it while patching the Keycloak deployments we operate, to verify that nobody had been taken over during the exposure window.

The vulnerability

CVE-2026-18963 is a flaw in the reset-credentials flow of keycloak-services (keycloak#51833). An unauthenticated attacker can complete the password reset process for any user without clicking the mail verification link, then set a new password on the account. No user interaction is required.

What it means for the traces

Checked against Keycloak 26.7.1 on a disposable instance. We are not publishing reproduction steps; the details below are the ones you need to read the queries.

The flow tracks which authenticator screen it is on in a way that is not tied to the step that set it. That lets the reset-credentials flow be re-entered at the mail-verification step, where the vulnerable ResetCredentialEmail.action() returns success without ever checking for an action token. Execution then carries on to the update-password form.

Two consequences shape every query in this repo:

  • The mail step runs for real before the bypass. The victim receives a genuine password-reset email, and Keycloak writes an ordinary SEND_RESET_PASSWORD event. The absence of that event is not a signature.
  • The vulnerable action() marks the account's email address as verified before it returns. The legitimate path never reaches action() at all, so this is a state change that only the bypass produces. Q3 rests on it.

The fix (keycloak#51844) ties the screen state to the execution that set it, and only lets action() succeed when an action token for that exact user is present.

Severity: Critical, CVSS v3.1 9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N), per Red Hat and NVD. Root cause is improper state validation in the authentication flow, fixed in keycloak#51844.

Affected / fixed versions

StreamFixed in
26.7.x26.7.2 (release notes)
26.6.x26.6.6
26.4.x (LTS)26.4.15
26.826.8.0

Everything below those versions in the supported streams is vulnerable. Out-of-support releases (26.5.x, 26.3 and older) get no fix: assume exposure and upgrade to a fixed stream. Red Hat lists legacy RH-SSO 7 as not affected; the Red Hat Build of Keycloak 26.4/26.6 is fixed in 26.4.15-1 / 26.6.6-1.

Workaround if you cannot patch immediately

Disabling self-service password reset removes the vulnerable entry point: Admin console > Realm settings > Login > "Forgot password" off, for every realm. Via the admin API: PUT /admin/realms/{realm} with {"resetPasswordAllowed": false}.

This blocks the login-actions/reset-credentials endpoint but also prevents legitimate users from resetting their own password, so treat it as a stopgap until you upgrade. Note that Red Hat does not list an endorsed mitigation for this CVE; patching is the only real fix.

What does not work as a signature

The first version of this script flagged a completed reset that had no SEND_RESET_PASSWORD shortly before it.

That was wrong, and vilmosnagy pointed it out after reproducing the CVE on his own instance and watching the event appear anyway. The reason is the first consequence above: the mail authenticator runs normally, so the victim gets a real reset mail and Keycloak logs an ordinary SEND_RESET_PASSWORD. The check was inverted. It would have cleared every compromised account it looked at. It is gone.

Timing schemes have a smaller but real problem. The approach in thomasdelorge's hunt script correlates SEND_RESET_PASSWORD with UPDATE_PASSWORD on a shared code_id and flags a short gap. The correlation key is right, and the author is explicit that the threshold is a heuristic. Our testing says the same: a bypassed reset and a legitimate one completed in the same browser produce identical event types, identical detail keys and the same code_id. Only the delay differs, which leaves the threshold carrying the whole verdict. Anyone in no hurry stays under it. Real users trip it, because the code_id only stays the same when the link is opened in the browser that started the flow, which is also the case where the mail is likely already sitting open.

We found no event, no event detail and no error that separates the two. There is no "mail step skipped" event to look for. What does exist is a state side effect, and that is what Q3 uses.

What the script checks

cve-2026-18963-keycloak-hunt.sql runs six read-only queries:

QueryWhat it finds
Q0Whether the realm stores events at all, their TTL, and whether event details are being persisted. Q2 correlates on the code_id detail, so if details are missing Q2 is blind and its silence means nothing.
Q1Every password credential set inside the exposure window (credential.created_date). This is the takeover itself, and it survives event expiry.
Q2Every completed credential update, correlated with the reset mail of the same authentication session, and classified. See below.
Q3The deterministic side effect: users who are email_verified with no legitimate cause.
Q4Admin-API credential resets and user writes, to explain Q1 and Q3 rows and rule out the admin-driven route.
Q5Raw reset/credential event timeline, for reading a single case by hand.

Q2 verdicts

VerdictMeaning
NO_MAIL_WAS_SENTThe credential update shares a code_id with a failed send: no address on the account, or SMTP refused it. No link ever left the server, so nobody could have opened one, yet the flow still reached the password form. Legitimately impossible; treat as exploitation.
CLEARED_TOKEN_CLICKEDThe reset completed under a different code_id than any SEND_RESET_PASSWORD for that user. Only an action token consumed in another browser or session produces that, so the mail link really was clicked. Deterministically legitimate.
CANDIDATESame code_id as the send. This is what the bypass looks like, and also what a legitimate same-browser reset looks like. Not a finding on its own.
NOT_RESET_FLOWNo SEND_RESET_PASSWORD for that user in the window; the password changed some other way (account console, admin API, enrolment). Cross-check Q4.

secs_after_send is printed for context. Do not use it as the verdict.

Q3, the one deterministic in-database trace

The vulnerable ResetCredentialEmail.action() marks the account's email address as verified before it succeeds. The legitimate path never enters action() at all, because authenticate() short-circuits on the action token, so a genuine reset leaves email_verified alone. We checked this on 26.7.1 for legitimate resets completed both in the browser that started them and in a different one. Both left an unverified user unverified. Both bypassed resets flipped the flag.

So an account whose password changed inside the window, which is now email_verified, with no VERIFY_EMAIL/UPDATE_EMAIL event and no admin write against it, has no innocent explanation for being verified.

Download Tool