it-swarm-pt.com

Permitir setuid em scripts Shell

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

195
Michael Mrozek

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.


Setuid Shebang

Há uma condição de corrida inerente à maneira como o Shebang (#!) É normalmente implementado:

  1. O kernel abre o executável e descobre que ele começa com #!.
  2. O kernel fecha o executável e abre o intérprete.
  3. O kernel insere o caminho do 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 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.

  1. O kernel abre o executável e descobre que ele começa com #!. Digamos que o descritor de arquivo do executável seja 3.
  2. O kernel abre o intérprete.
  3. O kernel insere /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 .


Intérpretes 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:

  • Setuid Shebang é inseguro, mas geralmente ignorado.
  • Se você executar um programa com privilégios (por meio do Sudo ou do setuid), escreva o código nativo ou o Perl ou inicie o programa com um wrapper que higienize o ambiente (como o Sudo com a opção env_reset).

¹ Esta discussão se aplica igualmente se você substituir "setgid" por "setuid"; ambos são ignorados pelo kernel do Linux nos scripts

213

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.

57
Hemant

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.

24
rcrowley

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:

  • Os shells são grandes pedaços de software que interagem fortemente com o usuário. É quase impossível verificar tudo com integridade - especialmente porque a maior parte do código não se destina a ser executada nesse modo.
  • Os scripts são uma solução bastante rápida e geralmente não são preparados com tanto cuidado que permitiriam setuid. Eles têm muitos recursos potencialmente perigosos.
  • Eles dependem fortemente de outros programas. Não é suficiente que o Shell tenha sido verificado. sed, awk, etc. também precisariam ser verificados

Observe 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.

12
Maciej Piechotka

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

5
Nizam Mohamed

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 ;-)

4
wzzrd

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.

2
Chris