
Hace un año, el 21 de marzo de 2019, en en HackerOne apareció un muy buen desde . Al introducir un byte nulo (ASCII 0) en el parámetro POST de una de las solicitudes API del correo web, que devolvía una redirección HTTP, se observaron fragmentos de memoria no inicializada, donde a menudo se revelaban fragmentos de los parámetros GET y de los encabezados de otras solicitudes al mismo servidor.
Esta es una vulnerabilidad crítica, ya que las solicitudes contenían también cookies de sesión. Unas horas más tarde se hizo un arreglo temporal que filtraba el byte nulo (como luego se descubrió, esto fue insuficiente, ya que permanecía la posibilidad de inyección CRLF / ASCII 13, 10, lo que permite manipular los encabezados y los datos de la respuesta HTTP; esto es menos crítico, pero sigue siendo incómodo). Al mismo tiempo, el problema fue enviado a los analistas de seguridad y a los desarrolladores para investigar y solucionar las causas de la aparición del error.
El correo Mail.ru es una aplicación muy compleja, donde puede participar una gran cantidad de diferentes componentes frontend/backend, tanto de código abierto (muchas gracias a todos los desarrolladores de software libre) como de desarrollo propio. Se logró excluir todos los componentes excepto nginx y openresty, y se localizó el problema hasta la llamada en el script OpenResty, que no se comportaba como se esperaba (no se puede introducir un byte nulo o un salto de línea a través de los parámetros GET con rewrite en ngx_http_rewrite_module, que, según la documentación, se utiliza y, aparentemente, debería funcionar de manera absolutamente análoga). Se eliminaron las posibles consecuencias, se añadió una filtración extremadamente estricta y se verificó que la filtración eliminaba todos los posibles vectores. Pero el mecanismo que causó la fuga de contenido de la memoria siguió siendo un misterio. Un mes después, el informe de error se cerró como resuelto, y el análisis de las causas del error se pospuso para mejores tiempos.
OpenResty es un plugin bastante popular que permite escribir scripts en Lua dentro de nginx, y se utiliza en varios proyectos de Mail.ru, por lo que el problema no se consideró resuelto. Y después de un tiempo, se regresó a él para entender las verdaderas causas, las posibles consecuencias y redactar recomendaciones para los desarrolladores. En las investigaciones del código fuente participaron y . Se ha descubierto que:
- En Nginx, al utilizar rewrite con datos del usuario, existe una posibilidad de recorrido de directorios (y probablemente SSRF) en algunas configuraciones, pero esto es un hecho conocido que debe ser detectado por analizadores estáticos de configuraciones en y de Yandex (sí, nosotros también lo usamos, gracias). Al usar OpenResty, es fácil pasar por alto esta posibilidad, pero nuestra configuración no se vio afectada.
ejemplo de configuración:
location ~ /rewrite { rewrite ^.*$ $arg_x; } location / { root html; index index.html index.htm; }resultado
curl localhost:8337/rewrite?x=/..../../../../../../../etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
... - En Nginx hay un error que provoca una fuga de contenido en memoria si la cadena de rewrite contiene un byte nulo. Al devolver un redireccionamiento, Nginx asigna un nuevo buffer de memoria que corresponde a la longitud total de la cadena, pero copia allí la cadena a través de una función de cadena, donde el byte nulo es el terminador de la cadena, por lo que la cadena se copia solo hasta el byte nulo, y el resto del buffer contiene datos no inicializados. Se puede encontrar un análisis detallado en .
ejemplo de configuración (^@ byte nulo)
location ~ /memleak { rewrite ^.*$ "^@asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdasdf"; } location / { root html; index index.html index.htm; }resultado
curl localhost:8337/secret -vv
...
curl localhost:8337/memleak -vv
...
Ubicación: http://localhost:8337/secret
...
- Nginx protege los parámetros GET contra la inyección de caracteres especiales y permite utilizar en rewrite únicamente parámetros GET. Por lo tanto, no se puede explotar la inyección a través de parámetros controlados por el usuario en Nginx. Los parámetros POST no están protegidos. OpenResty permite trabajar tanto con parámetros GET como POST, por lo que al usar parámetros POST a través de OpenResty, surge la posibilidad de inyección de caracteres especiales.
ejemplo de configuración:
location ~ /memleak { rewrite_by_lua_block { ngx.req.read_body(); local args, err = ngx.req.get_post_args(); ngx.req.set_uri( args["url"], true ); } } location / { root html; index index.html index.htm; }resultado:
curl localhost:8337 -d "url=secret" -vv
...
curl localhost:8337 -d "url=asdfasdfasdfasdfasdfasdfasdfasdf" -vv
...
Ubicación: http://localhost:8337/{...puede contener secret...}
...
Reacción posterior
Se informó del problema a los desarrolladores de Nginx y OpenResty, quienes no consideran el problema como un error de seguridad en Nginx, ya que en Nginx no hay posibilidad de explotación del error a través de la inyección de caracteres especiales. El fix fue publicado el 16 de diciembre. Durante 4 meses desde el reporte, en OpenResty tampoco se realizaron cambios, aunque había un entendimiento de que se necesitaba una versión segura de la función ngx.req.set_uri(). El 18 de marzo de 2020 publicamos información, y el 21 de marzo OpenResty lanzó , la cual agrega una verificación de URI.
Portswigger un buen artículo y obtuve comentarios de OpenResty y Nginx (sin embargo, el comentario de que solo se revela un pequeño fragmento de memoria es incorrecto y puede llevar a confusiones; esto se determina por la longitud de la cadena que sigue al byte nulo y, en ausencia de límites explícitos en la longitud, puede ser controlado por el atacante).
¿Cuál fue el error y qué hacer para prevenirlo?
¿Hubo un error en nginx? Sí, lo hubo, porque la fuga de contenido de memoria es un error en cualquier caso.
¿Hubo un error en OpenResty? Sí, al menos no se investigó ni documentó el tema de seguridad de la funcionalidad ofrecida por OpenResty.
¿Se cometió un error de configuración / uso de OpenResty? Sí, porque en ausencia de indicaciones explícitas, se hizo una suposición no verificada sobre la seguridad de la funcionalidad utilizada.
¿Cuál de estos errores representa una vulnerabilidad de seguridad con una recompensa de $10000? Para nosotros, en general, no es importante. En cualquier software, especialmente en la interfaz de varios componentes, particularmente proporcionados por diferentes proyectos y desarrolladores, nadie puede garantizar que todas sus características sean conocidas y documentadas, y que no existan errores. Por lo tanto, cualquier vulnerabilidad de seguridad surge precisamente donde afecta a la seguridad.
En cualquier caso, será una buena práctica normalizar o limitar/filtrar al máximo los datos de entrada que se envían a cualquier módulo/API externo, si no hay indicaciones claras y un entendimiento inequívoco de que no es necesario.
Errata
Por experiencia , para mantener la pureza del lenguaje:
bounty bug — concurso de detección de errores
informe de errores — aviso de error
redirect — redirección
de código abierto — de código abierto
errata — trabajo sobre errores
Fuente: habr.com
