Logiciel malveillant · · 28 min de lecture
Comment PoeLLM change un poème GitHub pour dire à ses victimes où appeler
PoeLLM entre dans des hôtes LiteLLM, Ollama, Gotenberg et Gitea exposés, puis lit quatre mots d’un poème GitHub pour trouver le serveur à appeler.

Le 7 octobre 2026, Black Lotus Labs, chez Lumen, a publié une campagne qu’ils ont nommée PoeLLM. Les serveurs infectés lisent un poème dans un fork GitHub, en tirent quatre mots placés à des endroits fixes, et transforment ces mots en adresse IPv4 du serveur de commande à appeler. On modifie le poème, et les machines se déplacent, sans nouveau binaire. Les mêmes hôtes minent de la cryptomonnaie et cherchent le prochain serveur à prendre.
Beelzebub Labs, à partir d’une seule requête qu’un pot de miel a conservée, a ensuite trouvé trois canaux de poèmes plus anciens, un historique qui commence en décembre 2025, et deux programmes d’accès à distance que le rapport de Lumen ne démonte pas. Ce texte suit les deux articles, les avis sur LiteLLM et Ollama, et les endroits où les comptes ne concordent pas. Les échantillons n’ont pas été exécutés ici.
Si vous ne faites pas tourner LiteLLM, Ollama, Gotenberg ou Gitea là où un réseau peut les joindre, la liste de chasse à la fin ne vous concerne pas. Le mécanisme vaut quand même la lecture : le poème est une boîte aux lettres morte, et une boîte aux lettres morte, ça se cherche.
Le mineur et le gestionnaire sont deux programmes
L’échantillon de Lumen est un ELF nommé libgcrypt. Il contient un shell distant, les mineurs XMRig et Iron, un scanner HTTP, et le code qui envoie l’exploit suivant. SHA-256 6fab94577364beec314afae3b082dd680933f08a8349b9f35b92667e8231b501. Dans un bac à sable public, il se signale sur le port TCP 9999 et mine vers Kryptex. Lumen a vu des victimes joindre 5.180.174[.]162 port 8029 et 46.21.245[.]211 port 7029. Le second héberge edge-ru-07.kryptex[.]network.
Les requêtes de livraison vues par Beelzebub n’installent pas ce fichier. Elles installent un gestionnaire. L’analyse statique des deux builds les plus récents, qu’ils appellent Bridge 3.0.7 et Sentinel 1.5.2, montre un client d’accès à distance et un superviseur de processus. Aucun des deux ne contient de code de minage. Aucun condensé de similarité ne relie libgcrypt à l’un ou à l’autre. Ils partagent une habitude, entre eux et avec le mineur : chacun écrit l’adresse qu’il vient de calculer dans un fichier, pour que le démarrage suivant n’ait pas à relire le poème. Le mineur utilise /tmp/.srvip. Bridge utilise un nom qui commence par bridge_srv_ip_.
C’est le fait pratique. Un hôte qui mine peut aussi faire tourner le superviseur. Effacer le mineur laisse l’accès de l’opérateur en place. Beelzebub dit clairement que la lecture statique montre ce que le code peut faire, pas quelles commandes ont tourné sur une machine donnée.
Ce que faisaient tourner les serveurs infectés
Après que Lumen a trouvé le premier serveur de commande, les données sur les machines qui l’appelaient revenaient comme LiteLLM, Ollama, Gotenberg et Gitea. LiteLLM est un proxy qui met plusieurs fournisseurs de modèles derrière une seule API. Ollama fait tourner des modèles sur une machine que vous administrez. Gotenberg convertit des documents en PDF. Gitea est un serveur Git. Les conclusions mentionnent aussi Ivanti Sentry comme cible possible. Le corps du texte est plus étroit. Ils enquêtaient déjà sur CVE-2026-10520, un défaut de la passerelle Sentry d’Ivanti, quand, début juin 2026, un Sentry compromis a contacté 5.78.73[.]122 puis s’est mis à scanner. Cette phrase décrit une victime qui s’est mise à scanner. Elle ne dit pas que le code d’exploit de PoeLLM vise Sentry.
Le scan s’est concentré sur les ports 3000 et 4000. Lumen les identifie comme les ports d’écoute par défaut de Gotenberg et de LiteLLM. Le guide d’installation de Gotenberg, que Lumen reproduit, dit aux opérateurs de ne pas exposer le service à Internet. Des centaines de serveurs Gotenberg et Gitea figuraient quand même parmi les victimes. Lumen ne nomme pas de CVE pour l’un ou l’autre produit. Le facteur commun publié est que le service répondait depuis Internet.
Les victimes se regroupaient aux États-Unis et en Europe de l’Ouest. La lecture de Lumen, c’est l’adoption : c’est là que ces outils sont laissés face au réseau. Ils disent aussi que le GPU derrière une charge d’IA fait partie de l’attrait pour un mineur, et que les mêmes serveurs peuvent contenir des invites, des clés, et ce à quoi l’application avait accès. La campagne qu’ils ont mesurée, c’est du minage et du scan. Les données sur ces hôtes sont la raison pour laquelle l’accès compte encore après les pièces.
La route LiteLLM est un test de connexion qui lance un programme
CVE-2026-42271, c’est GHSA-v4p8-mg3p-g94g, publié le 21 avril 2026. Les versions touchées vont de 1.74.2 jusqu’à 1.83.7, sans l’inclure. L’enregistrement CVE le note CVSS 4.0 à 8,7 :
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:N/SA:N
Réseau, complexité faible, une condition préalable, privilèges faibles, aucun clic. Impact élevé sur la confidentialité, l’intégrité et la disponibilité du proxy, et un impact élevé de confidentialité noté sur un système suivant. La faiblesse est CWE-78 : une commande du système d’exploitation qui contient du texte fourni par l’appelant.
Deux routes existent pour qu’un opérateur prévisualise un serveur MCP avant de l’enregistrer. MCP, c’est le Model Context Protocol. POST /mcp-rest/test/connection et POST /mcp-rest/test/tools/list acceptaient une configuration complète, y compris command, args et env. Un serveur qui utilise le transport stdio est un programme local, pas une URL. Le prévisualiser, c’est lancer ce programme comme sous-processus du proxy, avec les privilèges du proxy.
L’avis dit que les routes vérifiaient une clé d’API de proxy valide et ne vérifiaient pas le rôle de la clé. Un utilisateur interne à faibles privilèges pouvait lancer une commande. Le PR:L du vecteur, c’est cette clé. AT:P note une condition préalable. La condition de l’avis, c’est un proxy dans cette plage de versions, joint par quelqu’un qui détient déjà une clé.
La version 1.83.7, que Horizon3 date du 8 mai 2026, exige le rôle PROXY_ADMIN sur les deux routes. Les notes du module Rapid7 ajoutent un second contrôle qu’ils attribuent à cette version : la commande stdio est limitée à une liste d’interpréteurs (deno, docker, node, npx, python, python3, uvx), et une autre commande est refusée même pour un administrateur. L’avis GitHub ne mentionne pas cette liste. Le changement de rôle est l’énoncé du fournisseur. La liste est la lecture du correctif par Rapid7. Rapid7 note aussi que le processus lancé n’est pas un serveur MCP auquel la route peut parler, donc la réponse HTTP est un test de connexion échoué après que la commande a déjà démarré. Un défenseur qui ne surveille qu’un code 200 passera à côté.
Une clé reste une clé. Horizon3 a publié un enchaînement avec CVE-2026-48710, le bogue Starlette que X41 appelle BadHost. Starlette avant 1.0.1 construit request.url en collant l’en-tête Host au chemin. Le routage utilise le chemin de la ligne de requête. Un code qui demande « ce chemin exige-t-il une connexion ? » en lisant request.url.path peut voir un chemin différent de celui qui s’exécute. L’aide à l’authentification de LiteLLM faisait cela, dans get_request_route(). Horizon3 dit avoir confirmé le 1er juin 2026 que l’enchaînement atteint les routes de test MCP sans clé, sur LiteLLM 1.74.2 à 1.83.6, quand l’arbre de dépendances contient Starlette 1.0.0 ou plus ancien.
La note de LiteLLM est à part. Ils disent que le changement qui cesse de dériver la route de l’URL construite avec Host est livré dans 1.84.0, avec un durcissement supplémentaire du traitement des chemins reporté sur 1.84.3, 1.85.2, 1.86.2 et 1.83.10-stable.patch.3. Le SDK Python est en dehors. Un proxy devant qui refuse un Host mal formé réduit la chance, et LiteLLM dit que le blocage complet dépend de ce qui est devant. Le conseil d’Horizon3 de passer à 1.83.7 et le conseil de LiteLLM de prendre une version qui nomme le correctif d’en-tête Host sont tous les deux publiés. Fermer les deux trous, c’est une version de la liste Host de LiteLLM, donc aussi au-delà de 1.83.7.
Le pot de miel de Beelzebub a enregistré POST /mcp-rest/test/connection à partir du 18 juillet. Un pot de miel conserve la requête qu’on lui a envoyée. Il ne dit pas si une clé était jointe, ni si l’en-tête Host avait la forme BadHost. À partir du 14 août, le corps est une configuration MCP dont le serveur s’appelle system-monitor. La commande qu’elle nomme télécharge un programme appelé manager depuis le port TCP 81 et le démarre. Le 20 août, le chargeur est enveloppé pour que la commande visible soit un court décodeur. Le 24 août, il choisit un build x86-64 ou AArch64. Si le premier enregistrement-et-lancement échoue, il essaie un second répertoire, et si celui-là échoue il exécute les octets depuis un fichier mémoire anonyme. Cette dernière étape est celle qui fonctionne encore quand /tmp est monté de façon à interdire l’exécution, et une tentative ratée peut laisser le fichier. Les corps de requête sont dans l’article de Beelzebub. Le chemin, le port, le nom du serveur et le nom du programme sont ce qu’une recherche dans les journaux doit viser.
Lumen avait déjà relié un échantillon à la même route. Le condensé libgcrypt ci-dessus contient un POST vers /mcp-rest/test/connection. Des URL de téléchargement sur le port 81, chemins /private/python3.6 et /private/bins.sh, sont apparues d’abord sur 5.78.73[.]122 puis sur 120.224.114[.]212. Les condensés de Beelzebub alignent les noms sur des builds : bins.sh comme chargeur Bridge (169bb98a3e27c68ccb8cefbabf4d4211c258729b2825aa1e17d36f13b7166c6b), et un fichier qu’ils identifient comme Bridge 3.0.7 sous le nom python3.6 (3af9f25a4d45bb4f1ec5627cdbc6703cf3b4be75a892162d299d80ddfb266f42).
La découverte, dans les mêmes journaux, est plus ancienne que la livraison. À partir du 24 juin, des clients qui se présentaient comme axios/1.14.0 demandaient /v1/models puis /openapi.json. Beelzebub compte 48 adresses de ce type en juin. En août, 16 des 29 requêtes de livraison sont arrivées moins de dix minutes après que les deux requêtes de découverte avaient touché le même pot de miel. Trois sources de livraison de juillet avaient interrogé /api/version d’un pot de miel Ollama entre trois et 27 heures plus tôt. Trois hôtes de commande ou de préparation ont eux-mêmes envoyé la découverte axios.
Trois façons de cacher une adresse dans un poème
Une boîte aux lettres morte, ici, est une page publique que les deux côtés peuvent lire, pour que l’adresse du serveur de commande n’ait pas à vivre dans le binaire. Le programme va chercher la page, l’analyse, et se connecte. L’opérateur modifie la page. GitHub l’héberge. Les projets en amont n’ont pas été modifiés. L’opérateur les a forkés, a ajouté un fichier, puis a réécrit ce fichier dans l’éditeur web de GitHub.
Lumen a suivi un fichier. Beelzebub en a suivi trois autres. Les encodages ne sont pas le même poème avec les noms changés. Ce sont trois générations.
Des nombres collés aux mots
Beelzebub date le premier couplet du 9 décembre 2025, dans un fork de discord.js. Certains mots portent des entiers. Lus dans l’ordre place, ghost, verb, object, ces entiers sont les quatre octets. Leur exemple se décode en 194.69.203[.]32. Les lignes suivantes du même couplet répètent les mêmes quatre nombres. De ce jour au 17 juin 2026, ils comptent 39 couplets numérotés qui encodent 31 adresses. Vingt de ces adresses sont sorties avant le 13 avril, le jour où le poème de Lumen est commité. Le 13 avril même, le canal discord.js a publié un autre couplet.
Ils pointent des enregistrements publics du 6 décembre, trois jours avant le couplet : URLhaus, MalwareBazaar et VirusTotal ont journalisé 194.69.203[.]32 port 81 servant un fichier nommé colonna. Ces flux n’ont pas été reconsultés pour ce texte. La corroboration est la leur. colonna est le programme qu’ils décrivent comme un Mirai modifié, et il se signale sur le TCP 3778. Un script de 446 octets, SHA-256 c9d5637305f9638f37023fe625d8c5e825de3ede0c2b4ffb8c0f902bd476da21, reçu par VirusTotal le 15 février, va chercher colonna sur 191.37.28[.]163 port 81. Cette adresse est dans le même /24 que 191.37.28[.]160, le premier serveur de commande que Lumen a décodé du poème d’avril. Dans le bac à sable, colonna s’est ensuite signalé sur le port 3778 et a récupéré le canal discord.js à travers un relais.
Bridge analyse encore ce couplet. Beelzebub cite deux chaînes de format dans le binaire : The %63s %63s the %63s et %63[^,], %63s and deep,. La seconde est la deuxième ligne du couplet, avec les mots numérotés remplacés par des emplacements. %63s lit un jeton d’au plus 63 caractères. %63[^,] lit jusqu’à 63 caractères qui ne sont pas une virgule. L’analyseur enregistre l’adresse et se reconnecte quand le poème change.
Quatre mots, un dictionnaire, une adresse
Le canal de Lumen est un fork des sources du site nodejs.org, compte GitHub ejejejdfbbebe, fichier dash.css. Ce nom de fichier n’est pas dans l’arbre en amont, ce qui le fait ressortir dans un diff contre le site de Node. Le poème s’intitule On the Nature of Connection. Le premier commit date du 13 avril 2026. Lumen a compté 11 modifications. À travers ces modifications, l’échafaudage reste, et quatre mots bougent. Un diff du fichier, c’est la rotation.
La fonction qu’ils nomment extract_poem_phrase_field prend trois extraits, sans tenir compte de la casse :
- le texte entre
In the silent hum ofet la virgule suivante - le texte entre
each pulse ofetthreading - le texte entre
Beyond the wall ofet la virgule suivante
Un quatrième extrait, qu’ils placent aux décalages 0x44a8db à 0x44a99b, cherche of distant servers, recule jusqu’au mot précédent, et ne garde ce mot que si les quatre octets devant sont the .
Chaque mot est cherché dans un dictionnaire compilé dans l’échantillon. Les quatre nombres sont les quatre octets, dans cet ordre. L’exemple travaillé de Lumen, à partir d’un échantillon qu’ils datent du 23 juin, est driver = 92, diode = 119, decryption = 165, string = 74, soit 92.119.165[.]74. Cette adresse est sur leur liste du 1er juillet au 23 juillet. L’échantillon du 23 juin tenait le dictionnaire. Un poème plus tard l’a dépensé. Le couplet qu’ils impriment comme texte de septembre utilise d’autres mots dans les mêmes emplacements : driver, diode, encryption et tick. La légende sous le décodage de juin appelle ce décodage le poème courant. La prose appelle 92.119.165[.]74 une adresse précédente. La prose colle aux dates. La légende, non.
Lumen dit que les ancres n’avaient pas changé sur les 11 modifications, seulement les mots clés. Beelzebub appelle cela la deuxième génération : les chiffres ont disparu, et chaque mot clé vaut encore un octet. Ils ne publient pas le dictionnaire. Ce texte non plus. Les quatre ancres et un décodage historique suffisent à voir le mécanisme. Ils ne suffisent pas à transformer un couplet ultérieur quelconque en adresse, et c’est pour cela que le dictionnaire reste dans le binaire.
Un couplet dont le sens est deux tables
Sentinel lit un couplet de quatre lignes. Chaque ligne associe une phrase d’ouverture et une fin. Chaque moitié est tirée d’une table de 16 entrées. Beelzebub donne la combinaison octet = (opening_index << 4) | ending_index. Une ligne qu’ils marquent comme inventée, « the lighthouse rests at dawn. », avec l’ouverture 12 et la fin 0, encode 192. Les vraies lignes se lisent comme des vers sur des lunes, des rivières, des moineaux et des lanternes. Sans les deux tables, le couplet ne donne pas d’adresse. Ils ont compté huit couplets entre le 16 juin et le 27 août, nommant sept adresses de commande. Le contrôle du décodeur, c’est le recouvrement de l’opérateur lui-même : la même adresse est apparue en vers numéroté le 17 juin et en couplet le 18 juin.
L’analyseur accepte aussi une adresse pointée ordinaire ou une forme numérique plus simple, et il vérifie le résultat avec inet_pton avant de se connecter. Le poème est le chemin qui survit au retrait d’un serveur. Une adresse littérale fonctionne encore quand l’opérateur veut que les machines bougent tout de suite.
Sentinel va chercher chaque boîte aux lettres sur GitHub directement, puis à travers cinq relais sur le port TCP 1458, HTTPS avant HTTP, avec la vérification des certificats coupée. Trois passes utilisent des délais de 2, 5 et 30 secondes. Il dort un intervalle aléatoire sous une demi-seconde avant chaque requête et fait tourner quatre chaînes User-Agent de navigateur. Deux de ces chaînes sont mal formées. L’une prétend Edge/124.0. L’Edge que le navigateur envoie vraiment est Edg/. Un journal de proxy qui voit Edge/ et une URL raw GitHub dans la même requête, ce n’est pas une personne qui lit un poème.
Bridge va chercher les trois mêmes dépôts dans l’autre ordre : relais d’abord, puis direct. Les deux builds rangent la liste des dépôts et la liste des relais dans une configuration XOR avec l’octet 0x5A. Certaines chaînes de Sentinel sont assemblées sur la pile à l’exécution, donc elles sont absentes du fichier tant qu’il ne tourne pas. Le build Sentinel empaqueté est de l’UPX 4.21. Les chaînes des règles sur le binaire dépaqueté existent après upx -d. Beelzebub publie les règles YARA et Sigma sous la Detection Rule License 1.1. Les condensés qu’ils attachent aux builds sont plus bas.
Quatre forks, et les projets en amont n’ont pas changé
Les deux builds du gestionnaire portent les trois mêmes emplacements. Le tableau de Beelzebub :
| Parent | Compte du fork | Fichier ajouté | Commits | Fenêtre |
|---|---|---|---|---|
discordjs/discord.js | franchgamer34 | packages/api-extractor-model/src/items/NewAPi.readme | 30 | 9 déc. 2025 au 27 août 2026 |
wszqkzqk/deepin-wine-ubuntu | alessio756 | deepin-wine-plugin-virtual_1.0deepin1_all/DEBIAN/control.readme | 12 | 7 mars au 27 août 2026 |
getumbrel/umbrel | francoryt3 | packages/umbreld/source/modules/blacklist-uas/blacklist-uas.readme | 6 | 12 mars au 19 août 2026 |
Le fork dash.css de Lumen est le quatrième. Notez le P majuscule dans NewAPi.readme. Les quatre comptes ont depuis été supprimés. Les commits d’un fork supprimé peuvent rester lisibles à travers le dépôt du projet parent, parce que GitHub montre les commits d’un fork sous l’URL du parent. Beelzebub dit que les 48 commits qu’ils ont gardés répondaient encore le 12 octobre, et que leur copie de l’historique s’arrête le 2 septembre, le dernier jour où ils pouvaient lister les commits. Ces URL ne sont pas le signe que discord.js, Umbrel ou deepin-wine ont changé. Bloquer le projet en amont parce qu’un fork a ajouté un readme, c’est bloquer la mauvaise chose.
Les trois comptes de fork ont été créés à moins de 14 minutes d’intervalle, et les mêmes vers apparaissent dans plus d’un dépôt. Cinq des douze adresses de commande de Lumen apparaissent aussi dans ces canaux. Trois collent au premier jour vu par Lumen. 120.224.114[.]212 est dans un poème le 7 mai et dans la télémétrie de Lumen le 9 mai. 103.249.201[.]108 est dans la télémétrie de Lumen le 13 août et dans un poème le 19 août.
Ensemble, les trois canaux nomment 37 adresses de commande, dont 32 absentes des douze de Lumen. Certaines montrent l’opérateur qui revient aux mêmes réseaux pendant des mois. Le couplet de février qui nomme 191.37.28[.]163 est à côté du serveur d’avril de Lumen, 191.37.28[.]160. 101.47.160[.]163, nommé dans un poème en avril, a scanné les pots de miel de Beelzebub en juin et a servi d’hôte de préparation dans une requête de livraison en septembre.
Quatre des sept adresses nommées par des couplets faisaient déjà tourner un service d’IA, dans l’historique de scan et de DNS que cite Beelzebub, avant qu’un poème les nomme : LiteLLM, n8n, ComfyUI ou Jupyter. L’un portait un nom d’hôte n8n depuis le 20 mai et faisait tourner LiteLLM depuis le 17 juillet. Un poème l’a nommé le 22 juillet, et le port 81 est apparu trois jours plus tard. Ils disent ne pas pouvoir dire si l’opérateur a compromis ces serveurs ou s’il les a montés. L’observation séparée de Lumen, c’est que plusieurs serveurs de commande étaient des routeurs avec une page d’administration vulnérable. Le premier, 191.37.28[.]160, géolocalisé au Brésil, exposait un serveur web Boa sur le port 2222. Cette page était vulnérable à CVE-2018-21027 et CVE-2018-21028. Lumen n’a pas trouvé de preuve directe que ces deux-là ont servi. La même forme est revenue, y compris sur 120.224.114[.]212 en Chine. Leur évaluation : l’opérateur a continué d’utiliser des routeurs compromis comme serveurs de commande, plutôt que de louer des machines pour ce rôle.
Ce que le gestionnaire peut faire une fois qu’il a une adresse
Bridge 3.0.7 est un binaire statique x86-64 construit sur Alpine avec musl, lié à mbedTLS 2.28.10. Son canal de commande est le port TCP 5001, et il parle aussi UDP. Les noms de répartiteur que Beelzebub a retrouvés sont SHELL_CMD, SUBNET_SCAN, TCP_TUNNEL_OPEN et HTTP_REQUEST, plus un transfert de fichier qui peut reprendre après une coupure. Rien de ce qu’il scanne, et rien vers quoi il relaie, n’est figé dans le binaire. Le contrôleur fournit la cible. C’est pour cela qu’une victime devient un scanner sans nouveau poème : le poème trouve le contrôleur, et le contrôleur nomme le port suivant et l’adresse suivante.
Sentinel 1.5.2 est le superviseur. Le build qu’ils ont dépaqueté est AArch64, empaqueté UPX, le même Alpine et musl et le même mbedTLS. SHA-256 empaqueté 0ce90d89c1b4258b14f541c9add80ff13760590f4cf6bf338e22c2258ffe9eca. Dépaqueté, f30bf5814c265ab9bb68ebaacd2793db560f68a24d4591e73802f30418cd1751. Un build 1.5.1 antérieur est attribué à un rapport de Caronte, empaqueté d4629c28b9a505f6ea65966a3def61e1f6a218f67da1a7afb27d50b95cb73350. Le port de commande par défaut est le TCP 5002. Chaque côté ouvre avec quatre octets aléatoires. Sentinel mélange les deux nonces avec un secret de 16 octets tiré du binaire, en deux clés RC4, une par direction. Un enregistrement des octets ne se lit pas à partir de l’enregistrement seul. Il faut le secret et les deux nonces du début de cette session. Dans le flux, Sentinel s’enregistre avec une trame de 134 octets : magie 0x5EC4, son identifiant de processus, le --id que le chargeur a passé, et le nom d’hôte. Les commandes suivantes sont du texte séparé par des deux-points.
Il supervise jusqu’à 16 processus. Chacun est étiqueté MANAGED_BY_SENTINEL, reçoit un journal de 1 000 lignes, et est noté comme terminé, planté, ou tué par autre chose sur l’hôte. L’opérateur peut ajouter, tuer et redémarrer ces processus, lancer une commande shell, pousser un fichier, remplacer Sentinel lui-même, le pointer vers un nouveau serveur, ou lui dire d’effacer ses propres traces. Le mineur peut être l’un des 16. Il s’installe sous l’un de 16 noms empruntés à de vrais démons, dont avahi-daemon et packagekit, dans des répertoires comme /usr/libexec et /var/lib/dbus. Le binaire contient aussi des chaînes pour injecter un lanceur dans un python3 déjà en cours. Beelzebub dit que ce chemin n’a pas été tracé.
Le fichier d’unité est la partie qui fait de systemctl stop un mauvais test. L’unité de Sentinel règle KillSignal à SIGCONT et SendSIGKILL à no. Arrêter l’unité livre un signal qui ne termine pas un processus en cours, et l’unité ne suit pas avec SIGKILL. Une entrée cron @reboot et un contrôle pgrep toutes les cinq minutes, tous deux étiquetés #snt-, le relancent. chattr +i marque le binaire immuable, donc un effacement échoue tant que ce drapeau n’est pas retiré. L’ensemble de Bridge, c’est une unité systemd, un redémarrage cron toutes les cinq minutes si systemctl is-active échoue, un crochet PostgreSQL archive_command, un fragment de shell dans /etc/profile.d/dbus-mgr.sh, et le même drapeau immuable. Fichiers dont les noms comptent : /tmp/.snt.lck, /tmp/.snt_x_*, /tmp/.bce_*.
Le 16 juin, le canal Umbrel a publié 178.128.14[.]204 comme serveur Sentinel. Les 17 et 18 juin, le mineur, dans un bac à sable public, s’est connecté à cette même adresse. C’est la jointure publiée entre les canaux de poèmes et le programme que Lumen a analysé.
La propagation propre du mineur, dans ces deux exécutions de bac à sable, portait sur environ 300 hôtes au hasard sur le port 4000, et la requête de test MCP envoyée à 14 d’entre eux. Il s’installe comme un fichier nu nommé libgcrypt sous ~/.local/share/.syslib/. La vraie bibliothèque est livrée comme libgcrypt.so avec un suffixe de version. Un fichier dont le nom est exactement libgcrypt est celui qu’utilise cette campagne.
| SHA-256 | Ce que Beelzebub dit que c’est |
|---|---|
7ff0414dd57c9811a3711f3f12a66a611dd0adb00925b44cd728bc81e36e07fa | colonna.test, x86-64 statique |
c9d5637305f9638f37023fe625d8c5e825de3ede0c2b4ffb8c0f902bd476da21 | téléchargeur colonna, 446 octets |
169bb98a3e27c68ccb8cefbabf4d4211c258729b2825aa1e17d36f13b7166c6b | chargeur Bridge bins.sh |
3af9f25a4d45bb4f1ec5627cdbc6703cf3b4be75a892162d299d80ddfb266f42 | Bridge 3.0.7 |
7858164459843fd1d708f1ca604cf5e795501a4c881160755b869e2835e04f5d | un manager antérieur, x86-64, UPX |
0ce90d89c1b4258b14f541c9add80ff13760590f4cf6bf338e22c2258ffe9eca | Sentinel 1.5.2, AArch64 empaqueté |
6fab94577364beec314afae3b082dd680933f08a8349b9f35b92667e8231b501 | le mineur libgcrypt de Lumen |
Quelle taille, et quelle phrase vous citez
Les conclusions de Lumen disent que le logiciel malveillant a touché plus de 3 400 serveurs victimes, avec un pic au-dessus de 800 actifs dans une journée. Le corps du même article dit qu’au pic de la mi-juin la campagne impliquait presque 2 200 serveurs touchés, avec près de 800 actifs par jour, et plus loin que depuis avril elle a touché presque 2 200. La légende du graphique mentionne des pics fin juin et début octobre, sur un graphique étiqueté d’avril à septembre. BleepingComputer a utilisé « plus de 3 400 » et « jusqu’à 800 », et « au moins 11 » serveurs de commande. La liste de Lumen en a 12. CyberScoop a utilisé « plus de 3 400 » et « au moins une douzaine » de modifications du poème, contre le compte de Lumen de 11 modifications après le premier commit. Beelzebub a cité le « presque 2 200 » de la mi-juin.
Les deux totaux de victimes sont dans l’article de Lumen. L’article ne dit pas comment 3 400 et 2 200 ont été comptés. « Plus de 3 400 » est la conclusion. « Presque 2 200 » est ce que le récit utilise pour le total de la mi-juin et, quelques sections plus loin, pour la campagne depuis avril. Le pic de 800 par jour est le chiffre que les deux sections partagent. Citez la phrase que vous voulez dire.
Ils listent 12 adresses de commande et disent que trois étaient encore actives à la publication :
92.119.164[.]50, vue d’abord le 23 juillet 2026103.249.201[.]108, vue d’abord le 13 août 2026178.128.14[.]204, vue d’abord le 16 juin 2026
Les autres, avec les fenêtres qu’ils donnent : 191.37.28[.]160 (13 avril au 16 mai), 89.39.253[.]46 (14 avril au 24 juin), 120.224.114[.]212 (9 mai au 8 septembre), 5.78.73[.]122 (8 juin au 22 août), 15.204.178[.]28 (14 juin au 17 août), 92.119.165[.]74 (1er juillet au 23 juillet), 45.133.73[.]28 (18 juillet au 22 juillet), 185.132.53[.]158 (19 juillet au 12 septembre), 136.148.69[.]233 (12 septembre au 25 septembre).
Les 32 adresses supplémentaires de Beelzebub sont dans leur article. La date d’un poème est le moment où le vers a nommé l’adresse, pas une mesure de la durée d’usage, et certaines adresses peuvent appartenir à des tiers qui ont depuis nettoyé. Cherchez-les dans les anciens journaux. Confirmez le trafic actuel avant un blocage. Lumen dit avoir bloqué le trafic vers et depuis les serveurs de commande qu’ils avaient identifiés, sur leur dorsale, et que les clients Lumen Defender étaient couverts depuis la découverte du logiciel.
Les requêtes de livraison dans les pots de miel de Beelzebub venaient de 66 adresses sources entre le 18 juillet et le 30 septembre. Ils ont retenu ces sources, et les identifiants de cible qui auraient identifié les capteurs.
Les commentaires en italien, et de quoi ils sont la preuve
Environ une heure après le commit du 13 avril, le routeur brésilien a contacté 57.131.5[.]211 port 80, géolocalisé en Italie. Cet hôte sert malwarescan[.]xyz, enregistré en février 2026. Lumen n’a pas vu d’autres clients notables de ce point d’accès et estime que l’opérateur peut le faire tourner. Des connexions entrantes vers le port 81 du routeur sont ensuite arrivées de sorties Tor et de quelques serveurs dédiés. Le port 81 est l’endroit où l’on dit aux victimes de télécharger le fichier suivant, donc ils lisent ces flux comme des essais du chemin de téléchargement.
L’échantillon qu’ils ont examiné contient des commentaires en italien. Les commentaires sont dans une figure, pas transcrits dans le texte récupéré pour cet article, donc ils ne sont pas cités ici. À partir de mai, des serveurs de commande gardaient aussi une connexion vers 185.119.19[.]171, aussi en Italie, qui avait hébergé Prometheus et Uptime Kuma. 5.78.73[.]122 et 178.128.14[.]204 s’y connectaient aussi. La confiance de Lumen que cette machine est l’interface d’administration est modérée, et ils le disent : la langue, le netflow, et ces tableaux de bord.
Plus tard, des groupes de victimes ont sondé SSH et d’autres pages de connexion. Beaucoup des cibles étaient des serveurs dédiés en Italie. Lumen lit cela comme une fonction de force brute distribuée encore à l’essai, et ils disent que sa maturité est incertaine. Les noms de comptes de fork de Beelzebub collent à un opérateur italophone. Les noms de comptes ne coûtent rien. Les commentaires et les deux serveurs italiens sont la preuve que Lumen pèse vraiment. « Italophone » est leur évaluation. Ce n’est pas un nom.
Des tirages Ollama, neuf jours avant le rapport
Le POST /api/pull d’Ollama va chercher un modèle sur le registre que le corps nomme, y compris en HTTP simple quand insecure vaut true. La route n’exige pas de connexion.
Du 28 septembre au 5 octobre, les pots de miel Ollama de Beelzebub ont reçu 94 tirages de ce type depuis 67 adresses, vers trois registres sur le port TCP 9882. Un nom de modèle, gate/tensorgate, était le même pour chaque cible. Les autres étaient r-orig- plus un identifiant, puis usr ou usrlocal. Ces deux derniers segments sont les préfixes d’installation d’Ollama. Chaque pot de miel a gardé un identifiant pendant huit jours, à travers les trois registres et chaque adresse source. C’est à ça que ressemble une liste de cibles. Les trois registres étaient déjà apparus comme hôtes de préparation sur le port 81 dans les requêtes de livraison LiteLLM. L’un d’eux, 103.249.201[.]108, est sur la liste de serveurs de commande de Lumen. Les deux autres qu’ils nomment sont 62.31.247[.]107 et 76.13.49[.]213. À partir du 4 octobre, un POST /api/generate pour un modèle nommé does-not-exist-zz arrivait en premier. Le nom est une clé de chasse.
Le 8 octobre, CERT Polska a publié CVE-2026-103663. Le /api/pull d’Ollama passe les condensés de couches à digestToPath sans assez de contrôle (CWE-23, traversée de chemin relative). Un client non authentifié peut fournir un condensé qui sort du magasin de modèles, et la couche est écrite ailleurs. Si le processus peut écrire /usr/lib/ollama, ce que CERT Polska dit être le défaut dans la plupart des images Docker Ollama, le fichier atterrit là où le serveur le charge au redémarrage suivant, et ce chargement tourne en root. Le tableau de l’avis dit des versions de 0.34.2 à 0.35.0. Le même avis dit que le problème a été corrigé dans 0.35.0. Le texte de CVE qui circule suit la phrase du correctif : 0.35.0 est la version corrigée. Beelzebub dit aux opérateurs de passer à 0.35.0 ou plus récent. La borne haute du tableau est la ligne qui ne colle pas à la ligne du correctif. Confirmez le binaire, et traitez 0.35.0 comme la première version que la prose appelle corrigée.
Les pots de miel de Beelzebub ont refusé les tirages, donc les couches n’ont jamais été stockées. Ils disent que les requêtes, à elles seules, ne montrent pas si CVE-2026-103663 était le but. Le calendrier est à côté de la divulgation. Il n’identifie pas le bogue.
Une chronologie que les articles soutiennent
- 6 décembre 2025. Beelzebub : des flux publics journalisent
colonnaservi depuis194.69.203[.]32port 81. - 9 décembre 2025. Premier vers numéroté, dans le fork
discord.js. - 15 février 2026. Le téléchargeur de 446 octets, visant
191.37.28[.]163. - Février 2026.
malwarescan[.]xyzest enregistré, selon Lumen. - 7 et 12 mars. Les fichiers de poème deepin-wine et Umbrel commencent.
- 13 avril.
dash.cssest commité. Lumen décode191.37.28[.]160. Environ une heure plus tard, cette adresse contacte l’hôte italien. Le canaldiscord.jspublie un autre vers le même jour. - 21 avril. GHSA-v4p8-mg3p-g94g.
- 8 mai. LiteLLM 1.83.7, selon la datation d’Horizon3.
- Mai. Lumen : le scan s’élargit, surtout contre LiteLLM et Gotenberg. Le poème nomme
120.224.114[.]212le 7 mai. La télémétrie le voit le 9 mai. - 26 mai. Textes publics sur CVE-2026-48710. Horizon3 date la confirmation enchaînée du 1er juin.
- Début juin. Un Ivanti Sentry compromis contacte
5.78.73[.]122et se met à scanner. C’est l’entrée de Lumen dans la campagne. - 16 au 18 juin. Le poème Umbrel et la session de bac à sable du mineur se rejoignent sur
178.128.14[.]204. Le format en couplet commence. La mi-juin est le pic du récit de Lumen. - 18 juillet. Première requête de livraison sur le pot de miel LiteLLM de Beelzebub.
- 14, 20 et 24 août. La requête gagne le nom
system-monitor, un court décodeur autour du chargeur, et un choix d’architecture. - 28 septembre au 5 octobre. Les tirages Ollama.
- 7 octobre. Le rapport de Lumen, selon la date de Beelzebub. Le 7 octobre 2026 était un mercredi, ce qui colle au jour de la semaine que CyberScoop a utilisé pour le rapport. La page de Lumen, telle que récupérée pour ce texte, n’imprime pas de date dans le corps de l’article.
- 8 octobre. CERT Polska sur CVE-2026-103663.
- 12 octobre. Beelzebub : les commits de poèmes conservés répondaient encore.
- 13 octobre. L’article de Beelzebub.
Si l’un de ces services est sur une machine que vous faites tourner
- Un proxy LiteLLM plus ancien que 1.83.7, joignable depuis un réseau, lancera un programme pour le détenteur d’une clé ordinaire. Prenez aussi une version que LiteLLM liste pour le correctif d’en-tête
Host: 1.84.0 ou plus récent, ou un report qu’ils nomment, dont1.83.10-stable.patch.3, 1.84.3, 1.85.2 et 1.86.2. Si la mise à jour attend, refusezPOST /mcp-rest/test/connectionetPOST /mcp-rest/test/tools/listau proxy devant. C’est le contournement indiqué dans l’avis. Une clé incrustée dans une application n’est pas une raison de publier le proxy. - Un Ollama joignable depuis un réseau auquel vous ne faites pas confiance devrait être en 0.35.0 ou plus récent. La route de tirage n’a pas de connexion. La condition de CERT Polska pour une exécution en root est un processus qui peut écrire
/usr/lib/ollama. Retirer cette écriture retire l’étape du redémarrage sur une version que vous n’avez pas encore mise à jour. La mise à jour reste le correctif de la traversée. - Lumen ne publie pas de correctif propre à PoeLLM pour Gotenberg ou Gitea. Les deux étaient dans l’ensemble des victimes parce qu’ils répondaient depuis Internet. Le guide d’installation de Gotenberg le dit déjà. Les ports 3000 et 4000 ouverts au monde étaient le scan.
- Mettre à jour ne retire pas un programme déjà installé. Cherchez un fichier nu nommé
libgcrypt, un processus démarré avec un identifiant qui commence parllrig-et un argument de port 10, et des exécutables cachés sous/tmpou/dev/shmdont les noms commencent par un point. Cherchez/tmp/.srvip, des fichiers nommésbridge_srv_ip_,/etc/profile.d/dbus-mgr.sh, des lignes crontab étiquetées#snt-, et une unité systemd dontKillSignalvautSIGCONT.MANAGED_BY_SENTINELdans l’environnement d’un processus est l’étiquette du superviseur. Retirez le drapeau immuable avant d’effacer le binaire, et retirez l’unité, les lignes cron et le crochet shell dans le même passage. Un mineur disparu, avec le superviseur encore en place, c’est l’échec que cette campagne est construite pour survivre. - Dans les journaux d’accès, un POST vers l’une ou l’autre route de test MCP dont le corps définit un serveur, surtout un nommé
system-monitor, est la livraison que Beelzebub a enregistrée.GET /v1/modelspuis/openapi.jsond’un client qui s’appelleaxios/1.14.0est la passe de découverte. Sur Ollama, un tirage avecinsecure, vers le port 9882, nommégate/tensorgateour-orig-, est l’étape de septembre. Les journaux qui jettent le corps ne montreront ni le nom du modèle ni le nom du serveur. Un hit est une raison de faire la vérification des fichiers. Il ne prouve pas, à lui seul, que la commande a tourné. - Les ports de commande dans les deux rapports sont 3778, 5001, 5002 et 9999. La préparation est le TCP 81. Les relais de poème utilisent 1458. Les registres Ollama qu’ils nomment utilisent 9882. Un processus serveur qui va chercher encore et encore le même fichier raw GitHub, c’est la boîte aux lettres morte. Le dépôt en amont n’est pas l’indicateur. Le fichier ajouté dans le fork l’est.
- Si un gestionnaire ou un shell a tourné avec l’utilisateur du proxy, renouvelez les secrets que cet utilisateur pouvait lire. Un proxy LiteLLM détient des clés de fournisseurs. Un hôte Ollama détient des modèles et souvent tout ce qui était monté dans ce conteneur. Le temps de minage sur un GPU est la moitié bruyante. La moitié silencieuse est le superviseur, et les clés.
Sources
- Black Lotus Labs, Canto incognito : tracking the PoeLLM malware, 7 octobre 2026 selon la datation de Beelzebub.
- Beelzebub Labs, Zachary Gardner, Reading the Rest of the Poem, 13 octobre 2026.
- BerriAI, GHSA-v4p8-mg3p-g94g, 21 avril 2026, et la note sur l’en-tête Host.
- CVE-2026-42271.
- Horizon3, CVE-2026-42271 enchaîné avec CVE-2026-48710.
- X41, BadHost, 7 octobre 2026.
- CERT Polska, CVE-2026-103663, 8 octobre 2026.
- CyberScoop, PoeLLM malware has assembled a sweeping botnet.
- BleepingComputer, PoeLLM malware infects exposed AI servers.
- Rapid7, notes du module sur le point de test MCP de LiteLLM.
Les nouveaux articles, par courriel.
Environ une fois par mois. Des notes sur la vie privée en termes simples, rien d’autre. Je ne partage jamais votre adresse.