L’année 2023 a été une année charnière pour l'intelligence artificielle générative (GenIA). La publication de grands modèles de langage (LLM) a montré la puissance de la technologie pour rendre les processus d'entreprise plus efficaces. Si bien que beaucoup d’entreprises ont cherché activement à adopter l'IA générative en entraînant des modèles sur leurs propres ensembles de données. Le développement et l'entraînement de modèles d'IA peuvent coûter cher au point de devenir l'un des actifs les plus précieux de l’entreprise. Il est donc important de garder à l'esprit que ces modèles sont susceptibles d'être volés ou attaqués, et que les systèmes qui les hébergent doivent être dotés de protections et de politiques de sécurité solides. Une récente vulnérabilité corrigée dans la plateforme open-source de gestion du cycle de vie des modèles d'apprentissage machine, MLflow, montre à la facilité avec laquelle des attaquants pourraient voler ou empoisonner des données d'entraînement sensibles lorsqu'un développeur visite un site web aléatoire sur Internet à partir de la même machine que celle où est exécuté MLflow. La faille, référencée CVE-2023-43472, a été corrigée dans la version 2.9.0 de MLflow. Ce n'est pas la première fois que cette plateforme expose les modèles d'IA et d'apprentissage machine stockés dans le cloud, cela avait été aussi le cas en mars dernier.
De nombreux développeurs pensent, à tort, que les services liés à localhost - le nom d'hôte interne d'un ordinateur - ne peuvent pas être ciblés depuis Internet. C’est ce qu’a expliqué Joseph Beeton, chercheur principal en sécurité des applications chez Contrast Security, lors d’une conférence consacrée à l'attaque des environnements de développement via des services localhost organisée fin novembre pendant la DefCamp 2023. En effet, de graves vulnérabilités découvertes récemment par Joseph Beeton dans le framework Java Quarkus et MLflow permettent à des attaquants distants d'exploiter localement les caractéristiques des interfaces de développement ou des API exposées par ces applications. Les attaques n'exigent de l'utilisateur qu'une visite sur un site web contrôlé par l'attaquant dans son navigateur ou sur un site légitime sur lequel l'attaquant a réussi à placer des publicités spécifiques.
Attaques contre le serveur local via un code JavaScript malveillant
Cela fait longtemps que les attaques « drive-by » par détournement de site existent, mais elles sont puissantes quand elles sont combinées à une vulnérabilité de type « cross-site request forgery » (CSRF) de falsification de requêtes intersites dans une application. Dans le passé, les pirates ont utilisé des attaques par détournement de site (drive-by attacks) via des publicités malveillantes placées sur des sites web afin de détourner les paramètres DNS des routeurs domestiques des utilisateurs. Normalement, les navigateurs n'autorisent le code JavaScript qu'à effectuer des requêtes vers des ressources de la même origine (domaine) que le script. Mais il est possible d’utiliser un mécanisme spécial appelé « partage de ressources inter-origines » (Cross-Origin Resource Sharing, CORS) pour contourner cette restriction et permettre aux scripts d'effectuer des requêtes à partir de différentes origines si le serveur cible l'autorise spécifiquement. Par exemple, si un morceau de code JavaScript chargé dans un navigateur du domaine A essaie de faire une demande au domaine B, le navigateur fera d'abord une demande dite de contrôle préalable pour vérifier si le domaine B a une politique CORS qui autorise les demandes scriptées du domaine A.
Même si cette restriction s'applique également à l'hôte local, Joseph Beeton fait remarquer qu'il existe un autre type de demande appelée « demande simple », toujours autorisée par la plupart des navigateurs (à l'exception de Safari) et qui ne déclenche pas de demande de contrôle préalable parce qu'elle est antérieure à la politique CORS. Par exemple, ces demandes sont utilisées par l'élément de la norme HTML pour soumettre des données d'une origine à l'autre, mais elles peuvent aussi être déclenchées par JavaScript. Une requête simple peut être de type GET, POST et HEAD et peut avoir le type de contenu application/x-www-form-urlencoded, multipart/form-data, text/plain ou aucun type de contenu. Avec une limite cependant : le script qui les crée ne reçoit pas de réponse en retour, à moins que le serveur cible ne l'accepte via l'en-tête Access-Control-Allow-Origin (contrôle d'accès-autorisation d'origine). Mais, du point de vue de l'attaque, il n'est pas vraiment nécessaire d'obtenir une réponse en retour tant que l'action prévue déclenchée par la demande se produit. C'est le cas des vulnérabilités MLflow et Quarkus.
Vol et empoisonnement de modèles d'apprentissage machine
Une fois MLflow installé, son interface utilisateur est accessible par défaut via http://localhost:5000 et supporte une API REST à travers laquelle il est possible d’effectuer des actions de manière programmatique. Normalement, l'interaction avec l'API se fait par via des requêtes POST avec un type de contenu application/JSON qui n'est pas autorisé pour les requêtes simples. Toutefois, M. Beeton a constaté que l'API de MLflow ne vérifiait pas le type de contenu des requêtes, et qu’elle autorisait les requêtes de type text/plain. Ceci permet des attaques « cross-origin » à distance à travers le navigateur via des requêtes simples. L'API dispose de fonctionnalités limitées telles que la création d'une nouvelle expérience ou le renommage d'une expérience existante, mais pas la suppression d'expériences.
De manière pratique, l'expérience par défaut dans MLflow, dans laquelle seront enregistrées les nouvelles données, est appelée « Default », de sorte que les attaquants peuvent d'abord envoyer une demande pour la renommer en « Old » et ensuite créer une nouvelle expérience, qui sera appelée « Default » mais aura un artifact_uri pointant vers un bucket de stockage S3 externe qu'ils contrôlent. « Une fois qu'une nouvelle exécution MLFLow est effectuée - par exemple, mlflow run sklearn_elasticnet_wine -P alpha=0.5, experiment-name Default - le résultat de l'exécution sera téléchargé dans le bucket S3 », a expliqué Joseph Beeton dans un billet de blog. Cela permettrait à l'attaquant d'obtenir une version sérialisée du modèle ML ainsi que les données utilisées pour l'entraîner. L'attaquant pourrait aller encore plus loin. « Étant donné qu'un modèle de ML est stocké dans le bucket, il serait possible d'empoisonner le modèle de ML lui-même », a déclaré le chercheur. « Dans une telle attaque, un adversaire est capable d'injecter de mauvaises données dans le pool d'entraînement du modèle, et lui faire apprendre quelque chose qu'il ne devrait pas », a ajouté le chercheur.
L'exécution de code à distance souvent possible
L'exécution de code à distance est également possible si l'attaquant modifie le fichier model.pkl pour y injecter un exploit Python Pickle. L'exécution de code à distance a aussi été possible dans le cas de la vulnérabilité de Quarkus, découverte par ailleurs par M. Beeton. Et celle-ci était également exploitable via des requêtes simples provenant de sites web distants parce que l'interface utilisateur Dev de l'application était liée à l'hôte local et ne disposait d'aucune protection supplémentaire contre les attaques par falsification des requêtes intersites CSRF. « Comme je l'ai démontré lors de la présentation au DefCamp, il est possible de générer une exécution de code à distance (Remote Code Execution, RCE) sur la machine du développeur ou sur d'autres services de son réseau privé », a déclaré le chercheur. « Étant donné que les développeurs ont un accès en écriture aux bases de code, aux clés AWS, aux identifiants de serveur, etc., l'accès à la machine du développeur donne à un attaquant une grande marge de manœuvre pour pivoter vers d'autres ressources sur le réseau, et pour modifier ou voler entièrement la base de code ».