Pergunta

Alguém pode apontar algum código que lide com a segurança do acesso a arquivos através de um caminho especificado (em parte) por uma variável de ambiente, especificamente para Unix e suas variantes, mas as soluções Windows também são de interesse?

Esta é uma grande questão - não tenho certeza de quão bem ela se encaixa no paradigma SO.

Considere este cenário:

Fundo:

  • O pacote de software PQR pode ser instalado em local escolhido pelos usuários.
  • A variável de ambiente $PQRHOME é usada para identificar o diretório de instalação.
  • Por padrão, todos os programas e arquivos em $PQRHOME pertencem a um grupo especial, pqrgrp.
  • Da mesma forma, todos os programas e arquivos em $PQRHOME pertencem a um usuário especial, pqrusr, ou ao usuário root (e esses são programas root SUID).
  • Alguns programas são SUID pqrusr;mais alguns programas são SGID pqrgrp.
  • A maioria dos diretórios pertence ao pqrusr e pertence ao pqrgrp;alguns podem pertencer a outros grupos e os membros desses grupos adquirem privilégios extras com o software.
  • Muitos dos executáveis ​​privilegiados devem ser executados por pessoas que não são membros do pqrgrp;os programas têm que validar que o usuário tem permissão para executá-los por regras misteriosas que não dizem respeito diretamente a esta questão.
  • Após a inicialização, alguns dos programas privilegiados precisam reter seus privilégios elevados porque são daemons de longa duração que podem atuar em nome de muitos usuários durante sua vida útil.
  • Os programas não estão autorizados a mudar o diretório para $PQRHOME por vários motivos misteriosos.

Verificação atual:

  • Os programas atualmente verificam se $PQRHOME e os diretórios principais abaixo dele são 'seguros' (de propriedade do pqrusr, pertencem ao pqrgrp, não têm acesso público de gravação).
  • Depois disso, os programas acessam os arquivos em $PQRHOME por meio do valor total da variável de ambiente.
  • Em particular, G11N e L10N são obtidos acessando arquivos em diretórios 'seguros' e lendo strings de formato para printf() etc. dos arquivos nesses diretórios, usando o nome do caminho completo derivado de $PQRHOME mais uma subestrutura conhecida ( por exemplo, $PQRHOME/g11n/en_us/messages.l10n).

Suponha que o valor 'como instalado' de $PQRHOME seja /opt/pqr.

Ataque conhecido:

  • O invasor define PQRHOME=/home/attacker/pqr.
  • Na verdade, este é um link simbólico para /opt/pqr, portanto, quando um dos programas PQR, chame-o de pqr-victim, verifica o diretório, ele tem as permissões corretas.
  • Imediatamente após a verificação de segurança ser concluída com êxito, o invasor altera o link simbólico para que aponte para /home/attacker/bogus-pqr, que está claramente sob o controle do invasor.
  • Coisas terríveis acontecem quando a vítima do pqr agora acessa um arquivo no diretório supostamente seguro.

Dado que o PQR atualmente se comporta conforme descrito e é um pacote grande (vários milhões de linhas de código, desenvolvido ao longo de mais de uma década para uma variedade de padrões de codificação, que eram frequentemente ignorados, de qualquer maneira), quais técnicas você usaria para remediar o problema? problema?

As opções conhecidas incluem:

  1. Altere todas as chamadas de formatação para usar a função que verifica os argumentos reais em relação às strings de formato, com um argumento extra indicando os tipos reais passados ​​para a função.(Isso é complicado e potencialmente sujeito a erros devido ao grande número de operações de formato a serem alteradas - mas se a função de verificação for correta, funciona bem.)
  2. Estabeleça o caminho direto para PQRHOME e valide-o para segurança (detalhes abaixo), recusando iniciar se não for seguro, e a partir daí utilizando o caminho direto e não o valor de $PQRHOME (quando forem diferentes).(Isso requer que todas as operações de arquivo que usam $PQRHOME usem não o valor de getenv() mas o caminho mapeado.Por exemplo, isso exigiria que o software estabelecesse que /home/attacker/pqr é um link simbólico para /opt/pqr, que o caminho para /opt/pqr é seguro e, posteriormente, sempre que um arquivo for referenciado como $PQRHOME/some /thing, o nome usado seria /opt/pqr/some/thing e não /home/attacker/pqr/some/thing.Esta é uma grande base de código - não é trivial de corrigir.)
  3. Certifique-se de que todos os diretórios em $PQRHOME, mesmo o rastreamento por meio de links simbólicos, sejam seguros (detalhes abaixo, novamente), e que o software se recuse a iniciar se algo for inseguro.
  4. Codifique o caminho para o local de instalação do software.(Isso não funcionará PQR;isso torna os testes um inferno, pelo menos.Para os usuários, isso significa que eles podem ter apenas uma versão instalada e atualizações, etc., requerem execução paralela.Isso não funciona para PQR.)

Critérios propostos para caminhos seguros:

  • Para cada diretório, o proprietário deve ser confiável.(Justificativa:o proprietário pode alterar as permissões a qualquer momento, portanto, deve-se confiar que o proprietário não fará alterações aleatórias que quebrem a segurança do software.)
  • Para cada diretório, o grupo não deve ter privilégios de gravação (para que os membros do grupo não possam modificar o conteúdo do diretório) ou o grupo deve ser confiável.(Justificativa:se os membros do grupo puderem modificar o diretório, eles poderão quebrar a segurança do software; portanto, eles não poderão alterá-lo ou deverão ser confiáveis ​​para não alterá-lo.)
  • Para cada diretório, 'outros' não devem ter privilégio de gravação no diretório.
  • Por padrão, os usuários root, bin, sys e pqrusr podem ser confiáveis ​​(onde bin e sys existem).
  • Por padrão, o grupo com GID=0 (também conhecido como root, wheel ou system), bin, sys e pqrgrp pode ser confiável.Além disso, o grupo que possui o diretório raiz (chamado admin no MacOS X) pode ser confiável.

A função POSIX realpath() fornece um serviço de mapeamento que mapeará /home/attacker/pqr para /opt/pqr;ele não faz a verificação de segurança, mas isso só precisa ser feito no caminho resolvido.

Então, com tudo isso como pano de fundo, existe algum software conhecido que passe por movimentos vagamente relacionados para garantir sua segurança?Isso é ser excessivamente paranóico?(Se sim, por que - e você tem certeza?)

Editado:

Obrigado pelos vários comentários.

@S.Lott:O ataque (descrito na pergunta) significa que pelo menos um programa root setuid pode ser feito para usar uma string de formato da escolha do usuário (sem privilégios) e pode pelo menos travar o programa e, portanto, provavelmente pode adquirir um shell root.Felizmente, requer acesso shell local;não é um ataque remoto.É necessário um conhecimento não negligenciável para chegar lá, mas considero imprudente presumir que a experiência não está “lá fora”.

Então, o que estou descrevendo é uma 'vulnerabilidade de string de formato' e o caminho de ataque conhecido envolve falsificar o programa para que, embora ele pense que está acessando arquivos de mensagens seguros, ele realmente usa os arquivos de mensagens (que contêm strings de formato) que estão sob o controle do usuário e não sob o controle do software.

Foi útil?

Solução

A opção 2 funciona, se você escrever um novo valor para $PQRHOME após resolver seu caminho real e verificar sua segurança.Dessa forma, muito pouco do seu código precisa ser alterado posteriormente.

No que diz respeito a manter os privilégios do setuid, ajudaria se você pudesse fazer algum tipo de separação de privilégios, para que quaisquer operações que envolvam entrada do usuário real sejam executadas sob o uid real.O processo privilegiado e o processo UID real então se comunicam usando um socketpair ou algo parecido.

Outras dicas

Bem sons paranóico, mas se for ou não, depende de quais sistemas seu aplicativo está sendo executado e quais danos um invasor pode causar.

Portanto, se sua base de usuários for possivelmente hostil e se o dano for possivelmente muito alto, eu escolheria a opção 4, mas modificada da seguinte forma para remover suas desvantagens.

Deixe-me citar duas coisas relevantes:

1)

Atualmente, os programas verificam que $ PQRHOME e os principais diretórios sob ele são 'seguros' (de propriedade da PQRUSR, pertencem ao PQRGRP, não têm acesso público a gravação).

2)

Posteriormente, os programas acessam arquivos sob $ pqrhome através do valor total da variável de ambiente.

Você não precisa codificar o caminho completo, você pode codificar apenas o caminho relativo do "programa" mencionado em 1) até o caminho mencionado em 2) onde estão os arquivos.

Problema a controlar:

a) você deve ter certeza de que não há nada "acessível ao invasor" (por exemplo,em termos de links simbólicos) entre o caminho do executável e o caminho dos arquivos

b) você deve ter certeza de que o executável verifica seu próprio caminho de maneira confiável, mas isso não deve ser um problema em todos os Unix que conheço (mas não conheço todos eles e não conheço o Windows de forma alguma).


EDITADO após o 3º comentário:

Se o seu sistema operacional suporta /proc, o syslink /proc/${pid}/exe é a melhor maneira de resolver b)


EDITADO depois de dormir:

A instalação é um processo "seguro"?Nesse caso, você pode criar (no momento da instalação) um script wrapper.Este script deve ser executável, mas não gravável (e possivelmente nem legível).Ele definiria o $PQRHOME env var como o valor "seguro" e, em seguida, chamaria seu programa real (pode eventualmente fazer outras coisas úteis também).Já que no UNIX os env vars de um processo em execução não pode ser alterado por qualquer outra coisa, exceto o processo em execução, você está seguro (é claro que o env vars pode ser alterado pelos pais antes o processo começa).Não sei se essa abordagem funciona no Windows.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top