O Linux ignora o bit setuid¹ em todos os executáveis interpretados (ou seja, executáveis iniciando com uma #!
linha). As perguntas frequentes do comp.unix.questions explicam os problemas de segurança com scripts shell setuid. Esses problemas são de dois tipos: relacionados ao shebang e ao shell; Entro em mais detalhes abaixo.
Se você não se importa com a segurança e deseja permitir scripts setuid, no Linux, precisará corrigir o kernel. No kernel 3.x, acho que você precisa adicionar uma chamada install_exec_creds
na load_script
função antes da chamada open_exec
, mas ainda não testei.
Setuid shebang
Há uma condição de corrida inerente à maneira #!
como normalmente é implementado o shebang ( ):
- O kernel abre o executável e descobre que ele começa com
#!
.
- O kernel fecha o executável e abre o intérprete.
- O kernel insere o caminho para o script na lista de argumentos (como
argv[1]
) e executa o intérprete.
Se scripts setuid forem permitidos com esta implementação, um invasor poderá chamar um script arbitrário criando um link simbólico para um script setuid existente, executando-o e organizando a alteração do link após o kernel ter executado a etapa 1 e antes que o intérprete chegue ao abrindo seu primeiro argumento. Por esta razão, maioria das unidades ignora o bit setuid quando detecta um shebang.
Uma maneira de proteger essa implementação seria o kernel bloquear o arquivo de script até que o intérprete o abrisse (observe que isso deve impedir não apenas desvincular ou sobrescrever o arquivo, mas também renomear qualquer diretório no caminho). Mas os sistemas unix tendem a evitar os bloqueios obrigatórios, e os links simbólicos tornariam um recurso de bloqueio correto especialmente difícil e invasivo. Eu não acho que alguém faça dessa maneira.
Alguns sistemas unix (principalmente o OpenBSD, o NetBSD e o Mac OS X, que exigem a habilitação de uma configuração do kernel) implementam o shebang do setuid seguro usando um recurso adicional: o caminho refere-se ao arquivo já aberto no descritor de arquivo N (portanto, a abertura é aproximadamente equivalente a ). Muitos sistemas unix (incluindo Linux) possuem/dev/fd/N
/dev/fd/N
dup(N)
/dev/fd
mas não scripts setuid.
- O kernel abre o executável e descobre que ele começa com
#!
. Digamos que o descritor de arquivo do executável seja 3.
- O kernel abre o intérprete.
- O kernel insere
/dev/fd/3
a lista de argumentos (as argv[1]
) e executa o intérprete.
A página shebang de Sven Mascheck tem muitas informações sobre shebang nos departamentos, incluindo suporte a setuid .
Intérpretes Setuid
Vamos supor que você tenha conseguido executar seu programa como root, seja porque o seu sistema operacional suporta setuid shebang ou porque você usou um invólucro binário nativo (como sudo
). Você abriu uma brecha na segurança? Talvez . O problema aqui não é sobre programas interpretados versus compilados. O problema é se o sistema de tempo de execução se comporta com segurança se executado com privilégios.
Qualquer executável binário nativo vinculado dinamicamente é interpretado pelo carregador dinâmico (por exemplo /lib/ld.so
), que carrega as bibliotecas dinâmicas exigidas pelo programa. Em muitos departamentos, você pode configurar o caminho de pesquisa para bibliotecas dinâmicas por meio do ambiente ( LD_LIBRARY_PATH
é um nome comum para a variável de ambiente) e até carregar bibliotecas adicionais em todos os binários executados ( LD_PRELOAD
). O invocador do programa pode executar código arbitrário no contexto desse programa, colocando um especialmente criado libc.so
em $LD_LIBRARY_PATH
(entre outras táticas). Todos os sistemas sãos ignoram as LD_*
variáveis nos executáveis setuid.
Em shells como sh, csh e derivadas, as variáveis de ambiente se tornam automaticamente parâmetros de shell. Por meio de parâmetros como PATH
, IFS
e muitos mais, o invocador do script tem muitas oportunidades para executar código arbitrário no contexto dos scripts de shell. Alguns shells definem essas variáveis como padrões saudáveis, se detectarem que o script foi invocado com privilégios, mas não sei se há alguma implementação específica em que eu confiaria.
A maioria dos ambientes de tempo de execução (nativos, bytecode ou interpretados) possui recursos semelhantes. Poucos tomam precauções especiais em executáveis setuid, embora os que executam código nativo geralmente não façam nada mais sofisticado do que a vinculação dinâmica (que toma precauções).
Perl é uma exceção notável. Ele suporta explicitamente scripts setuid de maneira segura. De fato, seu script pode executar o setuid, mesmo que seu SO tenha ignorado o bit setuid nos scripts. Isso ocorre porque o perl é fornecido com um assistente raiz setuid que executa as verificações necessárias e reinicia o intérprete nos scripts desejados com os privilégios desejados. Isso é explicado no manual do perlsec . Antes, os scripts setuid perl necessários, em #!/usr/bin/suidperl -wT
vez de #!/usr/bin/perl -wT
, mas na maioria dos sistemas modernos, #!/usr/bin/perl -wT
são suficientes.
Observe que o uso de um wrapper binário nativo não faz nada por si só para evitar esses problemas . Na verdade, ele pode tornar a situação pior , porque pode evitar que o seu ambiente de tempo de execução de detectar que é invocada com privilégios e ignorando sua configurabilidade execução.
Um wrapper binário nativo pode tornar um script de shell seguro se o wrapper higienizar o ambiente . O script deve tomar cuidado para não fazer muitas suposições (por exemplo, sobre o diretório atual), mas isso vale. Você pode usar o sudo para isso, desde que esteja configurado para higienizar o ambiente. As variáveis da lista negra estão sujeitas a erros, portanto, sempre use a lista de permissões. Com sudo, certifique-se de que a env_reset
opção está ligada, que setenv
está desligado, e que env_file
e env_keep
contêm apenas variáveis inócuos.
TL, DR:
- Shebang Setuid é inseguro, mas geralmente ignorado.
- Se você executar um programa com privilégios (por meio do sudo ou setuid), escreva código nativo ou perl ou inicie o programa com um wrapper que higienize o ambiente (como o sudo com a
env_reset
opção).
Discussion Esta discussão se aplica igualmente se você substituir “setgid” por “setuid”; ambos são ignorados pelo kernel do Linux em scripts