Acerca de una vulnerabilidad en…

Acerca de una vulnerabilidad en…

Hace un año, 21 de marzo de 2019, en programa de recompensas por errores Mail.Ru uno muy bueno llego a HackerOne informe de error de maxarr. Al introducir un byte cero (ASCII 0) en el parámetro POST de una de las solicitudes API de correo web que devolvió una redirección HTTP, se vieron fragmentos de memoria no inicializada en los datos de redirección, en los que fragmentos de los parámetros GET y encabezados de otras solicitudes al mismo servidor.

Esta es una vulnerabilidad crítica porque... Las solicitudes también contienen cookies de sesión. Unas horas más tarde, se realizó una solución temporal que filtró el byte cero (como resultó más tarde, esto no fue suficiente, porque todavía existía la posibilidad de inyectar CRLF / ASCII 13, 10, que le permite manipular los encabezados y datos de la respuesta HTTP, esto es menos crítico, pero sigue siendo desagradable). Al mismo tiempo, el problema se transfirió a analistas y desarrolladores de seguridad para encontrar y eliminar las causas del error.

Mail.ru mail es una aplicación muy compleja; para generar la respuesta pueden participar una gran cantidad de diferentes componentes front-end/back-end, tanto de código abierto (muchas gracias a todos los desarrolladores de software libre) como desarrollados internamente. Logramos excluir todos los componentes excepto nginx y openresty y localizar el problema antes de llamar ngx.req.set_uri() en un script OpenResty que no se comportó como se esperaba (insertando un byte nulo o un avance de línea a través de parámetros GET con reescritura en ngx_http_rewrite_module, que, según la documentación, se usa y, al parecer, debería funcionar exactamente de la misma manera, no trabajo). Se eliminaron las posibles consecuencias, se agregó un filtrado lo más estricto posible y se verificó el filtrado para eliminar todos los vectores posibles. Pero el mecanismo que provocó la filtración del contenido de la memoria seguía 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 hasta tiempos mejores.

OpenResty es un complemento muy popular que permite escribir scripts 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, finalmente volvieron a él para comprender las verdaderas razones, las posibles consecuencias y hacer recomendaciones a los desarrolladores. Participó en la excavación del código fuente. Denis Denisov и Nikolai Ermishkin. Resultó que:

  • En nginx, cuando se utiliza la reescritura con datos de usuario, existe la posibilidad de que se cruce el directorio (y probablemente SSRF) en algunas configuraciones, pero esto es un hecho conocido y debe ser detectado por analizadores de configuración estática en Nginx Amplificar и gixy de Yandex (sí, también lo usamos, gracias). Cuando se usa OpenResty, es fácil pasar por alto esta característica, pero esto no afectó nuestra configuración.

    ejemplo de configuración:

    location ~ /rewrite {
        rewrite ^.*$ $arg_x;
    }
    
    location / {
        root html;
        index index.html index.htm;
    }

    resultar

    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
    ...

  • Nginx tiene un error que provoca pérdidas de memoria si la línea de reescritura contiene un byte nulo. Cuando se emite una redirección, nginx asigna un nuevo búfer de memoria correspondiente a la longitud total de la línea, pero copia la línea allí a través de una función de línea en la que el byte cero es un terminador de línea, por lo que la línea se copia solo hasta el cero. byte; el resto del búfer contiene datos no inicializados. Se puede encontrar un análisis detallado. aquí.

    ejemplo de configuración (^@ byte cero)

    
    location ~ /memleak {
        rewrite ^.*$ "^@asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdasdf";
    }
    
    location / {
        root html;
        index index.html index.htm;
    }

    resultar
    curl localhost:8337/secret -vv
    ...
    curl localhost:8337/memleak -vv
    ...
    Location: http://localhost:8337/secret
    ...

  • Nginx protege los parámetros GET de la inyección de caracteres de servicio y permite utilizar solo parámetros GET en la reescritura. Por lo tanto, no es posible 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 le permite trabajar con parámetros GET y POST, por lo que cuando utiliza parámetros POST a través de OpenResty, es posible inyectar 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=%00asdfasdfasdfasdfasdfasdfasdfasdf" -vv
    ...
    Location: http://localhost:8337/{...может содержать secret...}
    ...

Mayor reacción

El problema se informó a los desarrolladores de nginx y OpenResty, los desarrolladores no consideran el problema como un error de seguridad en nginx, porque en nginx en sí no hay forma de explotar el error mediante la inyección de caracteres especiales, solucione divulgación de la memoria fue publicado el 16 de diciembre. En los 4 meses posteriores al informe, no se realizaron cambios en OpenResty, aunque se entendió que se necesitaba una versión segura de la función ngx.req.set_uri(). El 18 de marzo de 2020 publicamos información, el 21 de marzo se lanzó OpenResty versión 1.15.8.3, que agrega validación de URI.

Portador написал buen artículo y tomó comentarios de OpenResty y Nginx (aunque el comentario de que solo se expone un pequeño fragmento de memoria es incorrecto y engañoso, esto está determinado por la longitud de la línea que sigue al byte nulo y, en ausencia de restricciones explícitas en el longitud, puede ser controlado por el atacante).

Entonces, ¿cuál fue el error y qué se puede hacer para evitarlo?

¿Hubo un error en nginx? Sí, lo fue, porque la pérdida de contenido de la memoria es un error en cualquier caso.

¿Hubo algún error en OpenResty? Sí, al menos el tema de la seguridad de la funcionalidad ofrecida por OpenResty no ha sido investigado ni documentado.

¿Hubo un error de configuración/uso con OpenResty? Sí, porque a falta de una declaración explícita, se hizo una suposición no verificada sobre la seguridad de la funcionalidad que se estaba utilizando.

¿Cuál de estos errores es una vulnerabilidad de seguridad con una recompensa de 10000 dólares? Para nosotros esto generalmente no es importante. En cualquier software, especialmente en la intersección de varios componentes, especialmente aquellos proporcionados por diferentes proyectos y desarrolladores, nadie puede garantizar que todas las características de su trabajo sean conocidas y documentadas y que no haya errores. Por lo tanto, cualquier vulnerabilidad de seguridad ocurre exactamente donde afecta la seguridad.

En cualquier caso, es una buena práctica normalizar o limitar/filtrar tanto como sea posible los datos de entrada que entran en cualquier módulo/API externo, a menos que existan instrucciones explícitas y un entendimiento claro de que esto no es necesario.

Erratas

Por experiencia artículo anterior, en aras de preservar la pureza del idioma:

recompensa de errores — concurso de caza de errores
informe de error - notificación de error
redirigir - redirección
fuente abierta - fuente abierta
Mal - trabajar en los errores

Fuente: habr.com

Añadir un comentario