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 eles forem diferentes. Essa é a diferença entre o uid efetivo (ID do usuário) e o uid real .
Alguns utilitários comuns, como passwd, pertencem ao root e são configurados dessa maneira por necessidade ( passwdprecisa acessar o /etc/shadowque só pode ser lido pelo root).
A melhor estratégia para fazer isso é fazer o que você precisa fazer como superusuário imediatamente e diminuir os privilégios para que menos erros ou uso indevido ocorram durante a execução do root. Para fazer isso, você define o uid efetivo do processo como 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 dos idiomas de nível superior, se forem usadas normalmente em sistemas POSIX:
getuid(): Obtenha o uid real .
geteuid(): Obtenha o uid eficaz .
seteuid(): Defina o uid efetivo .
Você não pode fazer nada com o último inapropriado ao 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, com privilégios:
chown root testuid
chmod u+s testuid
O último define o bit setuid. Se você executar agora ./testuidcomo um usuário normal, verá o processo por padrão executado com uid 0 eficaz, root.
E os scripts?
Isso varia de plataforma para plataforma , mas no Linux, coisas que exigem um intérprete, incluindo bytecode, não podem usar o bit setuid, a menos que esteja definido no intérprete (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 tem variáveis especiais para uid ( $>) e uid real ( $<). Mas se você tentar o mesmo chowne chmodusado com o executável compilado (de C, exemplo anterior), não fará diferença. 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 isso gcc --std=c99 whatever.c -o perlsuid, então chown root perlsuid && chmod u+s perlsuid. Agora você pode executar qualquer script perl com um uid efetivo de 0, independentemente de quem o possui.
Uma estratégia semelhante funcionará com php, python, etc. Mas ...
# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face
POR FAVOR, POR FAVOR, NÃO deixe esse tipo de coisa por aí . Muito provavelmente, você realmente deseja compilar o nome do script como um caminho absoluto , ou seja, substitua todo o código main()por:
const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
return execv("/usr/bin/perl", (char * const*)args);
Eles garantem que /opt/suid_scriptstudo nele seja 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 raiz. Portanto, a menos que você saiba que o intérprete e o próprio script são robustos em relação a todas as variáveis de ambiente possíveis, NÃO FAÇA ISSO .
Então, o que devo fazer?
Uma maneira mais segura de permitir que um usuário não root execute um script como root é adicionar uma regra sudo e fazer com que o usuário execute sudo /path/to/script. O Sudo retira 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. Consulte Como executar um programa específico como root sem um prompt de senha? Por exemplo.
-privilegedshell e, se chamados de um script responsável, também podem ser invocadossuid rootdessa maneira com segurança . Receio que a noção de um script responsável seja um sonho no mundo real. De qualquer forma, é provavelmente vale a pena mencionar o quão importante é a gravações Evitar de todos os tipos, se possível, enquanto as permissões são elevados ...