flawopen.com/Teardowns/cve-2026-105697-langflow-mcp-stdio-command-injection
CVE-2026-105697 : injection de commandes MCP dans Langflow
Avant la version 1.10.3, Langflow transmettait la configuration MCP stdio à un processus lancé par un shell. Sur une instance exposée avec LANGFLOW_AUTO_LOGIN=true, un attaquant pouvait obtenir un jeton sans identifiants ; avec la connexion automatique désactivée, un utilisateur authentifié non administrateur pouvait encore atteindre l’exécution de commandes sur l’hôte. La version 1.10.3 valide tous les chemins, juste avant le lancement compris, et démarre le programme sans shell.[1], [2], [3]
Imaginez un employé qui reçoit une phrase dont la ponctuation peut changer la tâche : un signe peut transformer une consigne de livraison en un second ordre. Langflow transmettait la commande configurée à un shell qui interprète cette syntaxe comme une instruction de contrôle, au lieu de traiter l’exécutable et ses arguments comme des données distinctes. Le correctif valide la commande autorisée et lance directement le programme, sans shell.[1], [2]
Concepts Clés et Termes
Transport MCP stdio- Lance un serveur MCP comme processus enfant local et échange des messages par l’entrée et la sortie standard.[1], [2]
Injection de commandes (CWE-78)- Un texte influencé de l’extérieur modifie la commande système transmise au shell ou au lanceur de processus.[1], [5]
Correspondances de référentiels
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-78
Analyse de Cause Racine
Avant la version 1.10.3, la configuration MCP stdio concaténait la commande et les arguments en une chaîne, puis lançait bash -c avec cette chaîne. L’API de configuration a reçu une liste d’autorisation et des contrôles des arguments et de l’environnement en version 1.9.0, mais les configurations MCP Tools intégrées aux flows ou aux tweaks pouvaient contourner la validation du modèle, car le point d’exécution n’appliquait aucune politique commune. Langflow 1.10.3 centralise la validation dans lfx.base.mcp.security.validate_mcp_stdio_config, l’applique à l’API, aux flows, aux tweaks, au composant historique et juste avant la création du processus, puis supprime le shell intermédiaire. CVE-2026-105697 correspond à CWE-78, CVSS 3.1 9.9. Lorsque la connexion automatique est désactivée, un utilisateur authentifié non administrateur pouvant configurer ou exécuter le chemin MCP reste nécessaire ; la connexion automatique par défaut peut exposer des instances de développement sans identifiants.[1], [2], [4], [5]
Déroulement de l'Attaque Étape par Étape
Un utilisateur atteint une voie de configuration MCP
L’avis décrit l’accès via les paramètres MCP ou un flow utilisant le composant MCP Tools. Sur une instance exposée conservant la valeur de développement LANGFLOW_AUTO_LOGIN=true, un attaquant pouvait obtenir un jeton sans identifiants. Si la connexion automatique est désactivée, un utilisateur authentifié non administrateur peut tout de même suffire.[1]
La configuration arrive à la préparation du processus
La commande et les arguments deviennent command_str pendant la préparation d’une connexion MCP stdio.[1]
Le shell interprète la chaîne
Avant 1.10.3, le chemin utilisait bash -c ; l’exécution avait lieu lors de la tentative de connexion, avant la réussite du handshake MCP.[1]
Les contrôles à l’entrée sont incomplets
La version 1.9.0 valide les paramètres REST, mais les flows et tweaks peuvent contourner cette validation du modèle.[1]
Le processus hôte utilise les privilèges de Langflow
La commande s’exécute sous le compte du service Langflow. L’interface peut néanmoins signaler l’échec du démarrage MCP.[1]
Code Source : Vulnérable vs Sécurisé
L’extrait vulnérable montre le mode de lancement via shell décrit dans l’avis ; ce n’est pas un exploit exécutable. L’extrait corrigé résume la conception de la version 1.10.3 : une politique commune valide la configuration à chaque entrée et juste avant la création du processus, tandis que l’API reçoit séparément l’exécutable et les arguments. Le lancement direct supprime l’interprétation par le shell, mais ne rend pas automatiquement sûrs tous les exécutables ou paquets autorisés ; les opérateurs doivent aussi appliquer des contrôles de provenance des paquets et d’isolation adaptés.[1], [2]
Extrait source: lfx/base/mcp/util.py — extraits de code ciblés
# Forme de lancement concernée selon l’avis (extrait ciblé) :
server_params = StdioServerParameters(
command="bash",
args=["-c", f"exec {command_str}"],
env=env_data,
)
# Version simplifiée inspirée du code 1.10.3 : valider la politique, puis lancer directement le processus.
command_parts = shlex.split(command_str)
if not command_parts:
raise ValueError("MCP stdio command is required")
command, args = command_parts[0], command_parts[1:]
validate_mcp_stdio_config(command, args, env_data) # Repeat at the execution boundary.
server_params = StdioServerParameters(
command=command,
args=args,
env=env_data,
) # No shell wrapper. The shared validator rejects disallowed configurations.
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Mettez à niveau
langflowvers 1.10.3+,langflow-basevers 0.10.3+ etlfxvers 1.10.3+ ; vérifiez les versions déployées.[1], [3] - ✓Ne considérez pas les contrôles REST de 1.9.0 comme un correctif complet ; examinez les configurations MCP intégrées aux flows et tweaks des anciennes versions.[1]
- ✓Validez commande, arguments, environnement, source du paquet et isolation à chaque entrée et juste avant la création du processus.[1], [2]
- ✓Transmettez séparément l’exécutable et le vecteur d’arguments à l’API de processus ; évitez les chaînes de commande shell.[2]
- ✓En environnement multi-tenant, verrouillez les paquets
npx/uvxautorisés et activez les réglages documentés de durcissement de l’interpréteur et de Docker.[1] - ✓Recommandation de réponse aux incidents de FlawOpen : Vérifiez qui pouvait configurer ou exécuter des connexions MCP stdio pendant l’exposition ; recherchez des processus enfants Langflow inattendus dans la télémétrie hôte.[1]
- ✓Ajoutez des tests pour l’API REST, flow, tweak, composant historique et pré-lancement ; vérifiez qu’une configuration rejetée ne crée jamais de processus.[2]