Il bit setuid può essere impostato su un file eseguibile in modo che, quando eseguito, il programma disponga dei privilegi del proprietario del file anziché dell'utente reale, se diversi. Questa è la differenza tra uid effettivo (id utente) e uid reale .
Alcune utility comuni, come ad esempio passwd
, sono di proprietà di root e configurate in questo modo per necessità ( passwd
deve accedere a ciò /etc/shadow
che può essere letto solo da root).
La strategia migliore quando si esegue questa operazione è fare tutto ciò che è necessario fare come superutente immediatamente, quindi abbassare i privilegi in modo che errori o abusi abbiano meno probabilità di verificarsi durante l'esecuzione di root. Per fare ciò, devi impostare l' UID efficace del processo sul suo UID reale. In 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;
}
Le funzioni pertinenti, che dovrebbero avere un equivalente nella maggior parte delle lingue di livello superiore se utilizzate comunemente nei sistemi POSIX:
getuid()
: Ottieni il vero uid.
geteuid()
: Ottieni l' UID efficace .
seteuid()
: Imposta l' Uid efficace .
Non puoi fare nulla con l'ultimo inappropriato per il vero uid tranne nella misura in cui il bit setuid è stato impostato sull'eseguibile . Quindi per provare questo, compila gcc test.c -o testuid
. È quindi necessario, con privilegi:
chown root testuid
chmod u+s testuid
L'ultimo imposta il bit setuid. Se ora esegui ./testuid
un normale utente, il processo verrà eseguito per impostazione predefinita con uid 0 effettivo, root.
Che dire degli script?
Questo varia da piattaforma a piattaforma , ma su Linux, le cose che richiedono un interprete, incluso il bytecode, non possono usare il bit setuid a meno che non sia impostato sull'interprete (che sarebbe molto molto stupido). Ecco un semplice script perl che imita il codice C sopra:
#!/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";
Fedele alle sue radici * nixy, perl ha incorporato variabili speciali per uid ( $>
) e real uid ( $<
) efficaci . Ma se provi lo stesso chown
e lo chmod
usi con l'eseguibile compilato (da C, esempio precedente), non farà alcuna differenza. Lo script non può ottenere privilegi.
La risposta a questa è usare un binario setuid per eseguire lo 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);
}
Compila questo gcc --std=c99 whatever.c -o perlsuid
, quindi chown root perlsuid && chmod u+s perlsuid
. Ora puoi eseguire qualsiasi script perl con un uid effettivo di 0, indipendentemente da chi lo possiede.
Una strategia simile funzionerà con php, python, ecc. Ma ...
# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face
PER FAVORE, PER FAVORE NON lasciare questo tipo di cose in giro . Molto probabilmente, in realtà vuoi compilare il nome dello script come un percorso assoluto , cioè sostituire tutto il codice main()
con:
const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
return execv("/usr/bin/perl", (char * const*)args);
Si assicurano /opt/suid_scripts
che tutto sia in sola lettura per utenti non root. Altrimenti, qualcuno potrebbe scambiare qualsiasi cosa per whatever.pl
.
Inoltre, attenzione che molti linguaggi di scripting consentono alle variabili di ambiente di cambiare il modo in cui eseguono uno script . Ad esempio, una variabile di ambiente potrebbe causare il caricamento di una libreria fornita dal chiamante, consentendo al chiamante di eseguire codice arbitrario come root. Pertanto, a meno che non si sappia che sia l'interprete che lo script stesso sono robusti rispetto a tutte le possibili variabili di ambiente, NON FARLO .
Quindi cosa dovrei fare invece?
Un modo più sicuro per consentire a un utente non root di eseguire uno script come root è quello di aggiungere una regola sudo e far eseguire l'utente sudo /path/to/script
. Sudo elimina la maggior parte delle variabili di ambiente e consente inoltre all'amministratore di selezionare con precisione chi può eseguire il comando e con quali argomenti. Vedi Come eseguire un programma specifico come root senza una richiesta di password? per un esempio.
-privileged
shell e, se chiamate da uno script responsabile, possono anche essere invocate insuid root
modo sicuro in quel modo. Temo tuttavia che l'idea di una sceneggiatura responsabile sia un po 'un sogno nel mondo reale. Ad ogni modo, probabilmente vale la pena ricordare quanto sia importante evitare scritture di ogni tipo, se possibile, mentre le autorizzazioni sono elevate ...