Une fonction définie par l'utilisateur étend MySQL avec du code natif chargé depuis une bibliothèque partagée dans le répertoire de plugins. La bibliothèque lib_mysqludf_sys expose sys_exec (exécuter une commande) et sys_eval (exécuter une commande et retourner sa sortie), qui donnent l'exécution de commandes sous le compte de service MySQL directement depuis SQL.
La chaîne comporte trois étapes, chacune nécessitant des privilèges :
- Écrire la bibliothèque dans le répertoire de plugins avec
INTO DUMPFILE(nécessiteFILE, unsecure_file_privpermissif et un@@plugin_diraccessible en écriture). - Enregistrer la fonction, ce qui nécessite
CREATE FUNCTION(fait partie des privilèges d'administration) :
CREATE FUNCTION sys_exec RETURNS INT SONAME 'lib_mysqludf_sys.so';
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'lib_mysqludf_sys.so';
- L'appeler :
SELECT sys_eval('id');
sys_eval('id') retourne la sortie de la commande sous forme de chaîne, donc elle se relit in-band ; sys_exec ne retourne qu'un code de sortie et est utilisé pour les actions sans retour comme ajouter un utilisateur ou démarrer un reverse shell.
Cette voie dépend des requêtes empilées ou d'un contexte de requête qui autorise CREATE FUNCTION, ce que les pilotes PHP courants ne fournissent pas, donc en pratique elle s'atteint via une console d'administration, un client multi-instructions ou une injection dans un contexte qui permet plusieurs instructions. Le gain est l'exécution sous le compte de service de la base plutôt que sous l'utilisateur web moins privilégié sous lequel s'exécute un web shell.
Outils#
- sqlmap (
--os-shellautomatise les voies OUTFILE et UDF) - lib_mysqludf_sys
Références#
- MySQL Reference Manual: CREATE FUNCTION (UDF),
plugin_dir - OWASP Testing Guide: Testing for SQL Injection