Reports
MEDIA[OTRAS INYECCIONES]#3518571

Weblate: inyección de argumentos en ssh-keyscan desde /manage/ssh/ permitía leer ficheros del servidor

El campo host del formulario de claves SSH de Weblate se pasaba tal cual a ssh-keyscan. Con un valor como -f/etc/passwd, un administrador podía leer ficheros del servidor, incluidas claves privadas SSH y settings.py.

Resumen
Resumen en castellano de un reporte público, no una traducción literal. El código y los comandos se mantienen como en el original.

Resumen

Weblate tiene una página de administración en /manage/ssh/ para gestionar claves SSH. Desde ahí se puede añadir la clave de un servidor indicando su nombre (host) y, opcionalmente, un puerto. El problema estaba en cómo se usaba ese nombre: la función add_host_key de weblate/vcs/ssh.py lo añadía directamente como un argumento más de la orden ssh-keyscan, sin ninguna comprobación.

cmdline = ["ssh-keyscan"]
if port:
    cmdline.extend(["-p", str(port)])
cmdline.append(host)

Como subprocess.run recibe una lista, no se pueden encadenar órdenes de shell, pero sí colar opciones de ssh-keyscan. La que aprovecha el investigador es -f, que hace que la herramienta lea los servidores desde un fichero. Cuando el contenido de ese fichero hace fallar a ssh-keyscan, Weblate captura el error y muestra al usuario la salida de error del proceso en un mensaje:

except subprocess.CalledProcessError as exc:
    messages.error(
        request,
        gettext("Could not get host key: %s") % exc.stderr or exc.stdout,
    )

El resultado es la lectura de ficheros del servidor a los que tenga acceso el usuario con el que corre la aplicación. El investigador lo confirmó en instalaciones propias con las versiones 5.0.2 y 5.15.2 (la estable en el momento del reporte) y cree que probablemente afecta a todas. El fallo tiene asignado el CVE-2026-24126.

El investigador señala también que el parámetro type de la acción de generar clave (generate_ssh_key) llega sin filtrar a subprocess.run, aunque la prueba de concepto se centra en host. Según el propio reporte, no hay forma directa de ejecutar código, porque ssh-keyscan no tiene opciones que permitan lanzar órdenes arbitrarias: el impacto se limita a la inyección de argumentos.

Pasos de reproducción

  1. Iniciar sesión en Weblate con un usuario que pueda gestionar las claves SSH.
  2. Abrir la página de gestión SSH, /manage/ssh/.
  3. Usar la opción de añadir la clave de un servidor ("Add a host key").
  4. Interceptar la petición con un proxy, por ejemplo Burp Suite.
  5. Cambiar en el cuerpo POST el parámetro host por la opción -f seguida de la ruta del fichero que se quiere leer. Payload:
action=add-host&host=-f/app/data/ssh/id_rsa&port=22
  1. Enviar la petición. La prueba de concepto del reporte, leyendo /etc/passwd:
POST /manage/ssh/ HTTP/1.1
[...]
Host: 127.0.0.1
[...]
csrfmiddlewaretoken=[REDACTADO]&host=-f/etc/passwd&port=12&action=add-host
  1. Observar la respuesta: la aplicación devuelve un mensaje de error que contiene el contenido del fichero indicado.

Impacto

Un atacante con permisos de administrador en Weblate podía leer ficheros del servidor a los que tenga acceso el usuario del servicio web. Según el investigador, así se pueden extraer:

  • las claves privadas SSH del servidor (id_rsa);
  • /etc/passwd;
  • el settings.py de Django, con la SECRET_KEY;
  • el código fuente de la aplicación.

Con las claves SSH o las credenciales filtradas, el atacante podría saltar a otros sistemas o llegar a tener una shell completa.

Qué aprender de este caso

  • Pasar argumentos como lista a subprocess.run evita la inyección de órdenes de shell, pero no la de opciones: si un valor del usuario puede empezar por -, la herramienta lo interpretará como un parámetro. Merece la pena revisar cualquier formulario que acabe en ssh-keyscan, git, curl, tar o similares.
  • La defensa habitual es doble: validar el formato esperado (aquí, un nombre de servidor o una IP, que nunca empieza por guion) y colocar -- antes de los argumentos del usuario cuando la herramienta lo admita.
  • Devolver al navegador el stderr de un proceso externo convierte un fallo de argumentos en una lectura de ficheros. Los errores de herramientas del sistema deberían registrarse en el servidor y mostrarse al usuario con un mensaje genérico.
  • Que una función solo esté al alcance de administradores no la hace inocua: aquí el panel web daba acceso a secretos del sistema (claves SSH, SECRET_KEY) que ese rol no debería poder ver.