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 ...-privileged
shell” i „odpowiedzialny skrypt”. Czy chcesz zrobić kolejną odpowiedź na temat muszli? Oczywiście nie mogę obiecać tyka;) Nie ma wielu dobrych powodów, aby to zrobić -sudo
jest to o wiele lepsza opcja, jeśli to możliwe - napisałem ją jako ogólną odpowiedź wynikającą z wczesnego pytania o uwierzytelnianie hasła systemowego w php .sudo
- uważamsudo
za psychotyczną , ponieważ udaje, że nie jestroot
. Możesz nawet umieścić globusy powłoki w swoimsudoers
.su
moim zdaniem jest lepszy do celów skryptowych, alesudo
jest dobry do aplikacji na żywo. Ale nie jest tak wyraźny jak w skrypcie z#!/bin/ksh -p
bangline lub cokolwieksuid ksh
w$PATH
to.Tak, możesz, ale to chyba bardzo zły pomysł . Zwykle nie ustawiasz bitu SUID bezpośrednio na pliku wykonywalnym, ale używasz sudo (8) lub su (1), aby go wykonać (i ograniczyć, kto może go wykonać)
Należy jednak pamiętać o wielu problemach związanych z bezpieczeństwem zezwalających zwykłym użytkownikom na uruchamianie programów (a zwłaszcza skryptów!) Jako root. Większość z nich musi to robić, chyba że program jest specjalnie i bardzo bardzo starannie napisany, aby sobie z tym poradzić, złośliwi użytkownicy mogliby go łatwo użyć do uzyskania powłoki roota.
Więc nawet gdybyś to zrobił, bardzo dobrym pomysłem byłoby najpierw zdezynfekować WSZYSTKIE dane wejściowe do programu, który chcesz uruchomić jako root, w tym jego środowisko, parametry wiersza poleceń, STDIN i wszelkie przetwarzane pliki, dane pochodzące z połączeń sieciowych otwiera się itp. Jest to niezwykle trudne, a jeszcze trudniejsze.
Na przykład możesz zezwolić użytkownikom, którzy edytują tylko niektóre pliki za pomocą edytora. Ale edytor pozwala na wykonywanie poleceń zewnętrznych pomocników lub zawieszanie, dając tym samym użytkownikom root root do robienia, co chcą. Lub możesz wykonywać skrypty powłoki, które wyglądają na bardzo ciasne pod względem bezpieczeństwa, ale użytkownik może uruchomić go ze zmodyfikowanymi $ IFS lub innymi zmiennymi środowiskowymi, całkowicie zmieniającymi jego zachowanie. Lub może to skrypt PHP, który powinien wykonać „ls”, ale użytkownik uruchamia go ze zmodyfikowaną $ PATH i tym samym powoduje, że uruchamia powłokę, którą nazywa „ls”. Lub dziesiątki innych problemów bezpieczeństwa.
Jeśli program naprawdę musi wykonać część swojej pracy jako root, najlepiej byłoby go zmodyfikować, aby działał jak zwykli użytkownicy, ale po prostu uruchom (po oczyszczeniu jak najwięcej) część, która absolutnie potrzebuje rootowania za pomocą programu pomocniczego suid.
Pamiętaj, że nawet jeśli całkowicie ufasz wszystkim lokalnym użytkownikom (np. Dajesz im hasło roota), jest to zły pomysł, ponieważ jakikolwiek niezamierzony nadzór bezpieczeństwa przez KAŻDEGO z nich (na przykład posiadanie skryptu PHP online lub słabe hasło lub cokolwiek) nagle pozwala zdalnemu atakującemu całkowicie skompromitować całą maszynę.
źródło
man sh
- a nawet to nie robicommand -p env -i ...
. W końcu jest to dość trudne. Mimo to, jeśli zostanie to zrobione poprawnie, dla wyraźnych celów, lepszymsuid
tłumaczem może być lepsze niż użyciesu
lubsudo
- ponieważ zachowanie jednego z nich zawsze będzie poza kontrolą skryptu (choćsu
rzadziej rzuca kulki krzywej, ponieważ ma tendencję do być trochę mniej szalonym) . Dołączenie całego pakietu to jedyny pewny zakład - po prostu lepiej być pewnym .