
Proof of Concept und Schwachstellenprüfer für CVE-2026-18963, eine nicht authentifizierte Kontoübernahme im Keycloak-Reset-Credentials-Ablauf. Einzelne Python-Datei, optionale Playwright-Browserkomponente.
Ein Proof of Concept und Schwachstellen-Checker für die Umgehung des Keycloak-Reset-Credentials-Flows, mit einer optionalen Browser-Komponente, die den resultierenden Zustand in einem echten Chrome-Fenster darstellt.
Das Tool besteht aus einer einzigen Datei, cve_2026_18963_poc.py, die gegen die Python-Standardbibliothek geschrieben wurde. Playwright ist die einzige Drittanbieter-Abhängigkeit und wird lazy importiert, sodass die HTTP-Seite auf einem Standard-Interpreter ohne installierte Abhängigkeiten läuft.
cve_2026_18963_poc.py 1009 lines, Python 3.10 or newer
Danksagung. CVE-2026-18963 wurde von James Paremain an Red Hat gemeldet, der im Advisory des Herstellers genannt wird. Orevim hat diese Schwachstelle nicht entdeckt oder gemeldet. Dieses Repository ist ein unabhängiger Proof of Concept, der auf Grundlage des veröffentlichten Advisories verfasst wurde.
CVE-2026-18963 ist eine Authentifizierungsumgehung im Passwort-vergessen-Ablauf von Keycloak. Es sind zwei Defekte, die zusammenwirken.
Die nicht abgegrenzte Notiz. Wenn ein Benutzer den Bildschirm zur Auswahl des Authentifikators öffnet,
schreibt Keycloak eine Notiz AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED in die
Authentifizierungssitzung. Die Notiz ist ein bloßer boolescher Wert. Sie hält fest, dass ein
Auswahlbildschirm angezeigt wurde, nicht welche Ausführung ihn angezeigt hat. Die Notiz
überlebt die Ausführung, die sie gesetzt hat.
Der ungeschützte Handler. ResetCredentialEmail.action() setzt emailVerified
auf dem aufgelösten Benutzer und ruft success() auf, ohne das Action-Token zu prüfen,
das der per E-Mail versendete Link eigentlich mitführen soll. Die Methode geht davon aus, dass sie nur
durch Aufrufen dieses Links erreicht werden kann.
Verkettet setzt ein nicht authentifizierter Aufrufer die Notiz gegen die Benutzernamen-
Ausführung, treibt den Ablauf voran, sodass die E-Mail-Ausführung aktuell wird, und
ruft dann die Reset-URL erneut auf. Die veraltete Notiz bewirkt, dass der Auswahlbildschirm
an die E-Mail-Ausführung gebunden gerendert wird. Das Senden eines leeren Bodys an die
loginAction dieser Ausführung ruft den ungeschützten Handler direkt auf. Der Ablauf schreitet zur
erforderlichen Aktion UPDATE_PASSWORD voran, ohne dass jemals eine E-Mail gelesen wurde.
| Kennung | CVE-2026-18963, GHSA-4gv3-mc9p-5wqc |
| Klasse | CWE-640, schwacher Mechanismus zur Passwortwiederherstellung für vergessene Passwörter |
| CVSS v3.1 | 9.1 AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N |
| Betroffen | >= 26.0.0, < 26.4.15, >= 26.5.0, < 26.6.6, >= 26.7.0, < 26.7.2 |
| Behoben in | 26.4.15, 26.6.6, 26.7.2. Die 26.8.0-Linie wird mit dem Fix ausgeliefert |
| Paket | org.keycloak:keycloak-services (Maven) |
| Veröffentlicht | 18. August 2026 |
| Gemeldet von | James Paremain, genannt im Red Hat Advisory |
Die einzige Voraussetzung ist, dass das Realm die Passwortwiederherstellung für nicht authentifizierte Benutzer anbietet. Es werden keine Anmeldedaten, Tokens oder vorherige Sitzung benötigt.
Acht Anfragen, in Reihenfolge. Die Tags entsprechen den Schrittbezeichnungen, die das Tool ausgibt, und den
Dateinamen, die es unter --out schreibt.
| # | Tag | Methode | Was sie bewirkt |
|---|---|---|---|
| 1 | login | GET | Autorisierungs-URL. Legt die Authentifizierungssitzung an und liefert den Reset-Link. |
| 2 | reset | GET | Seite zum Zurücksetzen der Anmeldedaten. Enthält das Benutzernamen-Formular. |
| 3 | tryanother | POST | tryAnotherWay=on. Schreibt die nicht abgegrenzte Notiz. |
| 4 | username | POST | Die Kennung. Treibt den Ablauf voran, sodass die E-Mail-Ausführung aktuell wird. Sendet Wiederherstellungs-E-Mail, wenn die Kennung aufgelöst wird. |
| 5 | reenter | GET | Erneut die Reset-URL. Die veraltete Notiz rendert den Selektor an die E-Mail-Ausführung gebunden. |
| 6 | action | POST | Leerer Body an die loginAction dieser Ausführung. Ruft den ungeschützten Handler auf. |
| 7 | required-action | GET | Folgt dem Location-Header zur UPDATE_PASSWORD-Seite. |
| 8 | setpassword | POST | Das neue Passwort. Wird nur mit --set-password gesendet. |
Schritt 6 ist der Exploit, und der leere Body ist tragend. Stattdessen
authenticationExecution=<id> zu senden ist die gewöhnliche Selektor-Interaktion: Sie
wechselt die Ausführung und sendet die Wiederherstellungs-E-Mail erneut, wodurch der
Defekt nie erreicht wird. Der leere POST beantwortet keine Challenge, sodass Keycloak den Handler für
diejenige Ausführung aufruft, die die veraltete Notiz als aktuell hinterlassen hat.
# Decide whether the instance is affected. Changes nothing, sends no mail.
./cve_2026_18963_poc.py --url https://sso.example.com --realm demo \
--client-id account-console --check
# Additionally report which identifiers resolve to a real account.
# Resolving accounts receive a recovery email. See the note below.
./cve_2026_18963_poc.py --url https://sso.example.com --realm demo \
--client-id account-console --check --users-file names.txt --rate 0.25
# Reach the set-password form for one account and stop there.
./cve_2026_18963_poc.py --url https://sso.example.com --realm demo \
--client-id account-console --username [email protected]
# Complete the takeover. This locks the account holder out.
./cve_2026_18963_poc.py --url https://sso.example.com --realm demo \
--client-id account-console --username [email protected] \
--set-password 'N3w-passw0rd!'
--check prüft mit einer zufälligen Kennung der Form
<16 hex>@<8 hex>.invalid. Die TLD .invalid ist durch RFC 2606 reserviert und
kann nicht aufgelöst werden, sodass die Prüfung weder mit einem echten Konto
kollidieren noch den Versand von E-Mails verursachen kann. Sie stellt den Status
des Builds fest, ohne jemanden zu berühren.
Das Hinzufügen von
--usersändert das. Schritt 4 ist die gewöhnliche Übermittlung des Forgot-Password-Formulars. In einer Realm mit konfiguriertem SMTP erhält eine Kennung, die zu einem Benutzer mit E-Mail-Adresse aufgelöst wird, eine echte Passwort-Wiederherstellungs-E-Mail – sowohl bei betroffenen als auch bei gepatchten Builds. Die reine Prüfung tut das nicht, weilResetCredentialEmail.authenticate()nichts sendet, wenn der Benutzer null ist. Behandle--check --usersals einen Ablauf, der echte Personen benachrichtigt, und bestätige, dass er von deiner Autorisierung abgedeckt ist, bevor du ihn ausführst.
Das Tool klassifiziert anhand der Antwort auf Schritt 6, den leeren POST.
| Antwort | Ergebnis | Bedeutung |
|---|---|---|
| 400 | account-absent | Der Handler lief ohne Action-Token und löste keinen Benutzer auf. Der Build ist betroffen. |
302 mit Location | form-reached | Der Handler lief und das Konto wird aufgelöst. Betroffen, und diese Kennung ist echt. |
| Alles andere | not-vulnerable | Der leere POST ist nicht weitergekommen. |
| Kein Selektor bei Schritt 5 | not-vulnerable | Der veraltete Hinweis wurde nicht berücksichtigt. |
| 302 nach Schritt 8 | password-set | Das Passwort wurde geändert und die Realm hat eine Sitzung ausgestellt. |
| 429 über das Retry-Budget hinaus | rate-limited | Kein Urteil. Der Lauf wurde beendet. |
| Ein negatives Ergebnis nach jeglicher Drosselung | inconclusive | Siehe unten. |
Die 400 ist die wichtige. Sie trennt der Build ist betroffen von dieses Konto
existiert, was es --check ermöglicht, die Exposition gegenüber einer Instanz
festzustellen, ohne einen einzigen echten Benutzer zu enumerieren. Das Auflösen
von Kennungen ist dann eine separate, optionale Frage.