Problème 2857

CVE-2023-1177 : LFI/RFI dans MLflow
* LFI/RFI menant à la prise de contrôle du système et du compte cloud
- Tous les CVE corrigés dans la version 2.2.2
- Outil d'exploitation et de numérisation publié
L'un des outils les plus populaires dans un système ML est MLflow (avec plus de 13 millions de téléchargements mensuels et en augmentation) qui est utilisé pour gérer le cycle de vie de l'apprentissage automatique de bout en bout. .
Protect AI a testé la sécurité de MLflow et a trouvé une vulnérabilité combinée Local File Inclusion/Remote File Inclusion qui peut conduire à une prise de contrôle complète du système ou du fournisseur cloud. Les organisations exécutant un serveur MLflow sont invitées à mettre à jour la dernière version ou au moins la version 2.2.2 immédiatement. La version 2.2.1 corrige CVE-2023-1177 et la version 2.2.2 corrige CVE-2023-1176. Dans ce blog, nous explorons l'impact de cette vulnérabilité, comment la détecter et notre processus pour découvrir ces impacts critiques. Si vous utilisez MLflow, veuillez utiliser notre outil gratuit fourni dans ce blog et commencez immédiatement à corriger vos systèmes. L'application de correctifs à vos systèmes peut être un défi à l'aide de vos outils traditionnels, car de nombreux systèmes automatisés de gestion des correctifs n'énumèrent ni n'identifient MLflow et, s'ils le font, peuvent ne pas effectuer de vérifications de version.
Il est essentiel que vous effectuiez immédiatement la mise à niveau vers la dernière version de MLflow, même si vos instances ne sont pas en production et utilisées uniquement dans des environnements de développement.
Impact
L'exploitation de cette vulnérabilité permet à un attaquant distant non authentifié de lire n'importe quel fichier sur le serveur auquel l'utilisateur qui a démarré le serveur MLflow peut accéder.
L'exécution de code à distance peut être acquise en saisissant les clés SSH privées ou les informations d'identification du fournisseur de services cloud à partir du serveur MLflow. Cela permet à un attaquant de se connecter à distance aux ressources du serveur ou du cloud et d'exécuter du code arbitraire sous les autorisations des informations d'identification trouvées.
Détails de la vulnérabilité
- Aucune interaction de l'utilisateur requise
- Aucune connaissance préalable de l'environnement requise
- Toutes les configurations personnalisées de MLflow sont vulnérables, y compris l'installation prête à l'emploi
- Toutes les versions de MLflow jusqu'à 2.1.1 sont vulnérables à LFI
- L'outil d'exploit fonctionne sur toutes les versions de MLflow v1.12 - v2.1.1
- Toutes les versions de MLflow antérieures à la v2.0 étaient vulnérables à LFI via l'exploit plus simple :
http://<server:port>/get-artifact?path=../../../../. ./etc/passwd&run\_uuid=<run\_uuid
Les responsables de MLflow ont été extrêmement rapides à répondre à la divulgation responsable de cette vulnérabilité avec des correctifs livrés en quelques semaines seulement. Les versions de MLflow postérieures à 2.1.1 ne sont plus vulnérables.
Détection de vulnérabilité
Pour vérifier si votre serveur MLflow est vulnérable, utilisez notre outil gratuit CVE-2023-1177-scanner.
Notre processus de découverte
Nous avons commencé par installer MLflow, en lançant le proxy d'interception BurpSuite pour intercepter tous les appels d'API MLflow, en exécutant une expérience pour remplir MLflow avec des données, puis en démarrant le serveur d'interface utilisateur pour l'exploration.
# Téléchargez la source MLflow pour accéder à leurs exemples d'exécution
git clone https://github.com/mlflow/mlflow
# Créez et entrez un nouveau répertoire en dehors du répertoire mlflow/
mkdir mlflowui
cd mlflowui
# Copiez l'exemple de code de la source MLflow dans ce nouveau répertoire
cp -r ../mlflow/examples/sklearn_elasticnet_wine .
# Configurer un environnement virtuel pour installer les exigences
python3 -m venv venv
source venv/bin/activer
# Installez mlflow dans cet environnement virtuel
pip installer pandas mlflow
# Exécutez l'exemple d'expérience
mlflow run --env-manager=local sklearn_elasticnet_wine -P alpha=0.5
# Exécutez l'interface utilisateur pour voir les détails de l'expérience
interface utilisateur mlflow --hôte 127.0.0.1:8000
Lors de la création d'expériences, il nous a donné la possibilité de spécifier un répertoire qui stockerait les objets. Cela semblait être un chemin de fichier configurable, comme le montre l'exemple d'expérience que nous avons exécuté :
Cela a immédiatement attiré notre attention car cela nécessiterait un filtrage parfaitement implémenté pour empêcher l'inclusion de fichiers locaux ou les écrasements arbitraires de fichiers. Cependant, vous ne pouvez pas exécuter une expérience MLflow à distance depuis l'interface utilisateur. Étant donné que rien ne se passe réellement avec l'emplacement de l'artefact lorsque vous créez une expérience via l'interface utilisateur, il n'y a aucune considération de sécurité ici. Nous avons ensuite poursuivi l'exploration en cliquant sur les exécutions d'expériences individuelles.
Cliquer sur le nom de l'exécution comme on le voit dans l'image ci-dessus nous a amenés aux détails de l'exécution de l'expérience où nous avons pu voir les fichiers impliqués dans l'expérience et les télécharger comme on le voit dans l'image ci-dessous.
Dans ce cas, nous avons vu un gros bouton "Enregistrer le modèle" dans nos fichiers d'artefacts. Nous étions curieux.
Cela ne semblait pas être quelque chose de particulièrement intéressant, car il apparaît simplement un modal qui vous permet de sélectionner un modèle, puis enregistre les détails de ce modèle en tant que "Version 1".
Mais que se passait-il sous le capot ? Nous avons vérifié BurpSuite.
Nous avons trouvé une autre entrée de protocole et de chemin de fichier qui ne nous a pas été montrée dans l'interface utilisateur. Cela parait suspect. Nous l'avons changé manuellement en clé SSH privée de l'utilisateur file:///Users/danmcinerney/.ssh/id_rsa. L'accès à ce fichier vous permettrait de vous connecter à distance à la machine hôte MLflow en tant qu'utilisateur qui a démarré le serveur MLflow.
La nouvelle "source" était reflétée dans la réponse, ce qui indique généralement qu'un changement côté serveur s'est produit. Curieux de savoir ce que cela a accompli, nous sommes retournés et avons parcouru les détails du modèle enregistré. Rien dans les artefacts d'exécution de l'expérience, rien d'intéressant dans les détails du modèle ou les détails de la version du modèle non plus. Cela ressemblait à une autre impasse similaire à la façon dont nous avons découvert que vous pouviez pointer le chemin de l'artefact de l'expérience vers un emplacement arbitraire, mais l'interface utilisateur ne vous en donnait alors rien à voir. Cependant, après avoir examiné le journal des demandes et des réponses de BurpSuite, quelque chose d'intéressant est apparu.
Un attaquant aurait désormais accès
Une erreur de serveur interne 500 dans l'appel de l'API get-artifact nous a semblé suspecte. L'appel d'API get-artifact était intéressant au début des tests de sécurité, car c'est l'appel qui renvoie les données de fichier à partir du référentiel d'artefacts. C'est ainsi que vous téléchargez le modèle à partir d'une exécution d'expérience, et nous avons constaté qu'il était protégé par une fonction qui empêchait les vulnérabilités Local File Include, comme indiqué ci-dessous.
Nous avions passé un certain temps à essayer de contourner cela sans succès. La différence dans cet appel get-artifact particulier est qu'il n'essaie pas d'obtenir un fichier à partir d'un sous-dossier, mais qu'il accède directement au nom de fichier. De plus, ce n'est pas vraiment le même appel d'API. Voici l'appel d'API REST get-artifact documenté :
Et voici l'appel comparable model-version/get-artifact :
Les différences incluent le chemin d'URL, les paramètres et les valeurs. Ce n'est clairement pas le même appel d'API.
Nous avons noté que cet appel d'API n'est pas dans la documentation. La principale différence est qu'il recherche directement un nom de fichier via le paramètre d'URL de chemin plutôt qu'un chemin de fichier relatif dans l'appel d'API get-artifact légitime.
Cela signifie que la protection LFI est absente car il n'est pas nécessaire de parcourir un répertoire. Il suffit de contrôler l'emplacement du dossier source. Quelques étapes plus haut, nous avons essayé de modifier l'emplacement du chemin d'accès "source" d'une requête API en "file:///Users/danmcinerney/.ssh/id_rsa" lorsque nous avons créé une nouvelle version de modèle :
Ce que nous aurions dû faire était de changer l'emplacement source en un dossier et non un fichier. Nous avons corrigé cela.
Nous avons ensuite renvoyé l'appel d'API REST non documenté trouvé et l'avons pointé vers id_rsa qui est un fichier dans l'emplacement source de la nouvelle version du modèle et les clés SSH privées qui déverrouillent la possibilité de se connecter à distance au serveur.
En utilisant cette clé SSH récupérée, nous pouvons obtenir un accès terminal à l'hôte exécutant le serveur MLflow. MLflow est le plus souvent configuré pour utiliser un compartiment S3 comme magasin d'artefacts. Si tel est le cas, une autre cible de très grande valeur sur la machine serait le fichier ~/.aws/credentials qui, comme on peut l'imaginer, stocke les informations d'identification AWS.
D'autres cibles de grande valeur pourraient inclure des fichiers tels que les configurations SQL du serveur Web qui contiennent des mots de passe en clair ou /etc/shadow qui contient tous les hachages de mot de passe utilisateur qui peuvent être piratés via un outil tel que hashcat.
Outil d'exploitation
Pour vous aider à protéger vos systèmes, nous avons créé un outil simple pour découvrir votre vulnérabilité potentielle, nommé MLFIflow.py (M + LFI + flux).
Installation
git clone https://github.com/protectai/Snaike-MLflow
cd Snaike-MLflow/MLFIflow
python3 -m venv mlfiflow
source mlfiflow/bin/activer
pip install -r exigences.txt
Usage
Par défaut, MLFIflow tentera de lire /etc/passwd à partir du serveur MLflow et utilisera les noms d'utilisateur trouvés pour rechercher des clés SSH et des fichiers d'informations d'identification cloud.
python MLFIflow.py -s http://1.2.3.4:5000
Pour spécifier une liste de mots personnalisée de fichiers à télécharger, utilisez l'indicateur -f :
python MLFIflow.py -s http://1.2.3.4:5000 -f /path/to/wordlist.txt
De plus amples recherches
Nous sommes toujours intéressés à entendre et à collaborer avec d'autres chercheurs en sécurité intéressés par la sécurité du domaine de l'IA. Si vous souhaitez travailler ensemble pour une recherche rémunérée sur la sécurité de l'IA, envoyez-moi un e-mail à [dan@protectai.com] (mailto:dan@protectai.com).