Sobre unha vulnerabilidade en...

Sobre unha vulnerabilidade en...

Hai un ano, 21 de marzo de 2019, ás programa de recompensa de erros Mail.Ru un moi bo chegou a HackerOne informe de erros de maxarr. Ao introducir un byte cero (ASCII 0) no parámetro POST dunha das solicitudes da API de correo web que devolveu unha redirección HTTP, anacos de memoria non inicializada eran visibles nos datos de redirección, nos que fragmentos dos parámetros GET e cabeceiras doutras solicitudes ao mesmo servidor.

Esta é unha vulnerabilidade crítica porque... as solicitudes tamén conteñen cookies de sesión. Poucas horas despois, realizouse unha corrección temporal que filtraba o byte cero (como se viu máis tarde, isto non foi suficiente, porque aínda existía a posibilidade de inxectar CRLF/ASCII 13, 10, que permite manipular as cabeceiras e datos da resposta HTTP, isto é menos crítico, pero aínda desagradable). Ao mesmo tempo, o problema foi transferido a analistas de seguridade e desenvolvedores para atopar e eliminar as causas do erro.

O correo de Mail.ru é unha aplicación moi complexa; un gran número de compoñentes front-end/back-end diferentes, tanto de código aberto (moitas grazas a todos os desenvolvedores de software libre) como desenvolvidos internamente, poden estar implicados na xeración da resposta. Conseguimos excluír todos os compoñentes excepto nginx e openresty e localizar o problema antes de chamar ngx.req.set_uri() nun script OpenResty que non se comportou como se esperaba (inserir un byte nulo ou un avance de liña mediante parámetros GET con reescritura en ngx_http_rewrite_module, que, segundo a documentación, se usa e, ao parecer, debería funcionar exactamente do mesmo xeito, non funciona). Elimináronse as posibles consecuencias, engadiuse o filtrado o máis estrito posible e verificouse o filtrado para eliminar todos os vectores posibles. Pero o mecanismo que levou á fuga dos contidos da memoria seguía sendo un misterio. Un mes despois, o informe de erros foi pechado como resolto, e a análise das causas do erro foi aprazada ata tempos mellores.

OpenResty é un complemento moi popular que permite escribir scripts Lua dentro de nginx, e úsase en varios proxectos de Mail.ru, polo que o problema non se considerou resolto. E despois dun tempo, finalmente volveron a el para comprender as verdadeiras razóns, as posibles consecuencias e facer recomendacións para os desenvolvedores. Participou na escavación do código fonte Denis Denisov и Nikolay Ermishkin. Resultou que:

  • En nginx, ao usar a reescritura con datos de usuario, existe a posibilidade de atravesar directorios (e probablemente SSRF) nalgunhas configuracións, pero este é un feito coñecido e debería ser detectado polos analizadores de configuración estáticos en nginx. Nginx Amplify и gixy de Yandex (si, usamos iso tamén, grazas). Ao usar OpenResty, esta función é fácil de perder, pero isto non afectou á nosa configuración.

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

  • Nginx ten un erro que fai que a memoria perda se a liña de reescritura contén un byte nulo. Cando se emite unha redirección, nginx asigna un novo búfer de memoria correspondente á lonxitude total da liña, pero copia a liña alí a través dunha función de liña na que o byte cero é un terminador de liña, polo que a liña só se copia ata o cero. byte; o resto do búfer contén datos non inicializados. Pódese atopar unha análise detallada aquí.

    exemplo de configuración (^@ cero byte)

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

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

  • Nginx protexe os parámetros GET da inxección de caracteres de servizo e fai posible usar só os parámetros GET na reescritura. Polo tanto, non é posible explotar a inxección mediante parámetros controlados polo usuario en nginx. Os parámetros POST non están protexidos. OpenResty permítelle traballar con parámetros GET e POST, polo que cando se usan parámetros POST a través de OpenResty, faise posible inxectar caracteres especiais.

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

Máis reacción

O problema foi informado aos desenvolvedores de nginx e OpenResty, os desenvolvedores non consideran o problema como un erro de seguridade en nginx, porque no propio nginx non hai forma de explotar o erro mediante a inxección de caracteres especiais, corrixir divulgación da memoria publicouse o 16 de decembro. Nos catro meses transcorridos desde o informe, non se realizaron cambios en OpenResty, aínda que se entendía que era necesaria unha versión segura da función ngx.req.set_uri(). O 4 de marzo de 18 publicamos información, o 2020 de marzo lanzouse OpenResty versión 1.15.8.3, que engade a validación de URI.

Portswigger escribiu bo artigo e levou comentarios de OpenResty e Nginx (aínda que o comentario de que só se expón un pequeno fragmento de memoria é incorrecto e enganoso, está determinado pola lonxitude da liña que segue ao byte nulo e, en ausencia de restricións explícitas sobre o lonxitude, pode ser controlada polo atacante).

Entón, cal foi o erro e que se pode facer para evitalo?

Houbo un erro en nginx? Si, foi así, porque unha fuga de memoria é un erro en calquera caso.

Houbo un erro en OpenResty? Si, polo menos non se investigou nin se documentou a cuestión da seguridade da funcionalidade que ofrece OpenResty.

Houbo un erro de configuración/uso con OpenResty? Si, porque a falta dunha declaración explícita, realizouse unha suposición non verificada sobre a seguridade da funcionalidade que se está a utilizar.

Cal destes erros é unha vulnerabilidade de seguridade cunha recompensa de 10000 dólares? Para nós, isto xeralmente non é importante. En calquera software, especialmente na intersección de varios compoñentes, especialmente os proporcionados por diferentes proxectos e desenvolvedores, ninguén pode nunca garantir que todas as características do seu traballo sexan coñecidas e documentadas e que non haxa erros. Polo tanto, calquera vulnerabilidade de seguridade ocorre exactamente onde afecta á seguridade.

En calquera caso, é unha boa práctica normalizar ou limitar/filtrar na medida do posible os datos de entrada que entran en calquera módulo/API externo, a non ser que existan instrucións explícitas e se entenda claramente que non é necesario.

Errata

Por experiencia artigo anterior, en aras de preservar a pureza da lingua:

recompensa de erros - Concurso de caza de bichos
informe de erros - notificación de erro
redireccionar - redirección
código aberto - código aberto
errata - Traballar os erros

Fonte: www.habr.com

Engadir un comentario