Prawidłowe użycie bitu setuid

45

Mam proces, który wymaga uprawnień roota, gdy jest uruchamiany przez zwykłego użytkownika. Najwyraźniej mogę użyć „bitu setuid”, aby to osiągnąć. Jak to zrobić w systemie POSIX?

Jak mogę to zrobić za pomocą skryptu korzystającego z interpretera (bash, perl, python, php itp.)?

Złotowłosa
źródło

Odpowiedzi:

53

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 ( passwdwymaga dostępu, /etc/shadowktó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ć ./testuidjako 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 chowni użyjesz chmodgo 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_scriptswszystko 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.

Złotowłosa
źródło
1
wiele powłok będzie zachowywać się inaczej, jeśli zostanie wywołane jako -privilegedpowłoka i, jeśli zostanie wywołane z odpowiedzialnego skryptu, można je bezpiecznie wywołać w suid rootten 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 ...
mikeserv
@ mikeserv Nie znam pojęć „ -privilegedshell” 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ć - sudojest 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 .
goldilocks
1
Naprawdę nie chcę tego robić - odpowiedzialne skryptynaprawdę trudne - prawdopodobnie poza mną. Ale muszę powiedzieć, że nie zgadzam się z twoją analizą sudo- uważam sudoza psychotyczną , ponieważ udaje, że nie jest root. Możesz nawet umieścić globusy powłoki w swoim sudoers. sumoim zdaniem jest lepszy do celów skryptowych, ale sudojest dobry do aplikacji na żywo. Ale nie jest tak wyraźny jak w skrypcie z #!/bin/ksh -pbangline lub cokolwiek suid kshw $PATHto.
mikeserv
Chociaż ta odpowiedź zawiera odpowiedź na pytanie, skrypty setuid są receptą na otwarcie się na lukę w zabezpieczeniach lokalnych. Istnieje powód, dla którego skrypty nie mogą być łatwo ustawione na setuid. Prawidłowa odpowiedź tutaj to sudo.
mdadm
Pozwoliłem sobie zredagować twoją odpowiedź, aby przynajmniej wspomnieć o ogromnej podatności na zmienne środowiskowe. Nie podoba mi się to tak bardzo, jak odpowiedź kanoniczna, ponieważ wchodzi ona w dygresję w niepewnej metodzie (setuid otoki dla skryptów). Najlepiej byłoby polecić sudo i zostawić rozszerzoną dyskusję na inny wątek (przy okazji, opisałem ją tam ).
Gilles 'SO - przestań być zły'
11

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ę.

Matija Nalis
źródło
1
oddelegowany. zobacz przykład bezpiecznego wywoływania z serii programistów POSIX dla man sh- a nawet to nie robi command -p env -i .... W końcu jest to dość trudne. Mimo to, jeśli zostanie to zrobione poprawnie, dla wyraźnych celów, lepszym suidtłumaczem może być lepsze niż użycie sulub sudo- ponieważ zachowanie jednego z nich zawsze będzie poza kontrolą skryptu (choć surzadziej 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 .
mikeserv