S
ystem Linux oferuje różnorodne metody przyznawania uprawnień do plików. Oprócz tradycyjnej, uznaniowej kontroli dostępu (DAC – Discretionary Access Control) obsługuje również listy kontroli dostępu (ACL – Access Control Lists) i obowiązkową kontrolę dostępu (MAC – Mandatory Access Control). Wszystkie te mechanizmy działają na podstawie przyznawania uprawnień, gdy domyślnie nic nie jest dozwolone. To podejście jest również czasami określane jako listy dostępowe (ang. allow list). Analogicznie, jak przy tworzeniu reguł dla zapór sieciowych – blokujemy wszystko i zezwalamy na selektywny ruch. Jednak zarówno DAC i jego rozwinięcie ACL pozwalają na implementację negatywnych uprawnień (negatyw – ujemna, niekorzystna strona jakiegoś zjawiska lub jakiejś sprawy). Odwołując się do poprzedniego przykładu – blokujemy selektywny ruch, ale wszystko inne jest dozwolone. W przypadku dostępu do plików uprawnienia takie uniemożliwiają dostęp określonym użytkownikom lub grupom, gdy wszyscy inni zachowują dostęp. Tego typu praktyka jest raczej rzadkością, za to powszechnie postrzegana jako zła praktyka w przypadku uprawnień i ruchu sieciowego.
Pojawienie się konteneryzacji w systemie Linux zmieniło na tyle krajobraz, iż negatywne uprawnienia nie są już tylko złą praktyką, ale także stanowią furtkę, którą można ominąć. Dlatego współczesne systemy Linux pełniące rolę hostów dla kontenerów mogą być podatne na omijanie tego typu uprawnień przez użytkowników, którzy chcą uzyskać dostęp do chronionych plików. Dla przykładu negatywnych uprawnień stworzymy teraz katalog games, do którego będą mieli wszyscy dostęp oprócz użytkowników należących do grupy o nazwie nogames:
Użytkownik, który należy do tej grupy nie powinien mieć dostępu do tego katalogu i jego zawartości:
agresor@darkstar:~$ ls -al /usr/share/games/ ls: cannot open directory '/usr/share/games/': Permission deniedUżytkownik agresor ma skutecznie ograniczony dostęp do katalogu /usr/share/games, ponieważ sekwencja uprawnień jest w kolejności: użytkownik (tutaj root), grupa (tutaj nogames bez żadnych praw) i dopiero inni (others). Kłódką na naszym sejfie będzie jeszcze dodanie takich samych uprawnień ACL:
root@darkstar:~# setfacl -m g:nogames:--- /usr/share/games root@darkstar:~# getfacl /usr/share/games # file: usr/share/games # owner: root # group: nogames user::rwx user:agresor:--- group::--- mask::--- other::r-xObchodzenie negatywnych uprawnień:
W celu obejścia ustawionych w ten sposób praw dostępu użytkownik musi usunąć się z grupy nogames. Do tego potrzebne jest wywołanie systemowe setgroups, które umożliwia modyfikację dodatkowych grup powiązanych z bieżącym procesem. Taka operacja wymaga uprzywilejowanych możliwości (ang. capabilities) w szczególności CAP_SETGID. Mechanizm ten podkreśla znaczenie zarządzania uprawnieniami i członkostwem w grupach dla skutecznego utrzymywania pożądanej kontroli dostępu. Jednak, jeżeli zrobimy coś takiego:
agresor@darkstar:~$ podman run --rm -it -v /:/xxx/ docker.io/bash ls -al /xxx/usr/share/games Trying to pull docker.io/library/bash:latest... Getting image source signatures Copying blob 7264a8db6415 done Copying blob 5f1191cc3d45 done Copying blob 735bc6fd85d9 done Copying config 3c04497fad done Writing manifest to image destination Storing signatures total 16 drwx---r-x 2 nobody nobody 4096 Sep 15 18:49 . drwxr-xr-x 118 nobody nobody 4096 Sep 15 18:52 .. -rw-r--r-- 1 nobody nobody 12 Sep 15 18:49 doomII -rw-r--r-- 1 nobody nobody 28 Sep 15 18:49 duke_nukem_3dNegatywne uprawnienia zostają pokonane. Dlaczego? Otóż wraz z kontenerami pojawiły się przestrzenie nazw użytkownika (ang. namespaces). W przestrzeni nazw użytkownik nieuprzywilejowany posiada wszystkie rozszerzone możliwości. Jednak każda akcja mająca miejsce w przestrzeni nazw jest powiązana z identyfikatorem użytkownika i grupą twórcy przestrzeni nazw. W celu uruchomienia bezadministracyjnych kontenerów (ang. rootless) na silniku kontenerowym, takim jak podman pakiet shadow-utils posiada mniej znany zestaw narzędzi: newuidmap oraz newgidmap. Narzędzia te albo posiadają ustawiony bit SUID:
agresor@darkstar:~$ ls -al /usr/bin/newgidmap -rwsr-xr-x 1 root root 28136 Nov 24 2022 /usr/bin/newgidmap agresor@darkstar:~$ ls -al /usr/bin/newuidmap -rwsr-xr-x 1 root root 28136 Nov 24 2022 /usr/bin/newuidmapalbo posiadają funkcję CAP_SETGID / CAP_SETUID. Korzystając z tych programów użytkownicy mogą utworzyć nową przestrzeń nazw użytkownika i skonfigurować mapowania UID i GID, które będą kierowane na identyfikatory różne od identyfikatorów zadania wywołującego. Te mapowania są zapisane w plikach /etc/subuid i /etc/subgid:
agresor@darkstar:~$ cat /etc/subuid agresor:100000:65536 agresor@darkstar:~$ cat /etc/subgid agresor:100000:65536Przy dodawaniu użytkownika do systemu narzędzie useradd konfiguruje również te podrzędne (sub) identyfikatory dla użytkownika i jego grupy. Posiadanie wielu mapowań identyfikatorów (ID) grup umożliwia procesowi w przestrzeni nazw użytkownika ponownie wykorzystanie wywołania systemowego setgroups() umożliwiając mu usuwanie dodatkowych grup według uznania! W ten sposób użytkownik pozbył się grupy nogames i odczytał zawartość katalogu. Jest to identyczna sytuacja, gdybyśmy pozwolili programowi unshare na używanie wywołania setgroups, a następnie uruchomili przestrzeń użytkownika tylko z wybranymi UID i GID:
agresor@darkstar:~$ unshare agresor@darkstar:~$ id uid=1000(agresor) gid=1000(agresor) groups=1000(agresor),1001(nogames) agresor@darkstar:~$ ls -al /usr/share/games/ ls: cannot open directory '/usr/share/games/': Permission denied agresor@darkstar:~$ exit logout agresor@darkstar:~$ unshare -G 1000 bash unshare: setgroups failed: Operation not permitted root@darkstar:~# setcap CAP_SETGID=ep /usr/bin/unshare root@darkstar:~# getcap /usr/bin/unshare /usr/bin/unshare cap_setgid=ep agresor@darkstar:~$ unshare -G 1000 bash agresor@darkstar:~$ id uid=1000(agresor) gid=1000(agresor) groups=1000(agresor) agresor@darkstar:~$ ls -al /usr/share/games total 16 drwx---r-x 2 root nogames 4096 Sep 15 20:49 . drwxr-xr-x 118 root root 4096 Sep 15 20:49 .. -rw-r--r-- 1 root root 12 Sep 15 20:49 doomII -rw-r--r-- 1 root root 28 Sep 15 20:49 duke_nukem_3dRys historyczny:
Początkowo, gdy dodawano przestrzenie nazw użytkowników, nie brano pod uwagę, iż usuwanie lub zmiana członkowstwa w grupach może powodować problemy. Stało się tak głównie dlatego, iż nie myślano o sytuacjach, w których ktoś tworzy uprawnienia w negatywny sposób. Zakładano, iż jeżeli użytkownik opuści grupę to będzie miał mniej uprawnień, co w systemie Linux jest dobrą praktyką. Nie trzeba było jednak długo czekać, by zdać sobie sprawę, iż opuszczanie grup może w rzeczywistości dawać więcej uprawnień w przypadku stosowania negatywnych uprawnień. To odkrycie doprowadziło do błędu znanego pod CVE-2014-8989, który został gwałtownie naprawiony w wersji Linuksa 3.19.
W 2018 roku odkryto, iż wersja 4.5 pakietu shadow-utils wprowadziła nowe narzędzie o wcześniej wspomnianej nazwie newgidmap. Program ten może być użyty do tworzenia bardziej złożonych mapowań dla identyfikatorów grup w przestrzeni nazw użytkowników. W konsekwencji przywrócił on możliwość porzucania członkostwa w grupach przy użyciu wywołania systemowego setgroups. Błąd ten otrzymał identyfikator CVE-2018-7169. Rozwiązaniem przyjętym przez niektóre dystrybucje Linuksa było wykluczenie tego narzędzia z ich pakietów. Ponadto drugi program newuidmap pomaga użytkownikom w zmianie identyfikatora użytkownika na taki, który mieści się w autoryzowanym zakresie skonfigurowanym w pliku: /etc/subid. Adekwatnie ten manewr może również służyć do obejścia negatywnych uprawnień opartych na ustawieniach dostępu dla użytkownika.
Chociaż problem ten nie był błędem jądra Linuksa, stało się oczywiste, iż naprawienie tej luki bez zakłócenia działania istniejących programów, takich jak podman, wymagałoby nietrywialnej zmiany w jądrze systemu. Inne potencjalne rozwiązania również skupiały się na samym pakiecie shadow-utils, ale jak dotąd nie zyskały znaczącej aprobaty. Ostatecznie do 2021 r. osiągnięto ogólne porozumienie w sprawie odejścia od wspierania negatywnych uprawnień i uznania, iż można je obejść. W 2023 roku Richard Weinberger ponownie odkrył problem i zdecydował się kompleksowo go udokumentować na wielu stronach podręcznika systemu Linux.
Podsumowanie:
Negatywne uprawnienia są konsekwentnie uważane za złą praktykę, którą w dodatku można ominąć w środowiskach kontenerowych. Dlatego jeżeli ją gdzieś stosujemy to powinniśmy jej zaprzestać i zamienić ją na uprawnienia zezwalające. W innym przypadku powinniśmy uprawnić się, iż w systemie nie ma wspomnianych programów (np. dla Ubuntu jest to pakiet uidmap), ponieważ są one głównymi bramkami umożliwiającymi ominięcie tego typu uprawnień. Na serwerach bez kontenerów możemy wyłączyć całkowicie przestrzenie nazw użytkowników tylko dla użytkowników dla użytkowników nieuprzywilejowanych ustawiając parametr kernel.unprivileged_userns_clone.
Więcej informacji: Rootless containers with Podman: The basics, The 7 most used Linux namespaces, No Love for Negative Permissions