SETUID można ustawić na plik wykonywalny tak, że po uruchomieniu, program będzie mieć uprawnienia właściciela pliku zamiast prawdziwego użytkownika, jeśli są one różne. Jest to różnica między efektywnym identyfikatorem użytkownika (identyfikatorem użytkownika) a rzeczywistym identyfikatorem użytkownika.
Niektóre typowe narzędzia, takie jak passwd
, są własnością root i skonfigurowane w ten sposób z konieczności ( passwd
wymaga dostępu, /etc/shadow
który można odczytać tylko przez root).
Najlepszą strategią jest zrobienie tego, co musisz zrobić jako administrator, a następnie obniżenie uprawnień, aby błędy lub niewłaściwe użycie były mniej prawdopodobne podczas uruchamiania roota. Aby to zrobić, ustaw efektywny identyfikator użytkownika procesu na rzeczywisty identyfikator użytkownika. W 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;
}
Odpowiednie funkcje, które powinny mieć odpowiednik w większości języków wyższego poziomu, jeśli są one powszechnie używane w systemach POSIX:
getuid()
: Uzyskaj prawdziwy identyfikator użytkownika.
geteuid()
: Uzyskaj skuteczny identyfikator użytkownika.
seteuid()
: Ustaw efektywny identyfikator użytkownika.
Nie możesz zrobić nic z ostatnim nieodpowiednim dla prawdziwego identyfikatora użytkownika, chyba że bit setuid został ustawiony na pliku wykonywalnym . Aby spróbować, skompiluj gcc test.c -o testuid
. Następnie musisz, z uprawnieniami:
chown root testuid
chmod u+s testuid
Ostatni ustawia bit setuid. Jeśli teraz uruchomić ./testuid
jako zwykły użytkownik zobaczysz proces domyślnie trasy z efektywnym uid 0, korzenia.
Co ze skryptami?
Różni się to w zależności od platformy , ale w Linuksie rzeczy wymagające interpretera, w tym kod bajtowy, nie mogą korzystać z bitu setuid, chyba że jest ustawiony na interpreter (co byłoby bardzo bardzo głupie). Oto prosty skrypt perla, który naśladuje powyższy kod C:
#!/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";
Zgodnie z * pierwiastkami nixy, perl ma wbudowane specjalne zmienne dla efektywnego uid ( $>
) i real uid ( $<
). Ale jeśli spróbujesz tego samego chown
i użyjesz chmod
go ze skompilowanym (z C, poprzedni przykład) plikiem wykonywalnym, nie zrobi to żadnej różnicy. Skrypt nie może uzyskać uprawnień.
Odpowiedzią na to jest użycie pliku binarnego setuid do wykonania skryptu:
#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);
}
Skompilować ten gcc --std=c99 whatever.c -o perlsuid
, następnie chown root perlsuid && chmod u+s perlsuid
. Możesz teraz wykonać dowolny skrypt perla z efektywnym identyfikatorem użytkownika wynoszącym 0, niezależnie od tego, kto jest jego właścicielem.
Podobna strategia będzie działać z php, python itp. Ale ...
# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face
PROSZĘ PROSZĘ NIE pozostawiać tego rodzaju rzeczy w pobliżu . Najprawdopodobniej chcesz skompilować w nazwie skryptu jako ścieżkę bezwzględną , tzn. Zastąpić cały kod main()
:
const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
return execv("/usr/bin/perl", (char * const*)args);
Upewnij się, że /opt/suid_scripts
wszystko w nim jest przeznaczone tylko do odczytu dla użytkowników innych niż root. W przeciwnym razie ktoś mógłby zamienić na wszystko whatever.pl
.
Ponadto należy pamiętać, że wiele języków skryptowych umożliwia zmiennym środowiskowym zmianę sposobu wykonywania skryptu . Na przykład zmienna środowiskowa może powodować załadowanie biblioteki dostarczonej przez program wywołujący, umożliwiając programowi wywołującemu wykonanie dowolnego kodu jako root. Tak więc, o ile nie wiesz, że zarówno interpreter, jak i sam skrypt są odporne na wszystkie możliwe zmienne środowiskowe, NIE Rób tego .
Więc co powinienem zamiast tego zrobić?
Bezpieczniejszym sposobem na zezwolenie użytkownikowi nie-rootowi na uruchomienie skryptu jako root jest dodanie reguły sudo i uruchomienie użytkownika sudo /path/to/script
. Sudo usuwa większość zmiennych środowiskowych, a także pozwala administratorowi precyzyjnie wybrać, kto może uruchomić polecenie i jakie argumenty. Zobacz Jak uruchomić określony program jako root bez pytania o hasło? dla przykładu.
-privileged
powłoka i, jeśli zostanie wywołane z odpowiedzialnego skryptu, można je bezpiecznie wywołać wsuid root
ten sposób. Obawiam się jednak, że pojęcie odpowiedzialnego skryptu jest w rzeczywistości rzeczywistością. W każdym razie prawdopodobnie warto wspomnieć, jak ważne jest unikanie wszelkiego rodzaju zapisów, jeśli w ogóle możliwe, gdy uprawnienia są podwyższone ...