Usando o bit setuid corretamente

41

Eu tenho um processo que precisa de privilégios de root quando executado por um usuário normal. Aparentemente eu posso usar o "bit setuid" para conseguir isso. Qual é a maneira correta de fazer isso em um sistema POSIX?

Além disso, como posso fazer isso com um script que usa um interpretador (bash, perl, python, php, etc)?

    
por goldilocks 08.11.2014 / 16:34

2 respostas

49

O bit setuid pode ser definido em um arquivo executável para que, quando executado, o programa tenha os privilégios do proprietário do arquivo em vez do usuário real, se forem diferentes. Esta é a diferença entre eficaz uid (id do usuário) e real uid.

Alguns utilitários comuns, como passwd , são de propriedade root e são configurados dessa forma por necessidade ( passwd precisa acessar /etc/shadow , que pode ser lido apenas pelo root).

A melhor estratégia ao fazer isso é fazer o que você precisa fazer imediatamente como superusuário e, em seguida, diminuir os privilégios para que bugs ou uso indevido sejam menos prováveis de acontecer enquanto estiver executando o root. Para fazer isso, você configura o efetivo uid do processo para seu uid real. No POSIX C:

#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void report (uid_t real) {
    printf (
        "Real UID: %d Effective UID: %d\n",
        real,
        geteuid()
    );
}

int main (void) {
    uid_t real = getuid();
    report(real);
    seteuid(real);
    report(real);
    return 0;
}

As funções relevantes, que devem ter um equivalente na maioria das linguagens de nível mais alto, se forem usadas comumente em sistemas POSIX:

  • getuid() : obtenha o real uid.
  • geteuid() : obtenha o eficaz uid.
  • seteuid() : defina o efetivo uid.

Você não pode fazer nada com o último inapropriado para o uid real, exceto na medida em que o bit setuid foi definido no executável . Então, para tentar isso, compile gcc test.c -o testuid . Você precisa então, com privilégios:

chown root testuid
chmod u+s testuid

O último define o bit setuid. Se você agora executar ./testuid como um usuário normal, verá que o processo, por padrão, é executado com uid efetivo 0, root.

E os scripts?

Isso varia de plataforma para plataforma , mas em Linux, coisas que requerem um intérprete, incluindo bytecode, não podem fazer uso do bit setuid a menos que ele esteja configurado no interpretador (o que seria muito estúpido). Aqui está um script perl simples que imita o código C acima:

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n"; 

Fiel às suas raízes * nixy, o perl foi construído em variáveis especiais para uid efetivo ( $> ) e uid real ( $< ). Mas se você tentar o mesmo chown e chmod usado com o executável compilado (do C, exemplo anterior), não fará diferença alguma. O script não pode obter privilégios.

A resposta para isso é usar um binário setuid para executar o script:

#include <stdio.h>
#include <unistd.h> 

int main (int argc, char *argv[]) {
    if (argc < 2) {
        puts("Path to perl script required.");
        return 1;
    }
    const char *perl = "perl";
    argv[0] = (char*)perl;
    return execv("/usr/bin/perl", argv);
}

Compile este gcc --std=c99 whatever.c -o perlsuid e, em seguida, chown root perlsuid && chmod u+s perlsuid . Agora você pode executar qualquer script perl com um uid efetivo de 0, independentemente de quem seja o proprietário.

Uma estratégia semelhante funcionará com php, python, etc. Mas ...

# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face

POR FAVOR, NÃO deixe este tipo de coisa por aí . O mais provável é que você queira compilar no nome do script como um caminho absoluto , ou seja, substituir todo o código em main() por:

    const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
    return execv("/usr/bin/perl", (char * const*)args);

Certifique-se de que /opt/suid_scripts e tudo nele são somente leitura para usuários não-root. Caso contrário, alguém poderia trocar qualquer coisa por whatever.pl .

Além disso, lembre-se de que muitas linguagens de script permitem que variáveis de ambiente alterem a maneira como executam um script . Por exemplo, uma variável de ambiente pode fazer com que uma biblioteca fornecida pelo chamador seja carregada, permitindo que o chamador execute código arbitrário como root. Assim, a menos que você saiba que tanto o interpretador quanto o próprio script são robustos contra todas as possíveis variáveis de ambiente, NÃO FAÇA ISSO .

Então, o que devo fazer em vez disso?

Uma maneira mais segura de permitir que um usuário não raiz execute um script como root é adicionar uma regra sudo e ter o usuário executando sudo /path/to/script . O Sudo remove a maioria das variáveis de ambiente e também permite ao administrador selecionar com precisão quem pode executar o comando e com quais argumentos. Veja Como executar um específico programa como root sem um prompt de senha? para um exemplo.

    
por 08.11.2014 / 16:34
11

Sim, você pode, mas é provavelmente uma ideia muito ruim . Normalmente, você não definiria o bit SUID diretamente no seu executável, mas use um sudo (8) ou su (1) para executá-lo (e limitar quem pode executá-lo)

Note, no entanto, que existem muitos problemas de segurança em permitir que usuários regulares executem programas (e especialmente scripts!) como root. A maioria deles tem que fazer isso a menos que o programa seja especificamente e muito cuidadosamente escrito para lidar com isso, usuários maliciosos poderiam usá-lo para obter shell de root facilmente.

Portanto, mesmo que você fizesse isso, seria uma boa idéia primeiro desinfetar TODAS as entradas do programa que você deseja executar como raiz, incluindo seu ambiente, parâmetros de linha de comando, STDIN e todos os arquivos que processa, dados de conexões de rede, abre, etc etc É extremamente difícil de fazer, e ainda mais difícil fazer o certo.

Por exemplo, você pode permitir que usuários editem apenas alguns arquivos com o editor. Mas o editor permite a execução de comandos de helpers externos ou suspendendo, dando assim aos usuários root shell para fazer o que quiserem. Ou você pode estar executando scripts de shell que parecem muito seguros para você, mas o usuário pode executá-lo com $ IFS modificado ou outras variáveis de ambiente alterando completamente seu comportamento. Ou pode ser um script PHP que deve executar "ls", mas o usuário o executa com $ PATH modificado e, portanto, faz com que ele execute um shell que ele chama de "ls". Ou dezenas de outros problemas de segurança.

Se o programa realmente deve fazer parte de seu trabalho como root, provavelmente seria melhor modificá-lo para que funcione como usuários normais, mas apenas execute (depois de desinfetar o máximo possível) a parte que precisa de raiz através do suid programa auxiliar.

Observe que, mesmo que você confie completamente em todos os usuários locais (por exemplo, forneça a senha de root), é uma má idéia, como qualquer supervisão de segurança não intencional por QUALQUER uma delas (como ter um script PHP on-line ou fraco). senha, ou qualquer outra coisa) de repente permite que o invasor remoto comprometa completamente a máquina inteira.

    
por 08.11.2014 / 21:55