Jest tam też chyba dość sporo sytuacji, gdzie dowolny użytkownik tego systemu może eskalować do roota wynikających z wyścigów na systemie plików.
Np. `useradd` nie jest zbyt ostrożny w tym jak tworzy katalog: https://github.com/shadow-maint/shadow/blob/master/src/useradd.c#L2293. Jeśli użytkownik (którego $HOME nie ma +t) odpali tworzenie użytkownika-cienia, i między między mkdir() z 2365 a chown() z 2387 zmieni nazwę .shadow-home, a na jego miejsce wsadz symlink dokądkolwiek ten chown zmieni właściciela tego symlinka.
Midlaunch odpala `bwrap`a jako root. W związku z tym bind mounty przezeń wykonywane będą używały uprawnień roota do trawersowania katalogów, więc pozwala użytkownikowi podmontować sobie w widocznym miejscu coś z katalogu, w którym on sam nie ma +x. Gdzieś wcześniej sprawdzasz czy właściwy użytkownik ma dostęp, no ale znowu to może być symlink który podmienię pomiędzy tym wywołaniem access a odpaleniem się bbwrapa. (Trochę nie rozumiem, czemu to nie uruchamia bwrapa jako użytkownik-cień.)
Jeśli sysctl protected_hardlinks (https://www.kernel.org/doc/Documentation/sysctl/fs.txt) nie jest włączony, chmid pozwala kraść pliki: można podmienić plik na hardlink do cudzego pliku pomiędzy weryfikacją właściciela a chmodem.
No i jeśli midlaunchowi każe podmontować coś na /sbin/runuser, to on to bardzo chętnie mi odpali jako root.
@robryk Hmm, u mnie jakoś ping działa, ale sudo już nie. I ping nie ma u mnie cap_net_raw. Widziałem go natomiast na innej dystrybucji.
Wiem, że brak tej flagi przysporzyłby mi dodatkowych problemów. Jednocześnie, to zmusza mnie do uruchamiania bwrap jako root - nie widzę innego sposobu, jak potem przelogować się na primary user.
TIL o pingu: https://unix.stackexchange.com/questions/592911/how-does-ping-work-on-fedora-without-setuid-and-capabilities
Ta flaga == no_new_privs?
Co najmniej dla rzeczy z run_as_shadow możesz zrobić to w odwrotnej kolejności. (Trochę nie rozumiem, do czego służa aplikacje bez run_as_shadow.)
@robryk Run_as_shadow zostało dodane później. Oryginalnie miało być tak, że aplikacja uruchamia się jako primary user, ale ma pomontowane w sandboxie niektóre foldery z shadow home (de facto należące do primary user, ale primary user nie ma do nich dostępu bo blokuje brak uprawnień grupy dla .shadow-home. Dostęp przez sandboxowaną aplikację do innych katalogów nieustawionych w konfiguracji jest efektem niepożądanym. Innymi słowy, ustawienie run_as_shadow=true to odpowiednik midsu z customowym shellem.
@anedroid A, czyli cała ochrona bazuje na pośrednim katalogu bez g+x, rozumiem.
@robryk Bardzo dziękuję za sprawdzenie tych rzeczy. Wiele to dla mnie znaczy. Mam nadzieję, że uda mi się zaadresować wymienione problemy choć częściowo i mój projekt nie okaże się bezużyteczny.
Możesz chcieć wiedzieć o istnieniu qubes (mimo że jest w zupełnie innym miejscu na skali kompromisu między utrudnianiem życia a tym przed czym chce chronić).
Na Twoim miejscu zastanawiałbym się nad zrobieniem czegoś prawie że odwrotnego. I tak w tym setupie który masz musisz bardzo dobrze rozumieć skąd każdy jeden kawałek środowiska graficznego/shella/... czyta swoją konfigurację. Może więc zamiast tego odpalać całe środowisko graficzne jako shadow user i je tak skonfigurować, żeby (a) bardzo łatwo było dostać shella jako normal user (b) wszystkie aplikacje odpalały się w jakichś mniejszych sandboxach?
@robryk Zabawa z symlinkami i race condition. Tego nie przewidziałem. Czyli ograniczenie targetu do homedir (realpath) i coś jeszcze trzeba będzie wykombinować.
Obawiam się, że może się nie dać. ZTCW nie da się spowodować, żeby `mount()` nie podążał za symlinkami w ścieżce do celu.
@anedroid Ten konkretny problem możnaby rozwiązać bez tego, gdyby tylko dało się mieć pewną kopię /proc w tym kontenerze: wtedy otwierasz /sbin/runuser i dajesz bwrapowi jako dodatkowy fd, i każesz mu odpalić /proc/self/fd/numerek. No ale upewnienie się, że naprawdę masz procfs na /proc jest co najmniej trudne.
@robryk Ograniczyłem możliwość montowania poza homem.
https://git.disroot.org/anedroid/midutils/commit/3a347f3206513715b4a30ae17add7f7dfae522af
@anedroid To nie pomaga: `mount(2)` podąża za symlinkami, więc target można podmienić pomiędzy momentem kiedy się wykona realpath a uruchomieniem bwrapa.
Poza tym te mounty się dzieją po kolei. Pierwszy z nich może podmontować $HOME/a pod $HOME/b, a drugi $costam pod $HOME/b/sbin/runas.
@robryk Czy można zablokować plik tak, aby nie można było nic z nim zrobić dopóki proces nie zginie lub zdejmie blokadę? Można by wtedy zablokować plik przed sprawdzeniem uprawnień.
Wydaje mi się, że chcesz naprawiać problemy przez postulowanie coraz bardziej skomplikowanych interfejsów. Bardzo rzadko jest to dobra droga.
Gdyby taki interfejs istniał, kto mógłby zablokować plik? Czego nie możnaby zrobić z plikiem gdy jest zablokowany? Czy zablokowany byłby inode, czy może wpis w katalogu (dentry)?
Polecam raczej popatrzenie na to co można zrobić na otwartym pliku za pomocą syscalli fcośtam oraz tego jak dziala open(O_PATH).
@robryk Chmid już nie pozwala kraść plików. Nie uruchomi się, jeżeli fs.protected_hardlinks jest wyłączony.
https://git.disroot.org/anedroid/midutils/commit/4c93e3bfe0ce37cc46abbc743709943e3226603e
Wydaje mi się, że podobny mechanizm prewencyjny powinienem zaimplementować przy tworzeniu użytkownika.
@robryk Albo mógłbym tymczasowo zmienić właściciela primary homedir na nobody i dać +t, a potem zmienić go na shadow usera. Myślisz, że to dobry pomysł?
Na pierwszy rzut oka każdy proces który ma wiele faz jest złym pomysłem, bo jest znacznie więcej sytuacji brzegowych do rozważenia (chociażby z powodu obsługi błędów, jak i z powodu możliwych wyścigów między wieloma kopiami tego samego procesu).
Czemu w ogóle kazać `useradd`owi tworzyć katalog domowy? Czemu nie zrobić tego zawczasu?
@robryk Która wersja midsu lepsza?
https://git.disroot.org/anedroid/midutils/commit/d7a58eb70a8a95a1d41a838c81512c65d2bbd885
https://git.disroot.org/anedroid/midutils/commit/c0bb74985a700c4c70657d8c3de848bc9893033b
W branch master najpierw zmieniam właściciela primary homedir i ustawiam +s, następnie kopiuję zawartość /etc/skel do .shadow_home. Z kolei w branch alt1 całkowicie przeniosłem homedir shadow usera do /home.
W obydwu wykorzystuję blokadę na pliku /etc/midsu, aby dwie wersje midsu nie mogły operować jednocześnie prowadząc do race condition.
@robryk Zastanawiam się, czy midsu zadziałałby z ecryptfs - w alt1 pliki shadow usera nie zostałyby zaszyfrowane.
@anedroid
BTW. Przed kilkoma innymi uchroniło Cię to, że bwrap zawsze ustawia no_new_privs (https://man7.org/linux/man-pages/man2/prctl.2.html) nawet jak nie musi. To też powoduje, że w środku tego co zostanie odpalone przez midlaunch bity SUID i SGID oraz file capabilities nie są respektowane, więc np. `ping` nie będzie działał.