1 puntos por GN⁺ 2023-07-04 | 1 comentarios | Compartir por WhatsApp
  • Nginx, un servidor web multipropósito con una cuota de mercado dominante desde 2004
  • Una configuración incorrecta de Nginx puede derivar en vulnerabilidades de seguridad y exposición de datos
  • Presentación de NavGix, una herramienta automatizada para detectar vulnerabilidades en Nginx
  • Esta vulnerabilidad puede permitir el acceso a archivos y directorios fuera del alcance previsto
  • Los estudios de caso sobre Bitwarden y el HPC Toolkit de Google muestran la gravedad de esta vulnerabilidad

1 comentarios

 
GN⁺ 2023-07-04
Opiniones de Hacker News
  • Para referencia, gixy (inspector de configuración de nginx) detecta este problema: https://github.com/yandex/gixy/blob/master/docs/en/plugins/a...
    Y NixOS ejecuta gixy automáticamente sobre las configuraciones generadas a través de él, y si hay un problema rechaza la compilación del sistema

    • Si un servidor web necesita herramientas adicionales para que los usuarios eviten este tipo de trampas, quizá habría que reconsiderar los valores predeterminados
    • No conocía gixy, pero lo corrí en mi servidor casero y apareció una vulnerabilidad. Estaba usando $uri en una redirección 301
    • Probé Nix una vez y hasta ahora se ve bastante bien
      Pero me pregunto si existe un buscador de opciones mejor. No me gustó la forma de buscar entre miles de opciones; quisiera ver solo un paquete como ssh y únicamente las opciones correspondientes, pero se mezclan demasiados resultados irrelevantes
    • NixOS ya no ejecuta Gixy. Véase https://github.com/NixOS/nixpkgs/pull/209075
  • Puede que sea una pregunta tonta, pero ¿hay alguna buena razón para que nginx deba permitir subir al directorio padre con .. en la ruta de una URL? Simplemente parece un comportamiento que está esperando causar problemas
    Edit: sigo algo confundido sobre lo que pasa en la vulnerabilidad original. Parece que http://localhost/foo../secretfile.txt se interpreta como algo como /var/www/foo/../secretfile.txt, pero en un servidor sin vulnerabilidad, ¿por qué http://localhost/foo/../secretfile.txt no se interpretaría igual? No entiendo por qué .. dentro de la ruta solo funciona a veces

    • Este es un problema conocido desde hace muchísimo tiempo en nginx, y es un vector de ataque común en CTF: https://book.hacktricks.xyz/network-services-pentesting/pent...
    • El problema es que una URL no es una ruta real. Una URL es una dirección abstracta hacia un recurso que puede ser un directorio, un archivo, un ejecutable, un stream, etc.
      En este caso, nginx la interpreta como un directorio (http://localhost/foo) por la forma en que una parte de la URL se mapea al sistema de archivos local en la configuración. Como parece apuntar a un directorio, cuando nginx construye la ruta completa del recurso solicitado termina con "${mapped_path}/../secretfile.txt", y aunque desde la perspectiva de la URL no tenga sentido, en el sistema de archivos local sí es válido. Una URL es solo una cadena, no un conjunto real de componentes de ruta, así que la ubicación de las barras tampoco es intrínsecamente importante
      Es un problema muy común que se ha visto una y otra vez en servidores web desde que existe la web. La asignación directa de URLs a rutas de archivos se volvió popular porque empezó con servidores simples de archivos estáticos con índices, pero pronto pasó a entornos mixtos donde la URL ya no es una ruta sino un identificador de aplicación. Se indica la app con una parte de la ruta y el resto se trata como parámetros o argumentos
      Y en general, respetar . o .. de una URL para objetos del sistema de archivos normalmente no tiene sentido. Mis apps normalizan la ruta solicitada para que se mapee correctamente. Los navegadores tratan la URL como una ruta al construir enlaces relativos, así que también hay que tener cuidado con cuándo y cómo usar una / final. Del lado del servidor podría apuntar a un recurso con un significado distinto
    • En un caso de uso “normal”, no hace falta. Parece más razonable que fuera un comportamiento activado explícitamente con una bandera en location, algo como allow_parent_traversal on;
    • Mi suposición es que NginX probablemente revisa "/foo/bar/.." para bloquearlo o normalizarlo a "/foo/". Pero "/foo/bar.." es un nombre de archivo completamente válido, así que parece esquivar esa comprobación y no lo detecta
    • Cuándo funciona .. en una ruta depende por completo de los permisos de archivos
      En este caso, si suponemos que desde el directorio índice web (../index.html) hay permiso para leer archivos hasta llegar al directorio raíz (/), entonces, una vez que se tiene permiso para subir hasta la raíz, también se puede ver cualquier archivo legible por todos y accesible desde ahí, por ejemplo /etc/passwd
      Piensa en un tenedor de tres puntas, donde el servidor web está en la punta de la derecha. Si la parte donde se unen las puntas es el sistema de archivos, cuando se tiene permiso de acceso a los archivos y directorios que conectan desde la punta derecha donde está el servidor web hasta ese mango común, una vez que llegas ahí puedes seguir accediendo a los archivos de las otras puntas
  • No entiendo por qué esto no se considera una vulnerabilidad de nginx. Este comportamiento es totalmente absurdo, no parece tener ningún propósito útil y es explotable de inmediato

    • Lo hicieron por velocidad. Una sustitución simple de texto es mucho más rápida que verificar que una ruta termine correctamente con una barra
      También hay que recordar que nginx se volvió popular gracias a benchmarks donde parecía más “web scale” que Apache2
  • Me dan ganas de proponer esto: poner una opción parecida a una Linux capability para quitar la opción .. del parser de nombres de archivo del kernel de Linux
    En las apps web, desde la época de los módems telefónicos, no han dejado de aparecer distintas formas de meter los dos puntos para saltarse restricciones. Igual que Linux hizo con otras clases de bugs en espacio de usuario, ya va siendo hora de buscar una forma de cerrar este problema de una vez

    • Existe RESOLVE_BENEATH en https://man7.org/linux/man-pages/man2/openat2.2.html
      FreeBSD tiene esta función en el openat(2) normal como O_RESOLVE_BENEATH
    • Eso rompería demasiadas cosas; no es algo razonable de hacer
      Basta con ejecutar nginx como un usuario separado con privilegios muy limitados, o correrlo en Docker. Si además lo actualizas con regularidad, normalmente eso resuelve el 90% de los problemas de seguridad
    • /some/../path debería estar prohibido casi al 100%. No hay un caso de uso razonable más allá de “alguien escribió código feísimo”
      ../some/path al menos a veces sí tiene sentido
      Aun así, probablemente no sería tan útil como parece. Muchas apps interpretan .. antes de pasárselo al sistema operativo
    • No haría mucha diferencia. El código suele normalizar la ruta antes de tocar la API del sistema de archivos
    • Del lado del kernel ya existe otro mecanismo, o sea el sistema de permisos del que dependemos
      Si estás sirviendo archivos desde una carpeta hacia la web, el framework web debe encargarse de no salirse de la carpeta raíz pública que le toca. Si lo estás armando tú mismo, tienes que contemplar todo tipo de casos, incluido ese
  • Eso de que “el equipo de Google VRP dio una recompensa de 500 dólares por encontrar esta vulnerabilidad. Consideró que el impacto en la aplicación no era lo bastante serio como para dar una recompensa mayor”, ¿o sea que la exposición del correo de la cuenta de GCP y la clave privada vale solo 500 dólares? ¿Qué demonios? Muy de Google, la verdad

  • Menos mal que lo filtrado seguía cifrado. Ni siquiera una empresa especializada en este tipo de cosas está a salvo de filtraciones, así que, siendo sinceros, este es el mejor escenario posible

  • El título está bastante editado. El título original es Hunting for Nginx Alias Traversals in the wild
    El título enviado a HN destaca la vulnerabilidad de Bitwarden, pero el artículo también cubre el caso de Google

    • Bien, ya devolví el título a como estaba. El título enviado era “Leaking Bitwarden's Vault with a Nginx vulnerability”
  • Si uno solo necesita servir archivos estáticos de forma segura y estable usando la menor cantidad posible de recursos, ¿cuál sería hoy la mejor opción? Antes habría elegido Nginx, pero desde la perspectiva de seguridad me pregunto si no convendría más una herramienta más específica y con menos margen de configuración

    • Yo uso https://static-web-server.net/
      Es multiplataforma, está escrito en Rust, su configuración es simple y trae valores seguros por defecto. También tiene imágenes de contenedor endurecidas y un módulo endurecido para NixOS
      No recomiendo Caddy. La imagen oficial de Docker corre como root por defecto [1], y tampoco ofrece un archivo de unidad de systemd correctamente aislado [2]
      [1]: https://github.com/caddyserver/caddy-docker/issues/104
      [2]: https://github.com/caddyserver/dist/blob/master/init/caddy.s...
      Edit: ajusté la redacción
    • Para hacer un poco de promoción, Caddy funciona muy bien para esto. Ofrece HTTPS automático, está escrito en Go, así que no hay que preocuparse por bugs de seguridad de memoria, y tiene un módulo file_server sólido
    • Llevo años usando Caddy. Da certificados SSL automáticos, file serving y reverse proxy, y además la configuración es muy fácil y clara. Como es un binario único en Go, hasta “instalarlo” es fácil y solo hay un archivo de configuración
    • Configurar Caddy para servir archivos estáticos es bastante simple
    • Merecat: https://github.com/troglobit/merecat/
  • Puede que sea una pregunta tonta, pero ¿por qué Bitwarden permitía desde el inicio solicitudes no autenticadas a /attachments? Aunque existiera el bug de Nginx, ¿no habría fallado la solicitud si esa URL hubiera requerido autenticación?

    • Este exploit apunta a la configuración del servidor web, así que ni el código de autenticación de Bitwarden ni el código de Bitwarden llegan a ejecutarse. No es raro ni incorrecto que el proyecto use su propia autenticación en vez de depender de Nginx o de módulos
      Aun así, como distribuyeron una configuración riesgosa a través de Docker, sí es responsabilidad de Bitwarden. Parece que Bitwarden también lo reconoció y luego lo corrigió
  • Perdonen la pregunta tonta. ¿Tener la propiedad correcta de directorios y archivos no bloquea este tipo de recorrido?
    Si nginx no se ejecuta como root, ¿cómo puede leer archivos distintos de los asignados explícitamente al usuario de nginx?

    • Claro que se puede bloquear. Si ejecutas la app con un usuario, nginx con otro, pones go-rwx a todos los archivos de la app y al grupo de los archivos “static” le asignas www-data con solo g+r, entonces el servidor web no puede acceder a los archivos de la app.
      Literalmente es hosting de aplicaciones 101, y la gente ya hacía eso hace 20 años.
    • Ah, la maravilla del umask 022. En lo personal, siempre recomiendo evitar que otros usuarios puedan leer los archivos. Si no puedes hacerlo con todos, al menos debería aplicarse a directorios importantes como los que están bajo /home.
      Puede que tengas que ajustar un poco más las membresías de grupo, pero vale totalmente la pena.
    • No sé los demás, pero hoy en día yo no instalo nginx correctamente para uso personal. Solo levanto una imagen de Docker. Y la verdad ni confirmo si corre como root o no.
      Igual la estoy regando durísimo. Ay.
    • El umask normal suele ser 022, así que en la mayoría de los casos los workers de nginx pueden leer, pero no escribir. Ni siquiera hace falta que estén asignados explícitamente a un usuario como www-data. Si la aplicación genera datos sensibles, obviamente es mejor usar umask 077.
    • Sí.
      Por desgracia, nginx y otros servidores web normalmente sí necesitan ejecutarse como root en aplicaciones web comunes, porque tienen que escuchar en los puertos 80 o 443. Solo root puede abrir puertos por debajo de 1024.
      Aquí hay una explicación más detallada: https://unix.stackexchange.com/questions/134301/why-does-ngi...