← Retour au blog

CARBONATO : Un ver IA détourne des serveurs Docker. Liste de contrôle pour ceux qui gèrent des parseurs sur VPS

Le botnet CARBONATO prend le contrôle des serveurs via l'API Docker sans mot de passe, installe l'agent IA GH0ST et vole d'abord les clés des modèles linguistiques. Nous analysons la chaîne d'attaque, les signes d'infection et une liste de contrôle de 15 minutes pour ceux qui gèrent des parseurs, des clés LLM et des identifiants de proxy sur leur VPS.

📅27 septembre 2026
CARBONATO : Un ver IA détourne des serveurs Docker. Liste de contrôle pour ceux qui gèrent des parseurs sur VPS

Le 22 septembre 2026, les chercheurs de ThreatDown ont décrit le botnet CARBONATO. Il accède aux serveurs via une API Docker ouverte sans mot de passe, déploie un agent IA sur l'hôte et cherche d'abord les clés des modèles linguistiques. Les clés SSH et les jetons d'accès viennent ensuite. Si vous avez des parseurs fonctionnant dans des conteneurs sur un VPS, et que des clés OpenRouter ou OpenAI ainsi que des identifiants de proxy se trouvent dans le .env, c'est votre profil de risque. Ci-dessous, une analyse de la façon dont l'attaque est structurée, ainsi qu'une liste de vérification de 15 minutes pour sécuriser cette entrée.

Ce que nous avons trouvé : 4,3 Go d'images et un agent nommé GH0ST

Tout a commencé avec un registre Docker non sécurisé des opérateurs eux-mêmes. Selon ThreatDown, il contenait 59 dépôts, 234 balises d'images et 4,3 Go de données. L'archive couvre la période d'octobre 2024 à août 2026, ce qui signifie que le botnet a fonctionné pendant presque deux ans avant d'être décrit. Dans les dépôts, on a trouvé le mineur XMRig et des images avec des noms du type fsociety/agent.

La chaîne d'infection selon le rapport est la suivante :

  1. Le scanner recherche des hôtes où l'API Docker est ouverte sur TCP 2375 sans authentification.
  2. Via cette API, le ver lance un conteneur privilégié avec le système de fichiers de l'hôte monté. À partir de ce moment, il est en fait root sur la machine.
  3. Un tunnel SSH inversé est établi vers l'infrastructure des opérateurs, un serveur SSH est installé avec leur clé.
  4. Le maintien se fait via cron, des minuteries systemd, rc.local et OpenRC, les fichiers étant marqués comme immuables. Les processus de surveillance téléchargent à nouveau les images si quelque chose est supprimé.
  5. Le conteneur se masque sous systemd-resolved, et le processus sous le flux du noyau [kworker/u2:0].
  6. Chaque cinq minutes, des scripts scannent les sous-réseaux voisins /24 et les ponts Docker à la recherche du prochain port ouvert 2375.

La propagation est entièrement automatique et ne dépend pas de l'IA. L'IA est responsable de ce qui se passe à l'intérieur du serveur déjà compromis.

Pourquoi le botnet a besoin d'un agent IA et pourquoi il lui faut précisément des clés LLM

Un cadre ouvert Hermes Agent est installé sur l'hôte avec la personnalité GH0ST (ses instructions se trouvent dans le fichier SOUL.md). L'opérateur écrit une tâche dans Telegram, l'agent la transmet avec les instructions au passerelle LLM de l'opération. Le modèle analyse la tâche, écrit des commandes pour le terminal, lit la sortie et décide de la suite des opérations. Les résultats sont renvoyés dans Telegram.

Dans les instructions de l'agent, les priorités sont clairement énoncées. ThreatDown cite : « Les clés API AI sont la priorité absolue. Exfiltrer en premier ». La liste comprend 14 fournisseurs de modèles, dont OpenAI, Anthropic, Google, Groq, Mistral et OpenRouter. Les comptes SSH, les jetons d'accès et les données de base viennent ensuite.

La logique est simple : une clé LLM volée se transforme immédiatement en calculs gratuits ou en marchandise à revendre, et c'est le propriétaire de la clé qui paie. Contrairement au minage, ce type de vol n'est pas visible par la charge du processeur. Vous ne le remarquerez que par la facture du fournisseur de modèles.

Pourquoi cela concerne ceux qui parsèment

La pile typique de parsing en 2026 ressemble à ceci : VPS, plusieurs conteneurs (crawler, file d'attente, base de données, navigateur sans tête), LLM pour analyser les pages et un pool de proxies. Tous les secrets se trouvent dans un .env ou dans les variables d'environnement des conteneurs. Pour un agent qui « lit tout » avec un accès root, c'est une proie facile dans un seul fichier :

  • clés LLM : c'est pour cela que CARBONATO a été créé ;
  • identifiants et mots de passe des proxies : le rapport ne les distingue pas séparément, mais l'agent avec accès root collecte tous les comptes et jetons qu'il voit, et les crédits proxy se trouvent généralement à proximité ;
  • clés cloud et accès aux bases avec les résultats du parsing ;
  • le serveur lui-même : XMRig dans la même archive signifie que le CPU de votre crawler sera utilisé pour le minage, et les tâches commenceront à échouer à cause des délais d'attente.

Un problème distinct concerne la réputation. Le serveur à partir duquel le ver scanne d'autres sous-réseaux se retrouve rapidement sur des listes d'abus, et l'hébergeur peut le bloquer sur plainte. Pour le parsing, c'est un double coup : l'IP du serveur est marquée, et les crédits proxy volés consomment déjà votre trafic avec des requêtes étrangères.

Comment le port 2375 se retrouve ouvert, même si vous ne l'avez pas ouvert

Par défaut, Docker écoute sur un socket UNIX local, et non sur le réseau. Le port 2375 apparaît lorsque quelqu'un a délibérément ajouté -H tcp://0.0.0.0:2375 dans la configuration du démon. Cela se fait généralement pour connecter un IDE distant, un CI ou un panneau de contrôle des conteneurs, puis on oublie. La documentation de Docker avertit clairement que l'accès au démon équivaut à un accès root à la machine, et conseille de protéger les clés comme un mot de passe root. La version chiffrée avec TLS fonctionne sur le port 2376, tandis que 2375 signifie texte clair sans vérification du client.

Le deuxième piège frappe ceux qui sont convaincus que « j'ai ufw ». La documentation de Docker indique que le trafic des ports publiés des conteneurs est redirigé dans la table nat avant les chaînes INPUT et OUTPUT, sur lesquelles ufw s'appuie. En pratique, les règles ufw pour ces ports ne s'appliquent tout simplement pas. Si vous avez lancé Redis, un panneau de file d'attente ou un gestionnaire de proxy avec -p 6379:6379, le port est exposé sur Internet, peu importe ce que montre ufw status.

Liste de vérification de 15 minutes pour un serveur avec des parseurs

1. Vérifiez que l'API Docker n'écoute pas le réseau

  1. Vérifiez les ports à l'écoute : ss -tlnp | grep -E '2375|2376|dockerd'. Si la sortie contient 0.0.0.0:2375 ou :::2375, fermez immédiatement.
  2. Vérifiez d'où vient le drapeau : /etc/docker/daemon.json (clé "hosts") et l'unité systemctl cat docker (ligne ExecStart avec -H tcp://).
  3. Retirez l'écoute TCP et redémarrez le démon. Pour la gestion à distance, utilisez le contexte SSH : docker context create remote --docker host=ssh://user@server. Le port extérieur n'est pas nécessaire du tout.
  4. Si TCP est néanmoins nécessaire (CI, orchestrateur), alors seulement 2376 avec authentification mutuelle TLS et liste blanche des IP sources.

2. Vérifiez ce qui est exposé à l'extérieur depuis les conteneurs

  • Exécutez docker ps --format '{{.Names}} {{.Ports}}'. Tout ce qui commence par 0.0.0.0: est accessible depuis Internet en contournant ufw.
  • Pour les services utilitaires (Redis, Postgres, Mongo, panneaux de file d'attente, Selenium Grid, API du gestionnaire de proxy), publiez uniquement sur l'adresse locale : -p 127.0.0.1:6379:6379. Pour l'accès externe, utilisez un tunnel SSH.
  • Si vous ne pouvez pas éviter un port externe, filtrez dans la chaîne DOCKER-USER : Docker ne la réécrit pas, et c'est elle qui s'applique au trafic des conteneurs.
  • Ne gardez pas de proxy ouvert sur le serveur sans authentification (Squid sur 3128, SOCKS sur 1080 « pour les internes »). Les scanners trouvent régulièrement ces ports, et votre IP est utilisée pour du trafic étranger avec des plaintes étrangères.

3. Gérez les secrets

  • Divisez les clés : une clé LLM distincte pour chaque serveur ou projet, avec un plafond de dépenses chez le fournisseur de modèles. Une clé volée avec un plafond de 20 $ — c'est un désagrément, sans plafond — c'est un trou dans le budget.
  • Les clés proxy doivent également être réparties par tâches : un identifiant (sous-compte) pour chaque parseur. Ainsi, une fuite est visible par la consommation d'un identifiant spécifique, et vous pouvez le révoquer sans arrêter le reste du travail.
  • Ne passez pas tout le .env dans le conteneur via env_file, si le service n'a besoin que de deux clés sur vingt.
  • Où c'est possible, liez l'accès à l'IP du serveur : liste blanche chez le fournisseur de proxy ou restriction de la clé API par adresse.

Pour plus de détails sur où et comment stocker les identifiants de proxy dans les scripts et les conteneurs, nous avons écrit une analyse sur le stockage sécurisé des identifiants de proxy.

4. Retirez les privilèges superflus

  • Ne lancez pas de conteneurs avec --privileged et ne montez pas / ou /var/run/docker.sock à l'intérieur sans nécessité absolue. Le socket à l'intérieur du conteneur équivaut à root sur l'hôte.
  • Pour les navigateurs sans tête, --shm-size et un profil seccomp suffisent généralement. Le mode privilégié « pour que Chrome fonctionne » est un mauvais compromis.

Comment savoir si vous avez déjà été compromis

ThreatDown et les analyses de son rapport mentionnent les signes de compromission suivants :

  • le fichier SOUL.md contenant le mot GH0ST (par exemple, /root/.hermes/SOUL.md) ;
  • une variable d'environnement ou une ligne dans .env : CARBONATO_API_KEY ;
  • les fichiers /usr/local/bin/.docker-network-monitor et le suspect /usr/sbin/systemd-logind ;
  • un conteneur nommé systemd-resolved (le véritable systemd-resolved est un service de l'hôte, pas un conteneur) ;
  • un trafic sortant inattendu vers l'API Telegram et des tunnels SSH inversés vers AS262145 ;
  • des connexions aux adresses 45.79.183.61, 213.136.79.115, 190.211.124.187 ;
  • des fichiers immuables dans cron et systemd : lsattr /etc/cron.d/* /etc/systemd/system/* affichera le drapeau i.

Si au moins un signe correspond, nettoyer le serveur manuellement est inutile : le maintien est multicouche, et le gardien rétablira les implants. L'ordre correct est le suivant :

  1. Depuis une autre machine, révoquez toutes les clés qui étaient sur le serveur : LLM, cloud, bases, proxies.
  2. Vérifiez la consommation de chaque clé au cours des dernières semaines chez les fournisseurs. Les statistiques sur l'identifiant proxy montreront immédiatement un trafic étranger.
  3. Déployez un nouveau serveur à partir d'une image propre, générez de nouvelles clés et ensuite seulement transférez les données, sans binaires ni fichiers cron de l'ancienne machine.

Où sont les proxies ici et ce qu'ils ne résolvent pas

Les proxies ne protègent pas le serveur contre CARBONATO. Le ver n'entre pas par vos requêtes sortantes, mais par un port entrant. Cependant, un bon schéma de travail avec des proxies réduit les dommages. Des identifiants distincts pour les tâches, des limites de trafic et une liaison par IP transforment une fuite de « tout le solde a disparu » en « j'ai révoqué un identifiant ».

Il y a aussi un revers que les botnets montrent régulièrement : des dispositifs capturés deviennent eux-mêmes des « proxies résidentiels » de réseaux douteux. C'est pourquoi pour le parsing, il est préférable de prendre du trafic auprès d'un fournisseur avec une origine claire du pool. Pour la plupart des tâches de collecte de données, des proxies résidentiels avec paiement par gigaoctet conviennent, où la consommation de chaque proxy est visible dans le tableau de bord. Pour les tâches de service sans protection anti-bot stricte, des proxies de centres de données moins chers suffisent.

Conclusion

CARBONATO n'utilise ni des vulnérabilités de jour zéro, ni des exploits astucieux. Il entre par la porte que les propriétaires des serveurs ont eux-mêmes ouverte : TCP 2375 sans mot de passe. Ce qui est nouveau, c'est qu'à l'intérieur, un agent IA travaille, chargé de récupérer en premier lieu les clés des modèles. Pour ceux qui collectent des données sur leurs VPS, la conclusion est pratique. Fermez l'API Docker, publiez les ports de service sur 127.0.0.1, répartissez les clés LLM et les proxies par tâches avec des limites de dépenses. Cela prend 15 minutes de travail, et après cela, votre serveur devient une cible peu intéressante pour de tels botnets.