Moodle 5.2.3 - Élevé - À distance - SQLi - enrol_database concatène l'idnumber du cours dans le SQL
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · moodle-enrol-database-sqli
Moodle LMS 5.2.3 - Moodle
Classe : SQLi Portée : Distante
enrol_database::db_get_sql échappe toujours avec des slashs et concatène. MSA-26-0005 / MDL-88138 a paramétré auth_db sous la forme Execute($sql, [$value]). Ce plugin n'a pas copié cette forme. Valeurs par défaut dans les paramètres : dbsybasequoting=0, localcoursefield=idnumber. Le point d'aboutissement est la base de données d'inscription externe. Le $DB du cœur de Moodle n'est pas sur ce chemin.
Un enseignant éditeur sur un site où enrol_database est activé définit le idnumber du cours. Cron l'interpole. Des lignes supplémentaires reviennent depuis la base d'inscription.
| ID | pas encore de CVE |
| CWE | CWE-89 |
| CVSS | Élevé : 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| Produit | Moodle |
| Affecté | tag 5.2.3 v5.2.3 (344232c15336c71b80f9aca8359ce0e0a9f3d116). Le plugin enrol_database doit être activé. |
| Auth | enseignant éditeur (idnumber du cours, mappage local par défaut) |
| Licence | GNU Affero GPL v3.0 |
| Labo | 127.0.0.1 uniquement |
Un enseignant éditeur sur un site où enrol_database est activé injecte du SQL sur la base de données d'inscription externe en définissant le idnumber du cours. Le cron sync_enrolments exécute alors ce SQL.
| Privilège | Méthode / point d'aboutissement | Reconnaissance / intégrité / disponibilité | Prouvé en labo ou déduit |
|---|---|---|---|
| Injecter un fragment SQL dans le SELECT d'inscription externe | Cron sync_enrolments -> db_get_sql($table, array($coursefield=>$course->mapping), ...) -> $extdb->Execute($sql) | Intégrité de la chaîne de requête | Prouvé en labo. Le SQL concaténé est WHERE courseid = 'LABCOURSE\'' OR 1=1 -- ' |
| Lire les lignes d'inscription d'autres cours sur cette base externe | Même SELECT après fermeture de la chaîne | Reconnaissance | Prouvé en labo. Propre : 1 ligne teacher-local. Injection : 2 lignes dont MOODLE-ENROL-DATABASE-SQLI-WITNESS |
| Ignorer la requête quand le plugin est désactivé | Même gadget, plugin=off | n/a | Prouvé en labo fermé. executed=false, SQL vide, 0 ligne |
| Conserver la même charge utile comme paramètre lié | Reliquat d'auth_db de MSA-26-0005 : Execute($sql, [$value]) | n/a | Prouvé en labo fermé. style=param a renvoyé 0 ligne, le SQL contient ? |
| Inscrire / désinscrire / attribuer un rôle aux utilisateurs Moodle nommés dans les lignes supplémentaires | Après que le cron applique requestedenrols | Intégrité des inscriptions | Déduit. Le labo s'arrête aux lignes supplémentaires sur la base d'inscription |
| FILE / INTO OUTFILE en tant qu'utilisateur de la base d'inscription | Même point d'aboutissement si ce compte dispose de FILE | Intégrité / disponibilité de cet hôte de base de données | Déduit. Non démontré. Pas d'élévation de privilèges OS |
| Chemin contrôlé par l'étudiant | Uniquement si l'admin mappe localuserfield=email (ou similaire) | n/a | Déduit. Le chemin par défaut est le idnumber de l'enseignant |
Fermé sur ce reliquat : le $DB du cœur de Moodle ; plugin désactivé (aucune requête) ; accès non authentifié ; shell OS ; requêtes empilées (ce gadget mysqli est à instruction unique). Un Moodle par défaut sans enrol_database n'est pas ce bug.
Mêmes reliquats de version : lecture de fichier img Blackboard, IDOR sur les retours de notes, éléments de note masqués.
J'ai épinglé moodle/moodle v5.2.3 après MSA-26-0042/0043. MSA-26-0005 a fermé auth_db avec un placeholder. J'ai ouvert enrol_database parce qu'il parle au même type de base externe et possède encore db_addslashes.
dbsybasequoting vaut 0 par défaut, donc seul ' est doublé. Un antislash suivi d'un guillemet dans le idnumber ferme la chaîne MySQL.
Fausses pistes déjà consignées : traiter le $DB du cœur de Moodle comme point d'aboutissement (ce n'est pas le cas) ; qualifier cela d'élévation de privilèges OS parce que FILE existe comme privilège MySQL (non démontré, pas ce reliquat) ; l'email de l'étudiant comme mappage par défaut (l'admin doit modifier localuserfield). Un reverse shell. Du théâtre.
Gadget fidèle des constructeurs de la version épinglée, pas une application PHP Moodle complète. php:8.3-cli sert les copies de db_get_sql / db_addslashes / db_encode. mysql:8.0 contient deux lignes d'inscription.
python3 moodle-enrol-database-sqli-Abraxas-Labs.py
ou
cd lab
./run.sh
Cible uniquement http://127.0.0.1:19570. Projet Compose moodle-enrol-database-sqli. La stack est démontée après l'exécution.
SUCCESS moodle-enrol-database-sqli how=gadget plugin-off=no-query clean-rows=1 inject-rows=2 sql-concat=yes param-rows=0 extra=yes sink=enrol-db MOODLE-ENROL-DATABASE-SQLI-WITNESS
Copier les requêtes paramétrées d'auth_db (MDL-88138). Passer la valeur mappée du cours / de l'utilisateur sous la forme Execute($sql, [$value]). Ne pas concaténer db_addslashes dans '$value'. Ajouter un test vérifiant qu'un guillemet dans le idnumber du cours ne renvoie pas de lignes d'autres cours.
lib.php (db_get_sql, db_addslashes, sync_enrolments)auth_dbGNU Affero GPL v3.0. Voir LICENSE. Labo en boucle locale uniquement. Aucune garantie.