
Um ano atrás, em 21 de março de 2019, na no HackerOne, surgiu uma boa de . Ao inserir um byte nulo (ASCII 0) em um parâmetro POST de uma das requisições API do webmail, que retornava um redirecionamento HTTP, fragmentos de memória não inicializada apareciam nos dados de redirecionamento, frequentemente revelando partes de parâmetros GET e cabeçalhos de outras requisições para o mesmo servidor.
Essa é uma vulnerabilidade crítica, pois as requisições contêm cookies de sessão. Algumas horas depois, um fix temporário foi implementado, filtrando o byte nulo (como mais tarde foi descoberto, isso não foi suficiente, pois ainda havia possibilidade de injeção de CRLF / ASCII 13, 10, o que permite manipular cabeçalhos e dados da resposta HTTP, isso é menos crítico, mas ainda assim desagradável). Simultaneamente, o problema foi transmitido para analistas de segurança e desenvolvedores para identificação e correção das causas da falha.
O Mail.ru é um aplicativo bastante complexo, na formação da resposta pode participar uma grande quantidade de diferentes componentes frontend/backend, tanto de código aberto (muito obrigado a todos os desenvolvedores de software livre), quanto de desenvolvimento próprio. Conseguimos isolar todos os componentes, exceto nginx e openresty, e localizar o problema até a chamada no script OpenResty, que se comportava de maneira inesperada (não é possível inserir um byte nulo ou linha nova através de parâmetros GET com rewrite no ngx_http_rewrite_module, que, de acordo com a documentação, é usado e, aparentemente, deveria funcionar de maneira idêntica). Possíveis consequências foram eliminadas, uma filtragem rigorosa foi implementada e foi verificado que a filtragem eliminava todos os possíveis vetores. Mas o mecanismo que levava ao vazamento de conteúdo de memória permaneceu um mistério. Um mês depois, o relatório de bug foi fechado como resolvido, enquanto a análise das causas da falha foi adiada para um momento mais oportuno.
OpenResty é um plugin bastante popular que permite escrever scripts Lua dentro do nginx, e é utilizado em vários projetos da Mail.ru, portanto, o problema não foi considerado resolvido. E, após algum tempo, ele foi revisitado para entender as verdadeiras causas, as possíveis consequências e elaborar recomendações para os desenvolvedores. A investigação do código-fonte contou com e . Foi descoberto que:
- No nginx, ao usar rewrite com dados do usuário, há a possibilidade de traversal de diretórios (e possivelmente SSRF) em algumas configurações, mas isso é um fato conhecido, e deve ser detectado por analisadores estáticos de configurações em e do Yandex (sim, nós também o utilizamos, obrigado). Ao usar OpenResty, essa possibilidade pode ser facilmente negligenciada, mas a nossa configuração não foi afetada.
exemplo de configuração:
location ~ /rewrite { rewrite ^.*$ $arg_x; } location / { root html; index index.html index.htm; }o 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
... - No nginx há um bug que leva ao vazamento de conteúdo da memória, se a string de rewrite contém um byte nulo. Ao devolver um redirecionamento, o nginx aloca um novo buffer de memória correspondente ao comprimento total da string, mas copia a string por meio de uma função de string, onde o byte nulo é o terminador da string, portanto, a string é copiada apenas até o byte nulo, e o restante do buffer contém dados não inicializados. Uma análise detalhada pode ser encontrada .
exemplo de configuração (^@ byte nulo)
location ~ /memleak { rewrite ^.*$ "^@asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdasdf"; } location / { root html; index index.html index.htm; }o resultado
curl localhost:8337/secret -vv
...
curl localhost:8337/memleak -vv
...
Localização: http://localhost:8337/secret
...
- O Nginx protege os parâmetros GET contra injeção de caracteres especiais e permite usar apenas parâmetros GET na rewrite. Portanto, explorar a injeção através de parâmetros controlados pelo usuário no nginx não é possível. Os parâmetros POST, por outro lado, não estão protegidos. OpenResty permite trabalhar tanto com parâmetros GET quanto POST, portanto, ao usar parâmetros POST através do OpenResty, há a possibilidade de injeção de caracteres especiais.
exemplo de configuração:
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
...
Localização: http://localhost:8337/{...pode conter secret...}
...
Reação subsequente
O problema foi reportado aos desenvolvedores do nginx e do OpenResty, os desenvolvedores não consideram o problema um erro de segurança no nginx, pois no próprio nginx não há possibilidade de exploração do erro por meio da injeção de caracteres especiais, correção foi publicada em 16 de dezembro. Em 4 meses desde o relatório, no OpenResty também não foram feitas quaisquer alterações, embora houvesse uma compreensão de que uma versão segura da função ngx.req.set_uri() era necessária. Em 18 de março de 2020, publicamos informações, em 21 de março, o OpenResty lançou , que adiciona uma verificação de URI.
Portswigger um bom artigo e pegou comentários do OpenResty e Nginx (sendo que o comentário de que apenas uma pequena parte da memória é revelada é incorreto e pode levar a mal-entendidos, isso é determinado pelo comprimento da string que segue o byte nulo e, na ausência de restrições explícitas de comprimento, pode ser controlado pelo atacante).
Qual foi o erro e o que fazer para evitá-lo?
Houve erro no nginx? Sim, houve, porque o vazamento de conteúdo da memória é, em qualquer caso, um erro.
Houve erro no OpenResty? Sim, no mínimo a questão da segurança da funcionalidade oferecida pelo OpenResty não foi investigada e documentada.
Foi cometido um erro de configuração / uso do OpenResty? Sim, porque na ausência de uma indicação explícita, foi feita uma suposição não verificada sobre a segurança da funcionalidade utilizada.
Qual desses erros é uma vulnerabilidade de segurança com um prêmio de $10000? Para nós, isso não é realmente importante. Em qualquer software, especialmente na interseção de vários componentes, especialmente aqueles fornecidos por diferentes projetos e desenvolvedores, ninguém pode garantir que todas as particularidades de seu funcionamento são conhecidas e documentadas e que não existem erros. Portanto, qualquer vulnerabilidade de segurança surge exatamente onde afeta a segurança.
De qualquer forma, é uma boa prática normalizar ou restringir ao máximo / filtrar os dados de entrada que vão para qualquer módulo / API externa, caso não haja indicações explícitas e entendimento claro de que isso não é necessário.
Errata
Pela experiência , para manter a pureza da linguagem:
bug bounty — concurso de caça a erros
relatório de bug — notificação de erro
redirecionamento — encaminhamento
código aberto — com código aberto
errata — trabalho sobre erros
Fonte: habr.com
