1 pontos por GN⁺ 2023-07-04 | 1 comentários | Compartilhar no WhatsApp
  • Nginx, um servidor web versátil com participação de mercado dominante desde 2004
  • Configurações incorretas do Nginx podem levar a vulnerabilidades de segurança e exposição de dados
  • Apresentação do NavGix, uma ferramenta automatizada para detectar vulnerabilidades no Nginx
  • Essa vulnerabilidade pode permitir acesso a arquivos e diretórios além do escopo pretendido
  • Estudos de caso sobre o Bitwarden e o HPC Toolkit do Google mostram a gravidade dessa vulnerabilidade

1 comentários

 
GN⁺ 2023-07-04
Comentários do Hacker News
  • Para referência, o gixy (verificador de configuração do nginx) detecta esse problema: https://github.com/yandex/gixy/blob/master/docs/en/plugins/a...
    E o NixOS executava o gixy automaticamente nas configurações geradas por ele, recusando a build do sistema se houvesse algum problema

    • Se um servidor web precisa de ferramentas extras para que os usuários evitem armadilhas como essa, talvez seja hora de repensar os padrões
    • Eu não conhecia o gixy, mas rodei no meu home server e apareceu uma vulnerabilidade. Eu estava usando $uri em um redirecionamento 301
    • Testei o Nix uma vez e, até agora, parece bem interessante
      Mas fico curioso se existe um buscador de opções melhor. Não gostei muito de pesquisar em milhares de opções de uma vez; eu queria ver apenas um pacote como ssh e só as opções relacionadas a ele, mas aparecem resultados irrelevantes demais
    • O NixOS não executa mais o Gixy. Veja https://github.com/NixOS/nixpkgs/pull/209075
  • Pode ser uma pergunta boba, mas existe algum bom motivo para o nginx permitir subir para o diretório pai com .. no caminho da URL? Isso parece só um comportamento esperando para dar problema
    Edit: ainda estou um pouco confuso sobre o que acontece exatamente na vulnerabilidade original. Parece que http://localhost/foo../secretfile.txt é interpretado como /var/www/foo/../secretfile.txt, mas então, em um servidor sem a vulnerabilidade, por que http://localhost/foo/../secretfile.txt não seria interpretado da mesma forma? Não entendo por que .. dentro do caminho só funciona em alguns casos

    • Isso é um problema conhecido no nginx há muito tempo e é um vetor de ataque comum em CTFs: https://book.hacktricks.xyz/network-services-pentesting/pent...
    • O problema é que URL não é caminho real. URL é um endereço abstrato de um recurso, que pode ser diretório, arquivo, executável, stream etc.
      Nesse caso, parte da URL é interpretada como um diretório (http://localhost/foo) pelo nginx por causa de como o mapeamento para o sistema de arquivos local foi definido na configuração. Como isso parece apontar para um diretório, ao montar o caminho completo do recurso solicitado o nginx acaba com "${mapped_path}/../secretfile.txt", que pode não fazer sentido do ponto de vista da URL, mas é válido no sistema de arquivos local. URL é só uma string, não um conjunto real de componentes de caminho, então a posição das barras também não é intrinsecamente importante
      Isso é algo muito comum em servidores web desde o início da web. Mapear URL diretamente para caminho de arquivo ficou popular porque começou com servidores simples de arquivos com índice, mas logo evoluiu para ambientes híbridos em que a URL deixa de ser um caminho e passa a ser um identificador de aplicação. Parte do caminho identifica o app, e o resto é tratado como parâmetro ou argumento
      E, de modo geral, respeitar . ou .. da URL para objetos do sistema de arquivos normalmente não faz sentido. Meus apps limpam o caminho da requisição para garantir um mapeamento correto. Navegadores tratam URLs como caminhos ao criar links relativos, então também é preciso ter cuidado com quando e como usar a / final. No lado do servidor, isso pode apontar para recursos com significado diferente
    • Em casos de uso “normais”, não há necessidade. Parece mais correto tornar isso um comportamento explicitamente ativado, com algum flag no location, tipo allow_parent_traversal on;
    • Meu palpite é que o NginX provavelmente verifica "/foo/bar/.." para bloquear ou normalizar para "/foo/". Mas "/foo/bar.." é um nome de arquivo totalmente válido, então parece escapar desse tipo de verificação
    • Quando .. funciona em um caminho depende inteiramente das permissões de arquivo
      Neste caso, suponha que a partir do diretório indexado pela web (../index.html) exista permissão de leitura até o diretório raiz (/). Se você tem permissão para subir até a raiz, então também passa a poder ver qualquer arquivo legível por todos a partir dela, como /etc/passwd
      Pense em um garfo de três pontas, com o servidor web na ponta da direita. Se a parte do cabo, onde as pontas se encontram, for o sistema de arquivos, então, quando o servidor web tem permissão para acessar os arquivos e diretórios do caminho da ponta direita até o cabo, ao chegar ao cabo ele também consegue continuar acessando arquivos das outras pontas
  • Não entendo por que isso não é visto como uma vulnerabilidade do nginx. Esse comportamento é completamente absurdo, não parece ter nenhuma utilidade e pode ser explorado imediatamente

    • Foi feito assim por causa de velocidade. Uma substituição simples de texto é muito mais rápida do que verificar se o caminho termina corretamente com barra
      Também vale lembrar que o nginx ganhou popularidade graças a benchmarks que o mostravam mais “web scale” do que o Apache2
  • Fiquei pensando em uma abordagem assim: colocar no parser de nomes de arquivo do kernel Linux uma opção parecida com uma capability do Linux para remover a possibilidade de ..
    Em apps web, desde a época do modem discado, continuam surgindo várias formas de burlar isso e enfiar dois pontos no caminho de algum jeito. Assim como o kernel Linux já fez com várias outras classes de bugs em espaço de usuário, talvez tenha chegado a hora de encontrar um jeito de fechar esse problema de uma vez

    • Existe o RESOLVE_BENEATH em https://man7.org/linux/man-pages/man2/openat2.2.html
      No FreeBSD, essa funcionalidade existe no openat(2) comum como O_RESOLVE_BENEATH
    • Isso quebraria coisa demais, não seria uma ideia muito sensata
      Dá para rodar o nginx com um usuário separado com privilégios bem limitados ou dentro de um Docker. Somando isso a atualizações regulares, normalmente 90% dos problemas de segurança já ficam resolvidos
    • /some/../path deveria ser proibido em quase 100% dos casos. Não existe um caso de uso razoável além de “alguém escreveu um código horroroso”
      ../some/path pelo menos às vezes faz sentido
      Ainda assim, talvez não fosse tão útil quanto parece. Muitos apps já resolvem .. antes de passar para o sistema operacional
    • Não mudaria muita coisa. O código com frequência normaliza o caminho antes de tocar na API do sistema de arquivos
    • Do lado do kernel já existe outro mecanismo, que é o sistema de permissões do qual dependemos
      Se você está servindo arquivos de uma pasta para a web, o framework web precisa garantir que não dá para escapar da pasta raiz pública que ele controla. Se você está fazendo isso manualmente, precisa considerar todo tipo de situação, inclusive essa
  • “A equipe do Google VRP deu uma recompensa de 500 dólares por ter encontrado essa vulnerabilidade. Considerou que o impacto na aplicação não era grave o bastante para justificar uma recompensa maior”, então exposição do e-mail da conta GCP e da chave privada vale só 500 dólares? Que história é essa. Bem a cara do Google mesmo

  • Ainda bem que o que vazou continuava criptografado. Se até uma empresa especializada nessa área não está livre de vazamentos, sinceramente esse parece o melhor cenário possível

  • O título foi bastante editado. O título original é Hunting for Nginx Alias Traversals in the wild
    O título enviado no HN destaca a vulnerabilidade do Bitwarden, mas o texto também cobre o caso do Google

    • Boa, reverti o título. O título enviado era “Leaking Bitwarden's Vault with a Nginx vulnerability”
  • Se a necessidade for apenas servir arquivos estáticos de forma segura e estável, com uso mínimo de recursos, qual seria hoje a melhor opção? Antigamente eu escolheria Nginx, mas do ponto de vista de segurança fico pensando se uma ferramenta mais específica e com menos possibilidades de configuração não seria melhor

    • Eu uso https://static-web-server.net/
      É multiplataforma, escrito em Rust, tem configuração simples e valores padrão seguros. Também há imagem de contêiner reforçada e módulo NixOS reforçado
      Não recomendo o Caddy. A imagem oficial de Docker roda como root por padrão [1], e eles também não fornecem um arquivo de unidade systemd devidamente sandboxed [2]
      [1]: https://github.com/caddyserver/caddy-docker/issues/104
      [2]: https://github.com/caddyserver/dist/blob/master/init/caddy.s...
      Edit: ajuste de redação
    • Fazendo um pouco de propaganda, o Caddy vai muito bem nisso. Ele oferece HTTPS automático, é escrito em Go, então não há preocupação com bugs de segurança de memória, e tem um módulo file_server sólido
    • Uso Caddy há anos. Ele oferece certificados SSL automáticos, serve arquivos, funciona como proxy reverso e é muito fácil e claro de configurar. Como é um binário único em Go, a “instalação” também é fácil e o arquivo de configuração é só um
    • O Caddy é bem simples de configurar para servir arquivos estáticos
    • Merecat: https://github.com/troglobit/merecat/
  • Talvez seja uma pergunta boba, mas por que o Bitwarden permitia requisições não autenticadas para /attachments em primeiro lugar? Mesmo com o bug do Nginx, se aquela URL exigisse autenticação, a requisição não falharia?

    • Esse exploit mira a configuração do servidor web, então o código de autenticação do Bitwarden, ou qualquer código do Bitwarden, nem chega a ser executado. Não é estranho nem errado que o projeto use autenticação própria em vez de Nginx ou de um módulo
      Ainda assim, como eles distribuíram uma configuração perigosa via Docker, isso de fato é responsabilidade do Bitwarden. E o Bitwarden aparentemente reconheceu isso e corrigiu depois
  • Perdoem a pergunta idiota. Com a propriedade adequada de diretórios e arquivos, esse tipo de varredura não seria bloqueado?
    Se o nginx não estiver rodando como root, como ele pode ler outros arquivos além daqueles explicitamente atribuídos ao usuário do nginx?

    • Claro que dá para bloquear. Rode o app com um usuário, o nginx com outro, aplique go-rwx em todos os arquivos do app, defina o grupo dos arquivos “static” como www-data e dê apenas g+r; assim o servidor web não consegue acessar os arquivos da aplicação
      Isso é literalmente hospedagem de aplicações 101, e as pessoas já faziam isso 20 anos atrás
    • Ah, as maravilhas do umask 022. Pessoalmente, eu sempre recomendo impedir que outros usuários possam ler os arquivos. Se for difícil fazer isso com todos, pelo menos faça em diretórios importantes como os que ficam sob /home
      Talvez seja preciso ajustar um pouco mais a associação a grupos, mas vale muito a pena
    • Não sei quanto aos outros, mas hoje em dia eu nem instalo nginx direito para uso pessoal. Só subo uma imagem Docker. E nem verifico se ela está rodando como root ou não
      Talvez eu esteja fazendo uma besteira enorme. Ai
    • O umask padrão costuma ser 022, então na maioria dos casos os workers do nginx conseguem ler, mas não escrever. Nem é preciso que os arquivos sejam explicitamente atribuídos a um usuário como www-data. Se a aplicação gera dados sensíveis, faz todo sentido usar umask 077
    • Isso mesmo
      Infelizmente, o nginx e outros servidores web geralmente precisam rodar como root em aplicações web comuns. Porque precisam escutar nas portas 80 ou 443. Portas abaixo de 1024 só podem ser abertas por root
      Há uma explicação mais detalhada aqui: https://unix.stackexchange.com/questions/134301/why-does-ngi...