Neutron, nuestro motor de IA, obtuvo un 97.1% en el benchmark CyberGym de UC Berkeley. Más información

Análisis a fondo

Análisis a fondo

Dokploy: RCE en el host desde una cuenta de miembro

Un miembro de Dokploy puede poner un backtick en la contraseña de una base de datos y ejecutar comandos como root en el host; la corrección solo está en canary.

Resumen

  • Un miembro de Dokploy que puede crear un servicio de base de datos puede ejecutar comandos como root en el host. El truco es un backtick en la contraseña, o un punto y coma en el nombre de usuario.
  • Dokploy v0.29.0 a v0.31.0 está afectado, y v0.31.0, publicado el 5 de noviembre de 2026, es la versión vulnerable actual. El atacante necesita un miembro con permiso de creación de servicio; no es no autenticado.
  • No hay CVE ni CVSS asignada a este problema. El investigador reporta una prueba en vivo contra la imagen oficial v0.31.0 y señala que Dokploy puntuó 9.9 la misma clase de precondición en CVE-2026-72868. No figura en el catálogo Known Exploited Vulnerabilities (KEV) de CISA a 10 de noviembre de 2026.
  • La corrección está en canary, no en una versión etiquetada. Quite el permiso de creación de servicio a los miembros no fiables ahora; después aplique el parche o espere una versión. Consulte "¿Cómo lo corrijo?".

¿Estoy afectado?

Todas estas condiciones deben cumplirse:

  • Ejecuta Dokploy autoalojado v0.29.0 a v0.31.0. El validador de contraseña vulnerable se introdujo en v0.29.0 y el investigador dice que no ha cambiado en ninguna rama.
  • Concede a un miembro de la organización el permiso de creación de servicio y acceso a un servicio de base de datos. Los procedimientos changePassword de Dokploy comprueban service: ["create"] antes de ejecutar el comando de base de datos.
  • El miembro puede alcanzar el panel. En una instalación de un solo administrador no hay frontera que cruzar, porque el administrador ya tiene el permiso.

El investigador probó la imagen oficial dokploy/dokploy v0.31.0 y reporta una prueba en vivo. La corrección es la fusión del commit 1c862c9cb0 de la pull request #5910 en la rama canary, fusionada el 9 de noviembre de 2026 a las 16:59 UTC. Las etiquetas de versión terminan en v0.31.0.

Rama Versiones afectadas Versión corregida
Dokploy autoalojado v0.29.0 a v0.31.0 Ninguna versión etiquetada al 10 de noviembre de 2026; la corrección está en canary en la pull request #5910

Compruebe la imagen y la versión en ejecución:

docker inspect --format '{{.Config.Image}}' dokploy
docker images dokploy/dokploy

El panel también muestra su versión en la interfaz. Una imagen etiquetada v0.29.0 a v0.31.0 está afectada. Una compilación canary que incluya la pull request #5910 tiene la corrección, pero no es una versión publicada.

¿Cómo lo corrijo?

Todavía no hay una versión corregida. La corrección está fusionada en canary, así que elija una de estas vías:

  1. Construya o descargue una imagen de Dokploy que contenga el commit canary de la pull request #5910 y vuelva a desplegar el panel. Confirme que la compilación incluye la llamada a quote() en los routers de base de datos antes de confiar en ella.
  2. Si no puede ejecutar una compilación canary, aplique el mismo cambio a su compilación: cada argumento después de docker exec <container> pasa por quote() del paquete shell-quote.
  3. Hasta que el panel esté corregido, quite el permiso de creación de servicio a cada miembro que no lo necesite. Trate a un miembro que puede crear una base de datos como equivalente a root en el host.
  4. Restrinja el acceso al panel de Dokploy a redes de confianza y no lo exponga directamente a Internet.
  5. Revise el registro de auditoría para cambios de contraseña de base de datos que no pueda justificar, y rote las credenciales de base de datos si encuentra alguno.
  6. Vigile una versión etiquetada de Dokploy que nombre la pull request #5910 o esta corrección, luego actualice y confirme la versión.

El artículo del investigador es la referencia para el alcance de la corrección. La pull request cambia las rutas de contraseña de Postgres, MySQL, MariaDB, MongoDB y Redis.

¿Cuál es la causa raíz?

El validador de contraseña de Dokploy bloquea varios caracteres de shell pero permite el backtick, el punto y coma, la barra vertical, el ampersand, los paréntesis y las redirecciones. Los procedimientos changePassword interpolan después la contraseña en un comando docker exec que bash analiza, y el nombre de usuario de la base de datos se interpola sin validador.

Este es el validador en apps/dokploy/server/src/db/schema/utils.ts en v0.31.0:

/** Database password: blocks shell-dangerous characters like $ ! ' " \ / and spaces. */
export const DATABASE_PASSWORD_REGEX =
    /^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/;

El comentario nombra los caracteres bloqueados. La clase de caracteres todavía incluye `, ;, |, &, (, ), > y <. Eso basta para la sustitución de comandos y para un segundo comando.

Este es el procedimiento changePassword de Postgres en v0.31.0:

const command = `docker exec ${container.Id} psql -U ${databaseUser} -d postgres -c "ALTER USER \\"${databaseUser}\\" WITH PASSWORD '${password}';"`;

await db.transaction(async (tx) => {
  ...
  if (serverId) {
    await execAsyncRemote(serverId, command);
  } else {
    await execAsync(command, { shell: "/bin/bash" });
  }
});

databaseUser viene del registro de Postgres almacenado y no tiene validador. Se coloca después de -U sin comillas. La contraseña está entre comillas simples, pero todo el argumento -c está entre comillas dobles. Bash expande los backticks y $() dentro de las comillas dobles, así que una contraseña como P2`id` ejecuta id mientras el shell construye el comando. Un punto y coma en databaseUser termina el comando y empieza otro.

El comando se ejecuta mediante execAsync con /bin/bash en el proceso servidor de Dokploy. Ese proceso es root en el contenedor servidor, que tiene el socket Docker del host. La ejecución de comandos allí equivale a root en el host en la práctica.

La misma construcción existe en los routers MySQL, MariaDB y MongoDB. El investigador reporta haber disparado las rutas Postgres y MySQL y califica las otras dos de idénticas en el código fuente. La ruta Redis pone la contraseña entre comillas simples de nivel superior y no se disparó, lo que es el control que demuestra el mecanismo.

¿Cómo se explota?

Un miembro que puede crear un servicio de base de datos cambia su contraseña o nombre de usuario por un valor que cierra el quoting del shell. El servidor Dokploy ejecuta entonces el comando inyectado como root.

  1. El atacante inicia sesión en Dokploy como miembro de la organización con el permiso de creación de servicio, o crea ese miembro si ya controla la organización.
  2. El atacante crea un servicio de base de datos, o usa uno al que tiene acceso. El registro de Postgres del servicio almacena un nombre de usuario.
  3. El atacante llama al procedimiento changePassword con una contraseña que pasa DATABASE_PASSWORD_REGEX pero contiene un backtick, por ejemplo P2`id>x`. El validador la acepta porque no bloquea el backtick.
  4. Dokploy construye el comando docker exec y lo pasa a /bin/bash. Bash realiza la sustitución de comandos sobre el backtick y ejecuta id, redirigiendo su salida a un archivo elegido por el atacante. El cambio de contraseña de la base también se completa.
  5. Como alternativa, el atacante define el nombre de usuario de la base con un punto y coma. El nombre no tiene validador, así que llega al argumento psql -U sin comillas y empieza un segundo comando.
  6. El comando se ejecuta como root en el contenedor servidor de Dokploy. El contenedor tiene el socket Docker del host, así que el atacante puede usarlo para alcanzar el host y los servicios de los demás inquilinos.

La prueba en vivo del investigador usó primero una contraseña benigna, luego una contraseña $(id) que el validador rechazó. La contraseña con backtick devolvió después 200 y dejó un archivo canario con uid=0(root) gid=0(root) dentro del contenedor servidor.

La prueba se ejecutó contra la imagen oficial v0.31.0 en una instalación ligada a localhost. El investigador también reporta haber disparado la ruta MySQL como segundo punto de entrada y un nombre de usuario con punto y coma como tercero.

No se conoce explotación en entornos reales. El registro público es el mensaje oss-security del investigador, el artículo que enlaza y la corrección fusionada.

¿Cómo se desarrolla el ataque?

Un miembro con el permiso de creación de servicio envía una contraseña que el validador acepta, y la contraseña llega a un comando bash dentro de docker exec. Bash ejecuta entonces el comando inyectado como root en el contenedor que tiene el socket Docker del host.

flowchart LR A["Miembro con creación de servicio"] --> B["changePassword con un backtick"] B --> C["El validador permite el backtick"] C --> D["Cadena construida en un comando docker exec"] D --> E["La sustitución de bash ejecuta la carga"] E --> F["Root en el contenedor de Dokploy"] F --> G["Socket Docker del host"] G --> H["Root en el host y secretos de otros inquilinos"]

¿Qué puede hacer un atacante?

Un atacante que dispara la inyección ejecuta comandos como root en el contenedor servidor de Dokploy y puede alcanzar el host mediante su socket Docker. Eso es el control del panel, de cada servidor que gestiona y de los secretos de cada inquilino.

Las condiciones forman parte del impacto:

  • El atacante necesita una cuenta de miembro de Dokploy autenticada con el permiso de creación de servicio y acceso al servicio de base de datos. El investigador indica que no es no autenticado y que no es un fallo entre organizaciones.
  • En una instalación de un solo administrador, el administrador ya tiene el permiso, así que no hay frontera entre el usuario del panel y el host.
  • El comando se ejecuta como root en el contenedor servidor. Ese contenedor alcanza el socket Docker del host por diseño. El atacante puede iniciar contenedores privilegiados, leer las variables de entorno de otros servicios y alcanzar el sistema de archivos del host.
  • La inyección se dispara al enviar el cambio de contraseña. La contraseña de la base también cambia, así que la operación puede parecer exitosa mientras se ejecuta la carga.
  • No se conoce explotación en entornos reales, y no se nombra ninguna víctima.

El impacto es a nivel del host, no de la base de datos. Un miembro que puede crear una base de datos ya es de confianza para ese servicio; este fallo convierte esa confianza en control del host y de los demás inquilinos.

¿Por qué funciona la corrección?

La corrección construye cada comando docker exec a partir de un arreglo de argumentos con quote() de shell-quote. La contraseña y el nombre de usuario siguen siendo argumentos únicos, y bash ya no analiza su contenido como sintaxis.

Este es el cambio en apps/dokploy/server/api/routers/postgres.ts de la pull request #5910:

-           const command = `docker exec ${container.Id} psql -U ${databaseUser} -d postgres -c "ALTER USER \\"${databaseUser}\\" WITH PASSWORD '${password}';"`;
+           const command = `docker exec ${container.Id} ${quote([
+               "psql",
+               "-U",
+               databaseUser,
+               "-d",
+               "postgres",
+               "-c",
+               `ALTER USER "${databaseUser}" WITH PASSWORD '${password}';`,
+           ])}`;

quote() devuelve una cadena escapada para el shell para cada argumento, así que un backtick o un punto y coma dentro de databaseUser o password permanece en su argumento. La sentencia SQL sigue siendo un único argumento -c, y la base recibe la contraseña literal. La pull request dice que la contraseña sigue siendo segura dentro de la cadena SQL porque el validador prohíbe la comilla simple.

La misma reescritura cubre los routers MySQL, MariaDB, MongoDB y Redis. La corrección no cambia el validador, así que un miembro todavía puede definir una contraseña con un backtick; la diferencia es que el backtick ya no llega a un shell.

¿Cómo compruebo si estuve comprometido?

No se publican indicadores de compromiso. Revise el registro de auditoría del panel y los registros de base de datos para las entradas que necesita el exploit.

  • Revise el registro de auditoría de Dokploy para cambios de contraseña de base de datos y creaciones de servicio de base de datos que no pueda justificar. El procedimiento changePassword escribe una entrada de auditoría después de ejecutar el comando.
  • Inspeccione las contraseñas y nombres de usuario almacenados en busca de backticks, puntos y coma, barras verticales y otros metacaracteres de shell. Un valor que el validador permite pero que parece sintaxis de shell es una pista.
  • Busque archivos inesperados en el contenedor servidor de Dokploy, sobre todo un archivo canario o un script de shell creado por un comando de base de datos.
  • Revise el contenedor servidor en busca de procesos root inesperados, nuevos contenedores privilegiados y conexiones salientes, y compruebe los eventos de Docker del host para contenedores que no inició.
  • Si encuentra una pista, conserve la base del panel, el contenedor servidor y los eventos de Docker antes de reconstruir el host. Rote cada credencial de base de datos y de servicio que almacene el panel.

Una inyección fallida puede no dejar rastro más allá de la entrada de auditoría del cambio de contraseña. La entrada de auditoría por sí sola no demuestra explotación.

Cronología

  • 2026-11-05: Dokploy publica v0.31.0, la versión actual y la que probó el investigador.
  • 2026-11-07: Se abre la pull request #5910, «fix(security): database changePassword command injection (all 5 DB types)».
  • 2026-11-09 16:59 UTC: La pull request se fusiona en la rama canary. Ninguna versión etiquetada la contiene.
  • 2026-11-10 02:11 UTC: El investigador publica la divulgación probada en vivo en la lista oss-security.

De la versión vulnerable a la divulgación pública en cinco días
Figura 1: v0.31.0 salió el 5 de noviembre, la corrección se fusionó en canary el 9 de noviembre y la divulgación llegó el 10 de noviembre. Fuente: versiones de Dokploy y pull request #5910, comprobados el 10 de noviembre de 2026

Preguntas frecuentes

¿Es no autenticado el fallo de Dokploy?

No. El atacante necesita un miembro de organización autenticado con el permiso de creación de servicio y acceso al servicio de base de datos. El investigador expone claramente la precondición y dice que el fallo no es entre organizaciones.

¿Corrige el fallo Dokploy v0.31.0?

No. v0.31.0 es la versión actual y la que probó el investigador. La corrección está en la rama canary en la pull request #5910, fusionada el 9 de noviembre de 2026. Ninguna versión etiquetada la contiene al 10 de noviembre de 2026.

¿Hay una CVE para este problema?

No hay CVE asignada al 10 de noviembre de 2026. Dokploy puntuó otro fallo con la misma clase de precondición, CVE-2026-72868, con CVSS v3.1 9.9; esa puntuación pertenece a la inyección anterior destination.testConnection, no a esta.

¿Existe un exploit público?

El investigador publicó el método, la prueba en vivo y un artículo, y la prueba es reproducible contra la imagen oficial v0.31.0. No se enlaza un repositorio de exploit autónomo en el mensaje público.

¿Debo tratar a cada miembro de Dokploy como root?

Un miembro que puede crear un servicio de base de datos puede alcanzar root en el host mediante este fallo, así que sí para ese permiso. Quite la creación de servicio a los miembros que no la necesiten, y trate el panel como un sistema equivalente a root.

¿Afecta a un servicio Dokploy alojado?

Dokploy es software autoalojable. La divulgación y la corrección se refieren al panel autoalojado. No se nombra ningún servicio alojado en el registro público, así que este artículo no afirma nada sobre uno.

Referencias

  • Divulgación oss-security del investigador, «Dokploy: the validator allows the backtick : a member's database password change is host RCE, 9.9 on the vendor's own vector (v0.31.0, live-proven)», 9 de noviembre de 2026, con la prueba en vivo, el alcance y el enlace al artículo: https://www.openwall.com/lists/oss-security/2026/11/10/2 (comprobado el 10 de noviembre de 2026)
  • Pull request de Dokploy #5910, «fix(security): database changePassword command injection (all 5 DB types)», abierta el 7 de noviembre de 2026 y fusionada el 9 de noviembre de 2026: https://github.com/Dokploy/dokploy/pull/5910 (comprobado el 10 de noviembre de 2026)
  • El DATABASE_PASSWORD_REGEX vulnerable en apps/dokploy/server/src/db/schema/utils.ts en v0.31.0: https://github.com/Dokploy/dokploy/blob/v0.31.0/apps/dokploy/server/src/db/schema/utils.ts#L50-L52 (comprobado el 10 de noviembre de 2026)
  • El comando changePassword de Postgres vulnerable en apps/dokploy/server/api/routers/postgres.ts en v0.31.0: https://github.com/Dokploy/dokploy/blob/v0.31.0/apps/dokploy/server/api/routers/postgres.ts#L448 (comprobado el 10 de noviembre de 2026)
  • Versión Dokploy v0.31.0, publicada el 5 de noviembre de 2026: https://github.com/Dokploy/dokploy/releases/tag/v0.31.0 (comprobado el 10 de noviembre de 2026)
  • Registro CVE-2026-72868, la inyección rclone destination.testConnection anterior de Dokploy, CVSS v3.1 9.9, citada como la puntuación del proveedor para la misma clase de precondición: https://www.cve.org/CVERecord?id=CVE-2026-72868 (comprobado el 10 de noviembre de 2026)
  • Repositorio de Docker Hub de dokploy/dokploy, 16,116,660 descargas, comprobado el 10 de noviembre de 2026: https://hub.docker.com/r/dokploy/dokploy (comprobado el 10 de noviembre de 2026)
  • Fuente JSON del catálogo Known Exploited Vulnerabilities de CISA, comprobada el 10 de noviembre de 2026, sin entrada de Dokploy: https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (comprobado el 10 de noviembre de 2026)