O bit de permissão setuid
diz ao Linux para executar um programa com o ID de usuário efetivo do proprietário em vez do executor:
> cat setuid-test.c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv) {
printf("%d", geteuid());
return 0;
}
> gcc -o setuid-test setuid-test.c
> ./setuid-test
1000
> Sudo chown nobody ./setuid-test; Sudo chmod +s ./setuid-test
> ./setuid-test
65534
No entanto, isso se aplica apenas a executáveis; Os scripts do shell ignoram o bit setuid:
> cat setuid-test2
#!/bin/bash
id -u
> ./setuid-test2
1000
> Sudo chown nobody ./setuid-test2; Sudo chmod +s ./setuid-test2
> ./setuid-test2
1000
Wikipedia diz :
Devido à maior probabilidade de falhas de segurança, muitos sistemas operacionais ignoram o atributo setuid quando aplicados a scripts Shell executáveis.
Supondo que estou disposto a aceitar esses riscos, existe alguma maneira de dizer ao Linux para tratar o bit setuid da mesma forma nos scripts do Shell e nos executáveis?
Caso contrário, existe uma solução comum para esse problema? Minha solução atual é adicionar uma entrada sudoers
para permitir que ALL
execute um determinado script como o usuário que eu quero que seja, com NOPASSWD
para evitar a solicitação de senha. A principal desvantagem disso é a necessidade de uma entrada sudoers
toda vez que eu quero fazer isso, e a necessidade do chamador de Sudo some-script
em vez de apenas some-script
O Linux ignora o bit setuid¹ em todos os executáveis interpretados (ou seja, executáveis iniciando com uma linha #!
). O comp.unix.questions FAQ explica os problemas de segurança com scripts shell setuid. Esses problemas são de dois tipos: relacionados a Shebang e relacionados à Shell; Entro em mais detalhes abaixo.
Se você não se preocupa com segurança e deseja permitir scripts setuid, no Linux, precisará corrigir o kernel. No kernel 3.x, acho que você precisa adicionar uma chamada a install_exec_creds
na função load_script
, antes da chamada para open_exec
, Mas não testei.
Há uma condição de corrida inerente à maneira como o Shebang (#!
) É normalmente implementado:
#!
.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 esse motivo, a 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, NetBSD e Mac OS X, todos os quais exigem que uma configuração do kernel seja ativada) implementam o setuid seguro Shebang usando um adicional recurso: o caminho /dev/fd/N
refere-se ao arquivo já aberto no descritor de arquivo [~ # ~] n [~ # ~] (portanto, abrir /dev/fd/N
é aproximadamente equivalente a dup(N)
). Muitos sistemas unix (incluindo Linux) possuem /dev/fd
, Mas não scripts setuid.
#!
. Digamos que o descritor de arquivo do executável seja 3./dev/fd/3
Na lista de argumentos (como argv[1]
) E executa o intérprete.Página Shebang de Sven Mascheck tem muitas informações sobre Shebang nos diferentes departamentos, incluindo suporte setuid .
Vamos supor que você tenha conseguido executar seu programa como root, seja porque o seu sistema operacional suporta o setuid Shebang ou porque você usou um wrapper binário nativo (como Sudo
). Você abriu uma brecha na segurança? Talvez . O problema aqui é não sobre programas interpretados versus compilados. A questão é se o seu 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 por o programa. Em muitos departamentos, você pode configurar o caminho de pesquisa de bibliotecas dinâmicas pelo 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 libc.so
Especialmente criado em $LD_LIBRARY_PATH
(Entre outras táticas). Todos os sistemas sãos ignoram as variáveis LD_*
Nos executáveis setuid.
Em shells como sh, csh e derivadas, as variáveis de ambiente tornam-se automaticamente parâmetros do Shell. Por meio de parâmetros como PATH
, IFS
e muito mais, o invocador do script tem muitas oportunidades para executar código arbitrário no contexto dos scripts do 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 (seja nativa, bytecode ou interpretada) 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. 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 perlsec . Antes, os scripts setlid Perl precisavam de #!/usr/bin/suidperl -wT
Em vez de #!/usr/bin/Perl -wT
, Mas na maioria dos sistemas modernos, #!/usr/bin/Perl -wT
É suficiente.
Observe que o uso de um wrapper binário nativo não faz nada por si só para evitar esses problemas . De fato, isso pode piorar a situação pior, pois pode impedir que o seu ambiente de tempo de execução detecte que ele é chamado com privilégios e ignora sua configurabilidade do tempo de execução.
Um wrapper binário nativo pode tornar um script do 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 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 o Sudo, verifique se a opção env_reset
Está ativada, que setenv
está desativada e que env_file
E env_keep
Contêm apenas variáveis inócuas.
TL, DR:
env_reset
).¹ Esta discussão se aplica igualmente se você substituir "setgid" por "setuid"; ambos são ignorados pelo kernel do Linux nos scripts
Uma maneira de resolver esse problema é chamar o script Shell de um programa que pode usar o bit setuid.
é algo como Sudo. Por exemplo, aqui está como você faria isso em um programa C:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
setuid( 0 ); // you can set it at run time also
system( "/home/pubuntu/setuid-test2.sh" );
return 0;
}
Salve-o como setuid-test2.c.
compilar
Agora faça o setuid neste programa binário:
su - nobody
[enter password]
chown nobody:nobody a.out
chmod 4755 a.out
Agora, você deve poder executá-lo e verá seu script sendo executado sem permissão de ninguém.
Mas aqui também você precisa codificar o caminho do script ou passá-lo como linha de comando arg para acima do exe.
Prefixo alguns scripts que estão neste barco assim:
#!/bin/sh
[ "root" != "$USER" ] && exec Sudo $0 "[email protected]"
Observe que isso não usa setuid
, mas simplesmente executa o arquivo atual com Sudo
.
Se você quiser evitar ligar para Sudo some_script
você pode fazer:
#!/ust/bin/env sh
Sudo /usr/local/scripts/your_script
Os programas SETUID precisam ser projetados com extremo cuidado, pois são executados com privilégios de root e os usuários têm grande controle sobre eles. Eles precisam verificar a sanidade de tudo. Você não pode fazer isso com scripts porque:
sed
, awk
, etc. também precisariam ser verificadosObserve que Sudo
fornece algumas verificações de integridade, mas não é suficiente - verifique todas as linhas em seu próprio código.
Como uma última observação: considere o uso de recursos. Eles permitem que você conceda a um processo em execução como usuário privilégios especiais que normalmente exigiriam privilégios de root. No entanto, por exemplo, embora ping
precise manipular a rede, ele não precisa ter acesso aos arquivos. Não tenho certeza, no entanto, se eles são herdados.
comando super [-r reqpath] [args]
Super permite que usuários especificados executem scripts (ou outros comandos) como se fossem root; ou pode definir os grupos uid, gid e/ou suplementares por comando antes de executar o comando. Destina-se a ser uma alternativa segura para criar scripts como setuid. Super também permite que usuários comuns forneçam comandos para execução por outros; estes são executados com o uid, gid e grupos do usuário que oferece o comando.
Super consulta um arquivo `` super.tab '' para ver se o usuário tem permissão para executar o comando solicitado. Se a permissão for concedida, super executará o pgm [args], em que pgm é o programa associado a este comando. (A raiz é permitida a execução por padrão, mas ainda pode ser negada se uma regra excluir a raiz. Usuários comuns não podem executar a execução por padrão.)
Se o comando for um link simbólico (ou também um link físico) para o super programa, digitar% command args é equivalente a digitar% super command args (o comando não deve ser super ou super não reconhecerá que está sendo chamado por meio de um ligação.)
http://www.ucolick.org/~will/RUE/super/README
http://manpages.ubuntu.com/manpages/utopic/en/man1/super.1.html
Você pode criar um alias para Sudo + o nome do script. Claro, isso é ainda mais trabalhoso de configurar, pois você também precisa configurar um alias, mas evita que você precise digitar Sudo.
Mas se você não se importa com riscos horríveis de segurança, use um Shell setuid como intérprete para o script do Shell. Não sei se isso funcionará para você, mas acho que pode.
Porém, deixe-me dizer que eu aconselho a não fazer isso de verdade. Estou apenas mencionando isso para fins educacionais ;-)
Se por algum motivo Sudo
não estiver disponível, você poderá escrever um script de wrapper fino em C:
#include <unistd.h>
int main() {
setuid(0);
execle("/bin/bash","bash","/full/path/to/script",(char*) NULL,(char*) NULL);
}
E depois de compilado, defina-o como setuid
com chmod 4511 wrapper_script
.
Isso é semelhante a outra resposta publicada, mas executa o script com um ambiente limpo e usa explicitamente /bin/bash
Em vez do Shell chamado por system()
e, portanto, fecha algumas possíveis falhas de segurança.
Observe que isso descarta o ambiente completamente. Se você deseja usar algumas variáveis ambientais sem abrir vulnerabilidades, você realmente só precisa usar Sudo
.
Obviamente, você deseja garantir que o próprio script seja gravável apenas pela raiz.