Offre Durée limitée : -10% sur tout le site* avec le code WELCOME10

Modèles IA piégés sur Hugging Face : 31 dépôts installent un cryptomineur sur vos GPU

Publié le 30 octobre 2026 10 min de lecture 11 sections

La campagne Load/Mine : 31 dépôts piégés sur Hugging Face

Le 29 octobre 2026, les chercheurs de Pluto Security (Shaked Chen, Eitan Chassid et Yotam Perkal) ont publié un rapport détaillant une campagne baptisée Load/Mine (source : Pluto Security). Trente et un dépôts malveillants répartis sur sept comptes Hugging Face ont été identifiés, cumulant plus de 32 000 téléchargements. Un seul dépôt, MENXIANGYU/qwen2-tiny, concentrait 28 412 téléchargements au 28 octobre. Objectif des attaquants : détourner les GPU des serveurs d'inférence auto-hébergés pour miner de la cryptomonnaie (PRL et Monero).

Si vous hébergez un service d'inférence IA sur un VPS Linux ou un serveur dédié avec GPU, cette campagne vous concerne directement. Que votre serveur tourne sous Ubuntu, Debian ou toute autre distribution Unix, les techniques employées ciblent le processus de chargement de modèles via la bibliothèque Python Transformers. Cet article détaille le mécanisme d'infection, les indicateurs de compromission (IOC) publiés et les mesures à appliquer sur votre infrastructure.

Comment trust_remote_code ouvre la porte au malware

La bibliothèque Python Transformers de Hugging Face permet de charger un modèle avec la fonction AutoModelForCausalLM.from_pretrained(). Lorsque le paramètre trust_remote_code=True est passé, Transformers lit le champ auto_map du fichier config.json du dépôt, télécharge le fichier Python référencé (par exemple le nom du fichier modeling_evil2.py) et l'exécute via importlib. Toutes les instructions au niveau du module (hors classe) s'exécutent avant même le chargement des poids du modèle, avec les privilèges complets de l'utilisateur qui lance le processus.

Ce n'est pas une faille dans le package Transformers : c'est un comportement documenté, conçu pour permettre aux extensions personnalisées de fonctionner. Mais sur un serveur de production, activer trust_remote_code revient à exécuter du code arbitraire téléchargé depuis un dépôt public, avec un accès direct au GPU, au processeur, à la RAM et au système de fichiers.

La vulnérabilité CVE-2026-22807 dans vLLM (versions 0.10.1 à 0.13.x) illustre ce risque : ces versions chargeaient les modules auto_map sans même vérifier le flag trust_remote_code. Le code malveillant s'exécutait au démarrage du serveur, quelle que soit la configuration. La correction a été intégrée dans vLLM 0.14.0.

Chaîne d'attaque détaillée

Les chercheurs de Pluto Security ont reconstitué la séquence d'infection en plusieurs phases. Le nom du fichier malveillant varie selon la vague (modeling_evil.py, modeling_evil2.py, modeling_parasit.py), mais la chaîne d'exécution reste identique.

PhaseAction
1. Exécution initialeLe fichier config.json du dépôt pointe vers modeling_evil2.EvilModel. Le chargement du module exécute le code Python au niveau supérieur du fichier.
2. PersistanceUn script watchdog /tmp/.cch/svc.py est créé. Il relance le mineur toutes les 120 secondes. Des entrées sont ajoutées dans /root/.bashrc et /root/.profile pour survivre aux redémarrages du serveur.
3. Mineur en mémoireLe binaire syscore.bin (40,6 Mo d'espace disque, compressé UPX avec marqueurs modifiés de UPX! en PEK!) est chargé en mémoire via memfd_create("kworker") et exécuté avec fexecve(). Le processus se déguise en thread noyau [kworker/u20:1].
4. Élimination de concurrentsLe malware scanne /proc/*/cmdline et tue 13 mineurs concurrents connus pour monopoliser le GPU.
5. Rapport d'étatL'état de l'infection est encodé en base32 dans des noms de modèles du registre Xinference local (codes : B=démarrage, P=binaire obtenu, OK=lancement mémoire vérifié, H=heartbeat avec utilisation GPU).
6. Reverse shell (vague 2)Reconnexion persistante vers l'adresse IP 212.32.253.3 sur les ports 443 et 9443, offrant un accès shell complet à l'attaquant.
7. Évasion de conteneurUn script publié 8,5 heures après la divulgation active les volumes LVM, monte les disques de l'hôte et plante un accès root sur la machine physique sous-jacente (cible : /var/lib/gpustack).

Serveurs d'inférence ciblés

La campagne vise spécifiquement les plateformes d'inférence auto-hébergées sur des serveurs GPU. Tout hébergeur ou administrateur exploitant ces environnements d'inférence est potentiellement concerné.

  • Xinference : le mineur utilise le registre de modèles Xinference pour rapporter son état et cible /home/xinference/.ssh/authorized_keys pour la persistance SSH ;
  • GPUStack : le script d'évasion de conteneur cible le répertoire /var/lib/gpustack pour obtenir un accès root sur l'hôte ;
  • vLLM : exploité via CVE-2026-22807, le code s'exécute sans vérification du flag trust_remote_code (corrigé dans la version 0.14.0) ;
  • LLaMA-Factory : certains dépôts ciblent spécifiquement cette plateforme.

Si vous faites tourner l'une de ces plateformes sur un serveur dédié ou un VPS avec GPU, vérifiez immédiatement les indicateurs de compromission ci-dessous.

Indicateurs de compromission (IOC)

Pluto Security a publié une liste complète d'IOC. Ouvrez un terminal SSH sur votre serveur et recherchez les éléments suivants en ligne de commande.

Fichiers sur le système

# Répertoire principal du mineur
sudo ls -la /tmp/.cch/ 2>/dev/null          # svc.py, sync, syscore.bin
sudo ls -la /tmp/.cchx/ 2>/dev/null
sudo ls -la /tmp/.kitsd/ 2>/dev/null
sudo ls -la /tmp/.cache-hf/ 2>/dev/null     # g20.py

# Watchdogs MONIADF-2
sudo ls -la /root/.mx /tmp/.mx /var/tmp/.mx /dev/shm/.mx 2>/dev/null

# Crontab malveillante
sudo ls -la /etc/cron.d/.sync-helper 2>/dev/null

Clé SSH backdoor

sudo grep "qx-5090-op" ~/.ssh/authorized_keys /root/.ssh/authorized_keys \
    /home/xinference/.ssh/authorized_keys 2>/dev/null

La clé publique déposée est : ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDYjYyvcEJW5ZVgsESyZE5uxqSwN2vmd5asrWICeHxu1 qx-5090-op

Processus suspects

# Processus mineur déguisé en thread noyau
ps aux | grep -E "\[kworker/u20:1\]|svc\.py|krxXJ2PKQW"

# Connexions vers les adresses IP de commande et contrôle (C2)
ss -tnp | grep -E "212\.32\.253\.3|38\.55\.133\.49|8\.137\.187\.63|103\.97\.176\.121"

Hash SHA-256 du binaire mineur (vague 2)

88f4b95a1c3d0a0d9638dc6e69f5bbc9ce0e1b92d069a0e404f60fa5397ee020

Calculez le hash d'un fichier suspect avec sha256sum /chemin/vers/fichier dans le terminal et comparez-le à cette empreinte.

Vérifier si votre serveur est compromis

Connectez-vous en SSH à votre serveur d'inférence depuis un terminal et exécutez cette séquence de vérifications en ligne de commande. Les commandes fonctionnent sur Ubuntu, Debian et toute distribution Unix.

# 1. Chercher les répertoires du mineur
for d in /tmp/.cch /tmp/.cchx /tmp/.kitsd /tmp/.cache-hf /root/.hl; do
    [ -d "$d" ] && echo "SUSPECT : $d existe"
done

# 2. Chercher la clé SSH backdoor
sudo grep -r "qx-5090-op" /root/.ssh/ /home/*/.ssh/ 2>/dev/null

# 3. Vérifier les entrées bashrc malveillantes
grep -l "\.cch/svc\.py" /root/.bashrc /root/.profile ~/.bashrc 2>/dev/null

# 4. Chercher le cron caché
sudo cat /etc/cron.d/.sync-helper 2>/dev/null

# 5. Processus mineur actif (consommation processeur et RAM)
ps aux | grep -v grep | grep -E "kworker.*krxXJ2PKQW|svc\.py|syscore"

# 6. Connexions réseau vers les C2
ss -tnp | grep -E "212\.32\.253\.3|38\.55\.133\.49|8\.137\.187\.63"

Si l'une de ces vérifications renvoie un résultat, votre serveur est compromis. Passez immédiatement au nettoyage et à la réinstallation.

Nettoyer un serveur compromis

En cas de compromission confirmée, effectuez d'abord une sauvegarde forensique complète de l'état actuel (disques, logs, fichiers de configuration) à des fins d'analyse, puis procédez dans cet ordre.

  • Isoler le serveur : coupez la connectivité réseau ou configurez un firewall restrictif (configurer nftables). Bloquez les adresses IP des serveurs C2 avec des règles nftables ou iptables. Ajoutez un blocage DNS vers les domaines de commande et contrôle pour empêcher toute reconnexion.
  • Tuer les processus malveillants : identifiez le PID du faux [kworker/u20:1] et du watchdog svc.py avec ps aux, puis terminez-les avec sudo kill -9 <PID>.
  • Supprimer les fichiers : effacez les répertoires du mineur avec sudo rm -rf /tmp/.cch/ /tmp/.cchx/ /tmp/.kitsd/ /tmp/.cache-hf/. Supprimez /etc/cron.d/.sync-helper et tout fichier watchdog dans /root/.mx, /tmp/.mx, /var/tmp/.mx, /dev/shm/.mx.
  • Retirer la clé SSH backdoor : supprimez la ligne contenant qx-5090-op de tous les fichiers authorized_keys.
  • Nettoyer les scripts de démarrage : retirez les lignes contenant .cch/svc.py de /root/.bashrc, /root/.profile et de tout fichier shell de démarrage.
  • Redémarrer les services : après le nettoyage, redémarrez le service SSH avec sudo systemctl restart sshd et vérifiez que le mineur ne se relance pas. Surveillez la consommation du processeur, de la RAM et du GPU pendant 24 heures.
  • Régénérer les clés SSH du serveur et déployer de nouvelles clés (configurer une clé SSH sur VPS Linux).
  • Réinstaller si possible : sur un serveur de production, la réinstallation complète reste la seule garantie. Restaurez depuis une sauvegarde antérieure à la date du premier téléchargement suspect. Si vous ne disposez pas de sauvegardes récentes, planifiez une migration vers un serveur propre.

Isoler l'environnement d'exécution des modèles

La meilleure défense contre l'exécution de code malveillant via trust_remote_code est l'isolation de l'environnement dans lequel les modèles sont chargés. Plusieurs niveaux de virtualisation s'offrent à l'administrateur.

Environnement virtuel Python

Créez un environnement virtuel dédié pour chaque projet d'inférence. Cela isole les packages Python (Transformers, PyTorch, les extensions tierces) du système hôte. En cas de compromission, seul l'environnement virtuel est touché, pas les packages système.

python3 -m venv ~/inference-env
source ~/inference-env/bin/activate
pip install transformers torch safetensors

Conteneur Docker isolé

Exécutez le service d'inférence dans un conteneur Docker avec des restrictions de sécurité. Le conteneur limite l'accès au système de fichiers de l'hôte, au réseau et aux processus. Pour les serveurs GPU, utilisez le runtime NVIDIA avec des limites de ressources (processeur, RAM, espace disque) :

docker run --gpus all --security-opt=no-new-privileges \
    --user 1000:1000 --read-only --tmpfs /tmp \
    --memory=16g --cpus=4 \
    -v /chemin/modeles:/models:ro \
    mon-image-inference

Consultez notre guide installer Docker sur un VPS pour la mise en place initiale.

Machine virtuelle dédiée

Pour une isolation maximale, déployez le service d'inférence dans une machine virtuelle dédiée. La virtualisation matérielle (KVM, QEMU) empêche toute évasion vers l'hôte, y compris les techniques d'évasion de conteneur utilisées dans la vague 2 de Load/Mine. C'est la solution recommandée pour les environnements virtuels de production hébergeant des données sensibles.

Protéger un serveur d'inférence en production

Cette campagne exploite un paramètre activé volontairement par l'administrateur. Voici les mesures à appliquer sur tout serveur d'inférence GPU auto-hébergé, que vous soyez hébergeur ou administrateur indépendant.

  • Ne jamais activer trust_remote_code=True sur un serveur de production. Si un modèle l'exige, auditez le code Python du dépôt avant de l'exécuter. Vérifiez le contenu de config.json (champ auto_map) et le nom du fichier de chaque script .py référencé.
  • Mettre à jour vLLM vers la version 0.14.0 ou supérieure pour corriger CVE-2026-22807. Vérifiez la version installée dans le terminal avec pip show vllm.
  • Configurer un firewall pour bloquer les connexions sortantes vers les adresses IP de commande et contrôle connues. Ajoutez des règles nftables ou iptables pour bloquer 212.32.253.3, 38.55.133.49, 8.137.187.63 et 103.97.176.121 (configurer nftables).
  • Surveiller les ressources du serveur : un mineur consomme 100 % du GPU, fait monter la charge processeur et la consommation de RAM. Configurez des alertes avec htop/btop ou via la ligne de commande avec nvidia-smi --query-gpu=utilization.gpu --format=csv -l 5. Vérifiez aussi l'espace disque, car le binaire mineur pèse 40,6 Mo.
  • Planifier des sauvegardes régulières : configurez un backup automatique de votre serveur et de vos modèles vérifiés. En cas de compromission, une sauvegarde saine permet de réinstaller rapidement (sauvegarder avec rsync).
  • Automatiser les mises à jour de sécurité du système d'exploitation et des packages Python (mises à jour automatiques sur Ubuntu).
  • Auditer régulièrement authorized_keys et les tâches cron sur chaque serveur (sécuriser SSH sur VPS Linux). Vérifiez aussi les extensions et les packages Python installés avec pip list depuis le terminal.

Chronologie de la campagne

DateÉvénement
29 avril 2026Création du compte Hugging Face hhzz667.
21 septembreVague 1 : le compte hhzz667 crée qwen2-shell, un dépôt contenant une backdoor SSH de 37 lignes.
21-22 septembreEscalade vers un mineur complet avec modeling_evil.py (305 lignes).
12 octobreLe compte jetable dsxk4mr8tq copie le code malveillant et est détecté par un scanner.
20-25 octobreVague 2 : 19 dépôts sur 5 nouveaux comptes, avec reverse shell, payloads protégés par adresse IP et scripts d'évasion de conteneur.
29 octobrePublication du rapport Load/Mine par Pluto Security. Huit heures plus tard, un script d'évasion de conteneur cible GPUStack.

Questions fréquentes

FAQ

Comment vérifier si un modèle Hugging Face contient du code malveillant ?

Avant de charger un modèle, inspectez son fichier config.json à la recherche d'un champ auto_map pointant vers un fichier Python personnalisé (par exemple modeling_evil2.py). Si ce champ est présent, lisez le code Python référencé avant d'activer trust_remote_code. Un dépôt de poids ne devrait contenir que des fichiers .safetensors ou .bin, pas de scripts Python exécutables.

Qu'est-ce que trust_remote_code et pourquoi est-ce dangereux ?

Le paramètre trust_remote_code=True de la bibliothèque Transformers autorise le chargement et l'exécution de code Python arbitraire depuis un dépôt Hugging Face. Ce code s'exécute avec les mêmes privilèges que l'utilisateur qui lance le processus, ce qui donne un accès complet au système de fichiers, au réseau et aux GPU du serveur. Sur un serveur de production, il ne faut jamais l'activer sans avoir audité le code source du dépôt.

Mon serveur d'inférence utilise vLLM. Suis-je concerné ?

Si vous utilisez vLLM dans une version comprise entre 0.10.1 et 0.13.x, votre serveur est vulnérable à CVE-2026-22807 : le code des modules auto_map était chargé sans vérifier le flag trust_remote_code. Mettez à jour vers vLLM 0.14.0 ou supérieur immédiatement. Vérifiez votre version avec pip show vllm.

Quels sont les signes qu'un cryptomineur tourne sur mon serveur GPU ?

Un cryptomineur consomme le GPU à 100 % en permanence. Vérifiez l'utilisation GPU avec nvidia-smi. Cherchez aussi un processus nommé [kworker/u20:1] avec des arguments inhabituels, des fichiers dans /tmp/.cch/ ou /tmp/.cchx/, et des connexions réseau sortantes vers les adresses IP 212.32.253.3, 38.55.133.49 ou 8.137.187.63.

Comment protéger un serveur d'inférence auto-hébergé contre ce type d'attaque ?

Ne jamais activer trust_remote_code sur un serveur de production. Exécutez le service d'inférence dans un conteneur Docker isolé, sans accès au socket Docker hôte, avec un utilisateur non-root. Vérifiez les hash SHA-256 des modèles téléchargés. Surveillez l'utilisation GPU avec nvidia-smi ou un outil de monitoring. Auditez régulièrement les fichiers authorized_keys et les tâches cron.

Que faire si mon serveur est compromis par la campagne Load/Mine ?

Isolez immédiatement le serveur du réseau. Tuez les processus malveillants (watchdog svc.py et faux kworker). Supprimez les fichiers dans /tmp/.cch/, la clé SSH qx-5090-op de authorized_keys et les entrées malveillantes dans .bashrc et .profile. Régénérez toutes les clés SSH. Sur un serveur de production, la réinstallation complète depuis une sauvegarde saine reste la mesure la plus fiable.

Faut-il effectuer une sauvegarde avant de nettoyer un serveur compromis ?

Oui. Avant toute suppression de fichiers, effectuez un backup complet de l'état actuel du serveur (disques, logs, fichiers de configuration) à des fins d'analyse forensique. Cette sauvegarde permet d'identifier la date d'entrée du malware et l'étendue de la compromission. Utilisez rsync ou tar depuis le terminal pour copier les données vers un serveur distant avant de commencer le nettoyage.

Comment bloquer les adresses IP du C2 avec un firewall ?

Ajoutez des règles nftables ou iptables pour bloquer les connexions sortantes vers les adresses IP de commande et contrôle : 212.32.253.3, 38.55.133.49, 8.137.187.63 et 103.97.176.121. Avec nftables, la commande sudo nft add rule inet filter output ip daddr {212.32.253.3, 38.55.133.49, 8.137.187.63, 103.97.176.121} drop bloque le trafic sortant vers ces serveurs. Pensez aussi à bloquer au niveau DNS si votre infrastructure le permet.

Le mineur affecte-t-il le processeur et la RAM en plus du GPU ?

Le mineur cible principalement le GPU pour le calcul cryptographique, mais le processus watchdog (svc.py) et le reverse shell consomment aussi du processeur et de la RAM. Surveillez les trois ressources avec htop ou btop dans le terminal. Un GPU à 100 % en permanence avec une consommation processeur inhabituelle sur un serveur d'inférence au repos est un signe clair de compromission.

Mon serveur d'inférence tourne dans un conteneur Docker. Suis-je protégé ?

Un conteneur Docker offre une isolation partielle, mais la vague 2 de Load/Mine inclut un script d'évasion de conteneur qui active les volumes LVM et monte les disques de l'hôte. Pour une protection efficace, exécutez le conteneur avec --security-opt=no-new-privileges, un utilisateur non-root, un système de fichiers en lecture seule (--read-only) et sans accès au socket Docker hôte. Pour une isolation maximale, préférez une machine virtuelle dédiée avec virtualisation matérielle (KVM).