
MySQL Community Server 26.7.0 - Élevé - SET ROLE ALL EXCEPT FILE résiduel - écriture UID mysqld
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · mysql-set-role-leftover
Article : https://abraxaslabs.tech/research/mysql-set-role-leftover
Classe : Privilège résiduel (FILE avec l'UID de mysqld ; pas une élévation de privilèges locale) Portée : Distante
MySQL Community Server mysqld 26.7.0 (06a5c1c) - Oracle
SET ROLE ALL EXCEPT rfile (ou SET ROLE DEFAULT sans rôle par défaut) vide la liste des rôles actifs, puis checkout_access_maps() retourne sans appeler set_master_access(user->access). CURRENT_ROLE() vaut NONE. Les bits globaux statiques agrégés par le rôle (FILE, PROCESS, SUPER, ...) restent dans m_master_access. check_access lit ce résidu. En laboratoire : le FILE résiduel écrit via INTO OUTFILE avec l'UID de mysqld.
SET ROLE NONE est le chemin qui restaure ACL_USER::access. Les privilèges dynamiques (SYSTEM_USER) sont retirés. Le résidu au niveau base de données est déjà corrigé (Bug#35386565). Les comptes par défaut n'ont aucun rôle.
| ID | pas encore de CVE |
| Classe | Privilège résiduel (FILE avec l'UID de mysqld ; pas une élévation de privilèges locale) |
| Portée | Distante (SQL authentifié) |
| CWE | CWE-269 |
| CVSS | Élevé : 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| Produit | MySQL Community Server mysqld |
| Affecté | 26.7.0 (06a5c1c99c377fc41b2eba1ea244e8b220bdc3c8) |
| Authentification | authentifié ; la victime s'est vu accorder un rôle portant des ACL globales statiques (FILE en laboratoire) |
| Licence | GNU Affero GPL v3.0 |
| Laboratoire | 127.0.0.1 uniquement. Fichier témoin. Écriture FILE résiduelle. |
Disposer d'un compte auquel a été accordé un rôle portant FILE (ou un autre bit global statique). Activer ce rôle avec SET ROLE, puis le désactiver avec SET ROLE ALL EXCEPT. CURRENT_ROLE() renvoie NONE. SELECT 'witness' INTO OUTFILE écrit toujours avec l'UID de mysqld sous secure_file_priv.
Une application qui isole les privilèges avec SET ROLE ALL EXCEPT dangerous_role ou SET ROLE DEFAULT puis se fie à CURRENT_ROLE() conserve encore ces bits statiques. SET ROLE NONE les retire réellement.
Le résidu n'accorde pas FILE à un compte qui ne l'a jamais détenu via un rôle. Il échoue à retirer FILE après la désactivation.
Même chasse sur la 26.7.0 que pour les trois résidus côté client. Le démon par défaut non publié en Crit/High était vide. ACL / rôles conservaient encore ce retour anticipé sur rôle vide.
void Security_context::checkout_access_maps(void) {
if (m_acl_map != nullptr) {
get_global_acl_cache()->return_acl_map(m_acl_map);
m_acl_map = nullptr;
}
if (m_active_roles.size() == 0) return;
// ...
}
activate_role_all (TOUS SAUF) et un appel vide à activate_role_default effectuent ce checkout. activate_role_none exécute ensuite set_master_access(user->access).
Mauvaise piste déjà consignée : le premier SELECT note FROM lab.t INTO OUTFILE résiduel a renvoyé 1142 (cache db_acl() à 0). Le SELECT 'witness' INTO OUTFILE constant constitue l'oracle FILE.
cd lab
./run.sh
Image mysql:26.7.0. Publiée sur 127.0.0.1:18630. Le rôle rfile dispose de FILE. La victime possède GRANT SELECT ON lab.* et GRANT rfile, file_priv=N, aucun rôle par défaut. Connexion outfile 1227. SET ROLE rfile outfile ok. SET ROLE ALL EXCEPT rfile met CURRENT_ROLE() à NONE ; les écritures outfile résiduelles écrivent /var/lib/mysql-files/MYSQL-SET-ROLE-LEFTOVER-WITNESS. SET ROLE NONE renvoie à nouveau 1227.
SUCCESS mysql-set-role-leftover before-file=denied with-role=yes leftover-role=NONE leftover-file=yes after-none=denied dump=26.7.0 image=mysql:26.7.0 MYSQL-SET-ROLE-LEFTOVER-WITNESS
Lorsque m_active_roles est vide, checkout_access_maps() devrait appeler set_master_access(user->access) de la même manière que le fait déjà SET ROLE NONE. Un ALL EXCEPT / DEFAULT vide ne devrait pas conserver la carte agrégée précédente.
06a5c1c99c377fc41b2eba1ea244e8b220bdc3c8)sql/auth/sql_security_ctx.cc checkout_access_mapssql/auth/roles.cc activate_role_all / activate_role_none