Permitir setuid em scripts shell

Permitir setuid em scripts shell

O setuidbit de permissão 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

Entretanto, isso se aplica apenas a executáveis; scripts 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

Wikipédiadiz:

Devido à maior probabilidade de falhas de segurança, muitos sistemas operacionais ignoram o atributo setuid quando aplicado a scripts de 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 em scripts de shell e em executáveis?

Caso contrário, existe uma solução alternativa comum para esse problema? Minha solução atual é adicionar uma sudoersentrada para permitir ALLa execução de um determinado script como o usuário que desejo que ele seja executado, para NOPASSWDevitar a solicitação de senha. A principal desvantagem disso é a necessidade de uma sudoersentrada toda vez que eu quiser fazer isso, e a necessidade do chamador, sudo some-scriptem vez de apenassome-script

Responder1

O Linux ignora o bit setuid¹ em todos os executáveis ​​interpretados (ou seja, executáveis ​​começando com uma #!linha). OPerguntas frequentes sobre comp.unix.questionsexplica 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 preocupa com segurança e deseja permitir scripts setuid, no Linux você precisará corrigir o kernel. A partir dos kernels 3.x, acho que você precisa adicionar uma chamada parainstall_exec_credsnoload_scriptfunction, antes da chamada para open_exec, mas não testei.


Setuid shebang

Há uma condição de corrida inerente à forma como 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 interpretador.
  3. O kernel insere o caminho do script na lista de argumentos (como argv[1]) e executa o interpretador.

Se scripts setuid forem permitidos com esta implementação, um invasor pode invocar um script arbitrário criando um link simbólico para um script setuid existente, executando-o e providenciando a alteração do link após o kernel ter executado a etapa 1 e antes que o interpretador chegue a abrindo seu primeiro argumento. Por esta razão,a maioria das unidades ignora o bit setuidquando eles detectam uma coisa.

Uma maneira de garantir essa implementação seria o kernel bloquear o arquivo de script até que o intérprete o abrisse (observe que isso deve evitar não apenas desvincular ou sobrescrever o arquivo, mas também renomear qualquer diretório no caminho). Mas os sistemas Unix tendem a evitar bloqueios obrigatórios, e links simbólicos tornariam um recurso de bloqueio correto especialmente difícil e invasivo. Acho que ninguém faz assim.

Alguns sistemas unix (principalmente OpenBSD, NetBSD e Mac OS X, todos os quais requerem uma configuração de kernel para serem habilitados) implementamsetuid shebang segurousando um recurso adicional: o caminho refere-se ao arquivo já aberto no descritor de arquivo/dev/fd/NN(então a abertura é aproximadamente equivalente a ). Muitos sistemas unix (incluindo Linux) possuem scripts setuid, mas não./dev/fd/Ndup(N)/dev/fd

  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 interpretador.
  3. O kernel insere /dev/fd/3a lista de argumentos (como argv[1]) e executa o interpretador.

Página shebang de Sven Maschecktem muitas informações sobre shebang em unidades, incluindosuporte setuid.


Intérpretes Setuidas

Vamos supor que você conseguiu fazer seu programa rodar como root, seja porque seu sistema operacional suporta setuid shebang ou porque você usou um wrapper binário nativo (como sudo). Você abriu uma falha de segurança?Talvez. A questão aqui énãosobre programas interpretados versus programas compilados. A questão é se o seusistema de tempo de execuçãose comporta com segurança se executado com privilégios.

  • Qualquer executável binário nativo vinculado dinamicamente é de certa forma interpretado pelocarregador dinâmico(por exemplo /lib/ld.so), que carrega as bibliotecas dinâmicas exigidas pelo programa. Em muitas unidades, 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é mesmo 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.socódigo especialmente criado $LD_LIBRARY_PATH(entre outras táticas). Todos os sistemas sensatos ignoram as LD_*variáveis ​​nos executáveis ​​setuid.

  • Emcartuchoscomo sh, csh e derivados, variáveis ​​de ambiente tornam-se automaticamente parâmetros de shell. Através de parâmetros como PATH, IFSe muitos mais, o invocador do script tem muitas oportunidades de executar código arbitrário no contexto dos scripts de shell. Alguns shells definem essas variáveis ​​​​para padrões sensatos se detectarem que o script foi invocado com privilégios, mas não sei se existe alguma implementação específica em que eu confiaria.

  • A maioria dos ambientes de tempo de execução(sejam nativos, bytecode ou interpretados) possuem recursos semelhantes. Poucos tomam precauções especiais em executáveis ​​setuid, embora aqueles 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. Istosuporta explicitamente scripts setuidde forma segura. Na verdade, seu script pode executar setuid mesmo que seu sistema operacional ignore o bit setuid nos scripts. Isso ocorre porque o perl vem com um auxiliar de root setuid que executa as verificações necessárias e invoca novamente o interpretador nos scripts desejados com os privilégios desejados. Isto é explicado noperlsecmanual. Antigamente, os scripts perl setuid eram necessários #!/usr/bin/suidperl -wTem vez de #!/usr/bin/perl -wT, mas na maioria dos sistemas modernos #!/usr/bin/perl -wTsão suficientes.

Observe que usando um binário nativowrapper não faz nada por si só para evitar esses problemas. Na verdade, pode tornar a situaçãopior, porque pode impedir que seu ambiente de tempo de execução detecte que ele foi chamado com privilégios e ignore sua configurabilidade de tempo de execução.

Um wrapper binário nativo pode tornar um script de shell seguro se o wrapperhigieniza 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. Colocar variáveis ​​na lista negra é propenso a erros, portanto, sempre coloque na lista branca. Com o sudo, certifique-se de que a env_resetopção esteja ativada, desativada setenve que contenha env_fileapenas env_keepvariáveis ​​​​inócuas.


TL,DR:

  • Setuid shebang é inseguro, mas geralmente ignorado.
  • Se você executar um programa com privilégios (por meio de sudo ou setuid), escreva código nativo ou perl ou inicie o programa com um wrapper que higienize o ambiente (como sudo com a env_resetopção).

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

Responder2

Uma maneira de resolver esse problema é chamar o shell script 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.
compile
Agora faça o setuid neste programa binário:

su - nobody   
[enter password]  
chown nobody:nobody a.out  
chmod 4755 a.out  

Agora, você 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 argumento de linha de comando para o exe acima.

Responder3

Eu prefixo alguns scripts que estão neste barco assim:

#!/bin/sh
[ "root" != "$USER" ] && exec sudo $0 "$@"

Observe que isso não usa, setuidmas simplesmente executa o arquivo atual com a extensão sudo.

Responder4

Se por algum motivo sudonão estiver disponível, você pode escrever um script 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 compilá-lo, defina-o setuidcomo chmod 4511 wrapper_script.

Isso é semelhante a outra resposta postada, mas executa o script com um ambiente limpo e usa explicitamente /bin/bashem vez do shell chamado por system()e, assim, fecha algumas possíveis falhas de segurança.

Observe que isso descarta totalmente o meio ambiente. Se você quiser usar algumas variáveis ​​ambientais sem abrir vulnerabilidades, você realmente só precisa usar sudo.

Obviamente, você quer ter certeza de que o script em si só pode ser gravado pelo root.

informação relacionada