Linux

Linux Hardening Zwykły użytkownik nie jest bezpieczny. 9 mitów o uprawnieniach w Linuksie

Linux Hardening: Zwykły użytkownik nie jest bezpieczny. 9 mitów o uprawnieniach w Linuksie

W świecie Linuksa istnieje bardzo wygodny model myślenia:

root = niebezpiecznie

zwykły użytkownik = bezpiecznie

To prawda tylko do pewnego momentu.

Linux rzeczywiście został zaprojektowany tak, aby rozdzielać uprawnienia użytkownika od uprawnień administracyjnych. Proces uruchomiony jako zwykły użytkownik nie powinien móc dowolnie modyfikować /etc, ładować modułów kernela, zmieniać konfiguracji systemowej czy ingerować w procesy innych użytkowników.

Problem polega na tym, że współczesny Linux nie jest już systemem opartym wyłącznie na prostym pytaniu:

„Czy proces działa jako root?”

Model bezpieczeństwa jest znacznie bardziej skomplikowany.

Liczą się grupy, sudo, capabilities, SUID, namespaces, dostęp do socketów, usługi systemowe, profile AppArmor lub SELinux, uprawnienia do plików, interfejsy kernela oraz sposób, w jaki poszczególne komponenty komunikują się ze sobą.

Dlatego jeden z najważniejszych mitów Linux Hardening brzmi:

„Jeżeli użytkownik nie jest rootem, system jest bezpieczny.”

Nie.

Jest bezpieczniejszy.

To bardzo duża różnica.

Mit #1: „Zwykły użytkownik nie może zrobić nic poważnego”

To najczęstsze uproszczenie.

Załóżmy, że proces działa jako:

uid=1000(marek) gid=1000(marek) groups=1000(marek)

Nie jest rootem.

Nie oznacza to jednak, że proces jest całkowicie odizolowany od systemu.

Taki proces może:

  • czytać własne pliki,
  • modyfikować własne dane,
  • komunikować się z siecią,
  • uruchamiać kolejne programy,
  • korzystać z dostępnych socketów,
  • komunikować się z usługami systemowymi,
  • korzystać z określonych interfejsów kernela,
  • wykorzystywać podatności w aplikacjach,
  • próbować wykorzystać błędną konfigurację systemu.

A przede wszystkim może wykonywać kod.

To ostatnie jest fundamentalne.

Jeżeli atakujący uzyska możliwość wykonywania kodu jako zwykły użytkownik, dostał już działającą platformę do dalszego rozpoznania systemu.

Nie musi od razu być rootem.

Najpierw może sprawdzić:

id
groups
sudo -l
uname -a
cat /etc/os-release
ps aux
systemctl list-units --type=service

Może również analizować:

find / -perm -4000 -type f 2>/dev/null

czy:

getcap -r / 2>/dev/null

Celem nie jest jeszcze „zrobić wszystko”.

Celem jest znalezienie granicy bezpieczeństwa.

I właśnie dlatego konto nieuprzywilejowane należy traktować jako ograniczony poziom zaufania, a nie jako magiczną strefę bezpieczeństwa.

Mit #2: „Najważniejsze jest usunięcie root loginu przez SSH”

Wyłączenie bezpośredniego logowania root przez SSH ma sens.

Konfiguracja:

PermitRootLogin no

eliminuje jedną ścieżkę bezpośredniego uzyskania sesji administracyjnej.

Ale nie rozwiązuje problemu uprawnień.

Administrator może nadal mieć:

sudo -l

i zobaczyć coś w rodzaju:

User marek may run the following commands:
    (ALL) /usr/bin/systemctl
    (ALL) /usr/bin/journalctl

W tym momencie pytanie nie brzmi już:

„Czy Marek może zalogować się jako root?”

Pytanie brzmi:

„Co dokładnie Marek może zrobić za pomocą programu uruchamianego z podwyższonymi uprawnieniami?”

To fundamentalna różnica.

sudo nie jest pojedynczym przełącznikiem „bezpieczny/niebezpieczny”.

Jest mechanizmem polityki.

Dokumentacja sudoers pozwala określać między innymi użytkownika, hosta, użytkownika docelowego i konkretne polecenia, które można uruchamiać z podwyższonymi uprawnieniami.

Dlatego zamiast:

marek ALL=(ALL) ALL

znacznie bardziej interesująca z punktu widzenia hardeningu jest polityka ograniczona do rzeczywiście potrzebnych operacji.

Nie chodzi o to, żeby administratorowi odebrać możliwość administracji.

Chodzi o to, żeby każda możliwość uzyskania wyższych uprawnień była celowa.

Mit #3: „Sudo jest bezpieczne, bo wymaga hasła”

Hasło zwiększa bezpieczeństwo, ale nie zmienia faktu, że sudo może uruchomić program jako root.

Jeżeli użytkownik posiada szerokie uprawnienia sudo, to kompromitacja jego konta może mieć znacznie większe konsekwencje niż kompromitacja konta pozbawionego takich uprawnień.

Jeszcze ciekawszy problem pojawia się wtedy, gdy administrator pozwala uruchamiać jako root programy, które same oferują mechanizmy wykonywania innych poleceń.

Dlatego podczas audytu nie wystarczy sprawdzić:

sudo -l

i odpowiedzieć:

„Jest sudo. OK.”

Trzeba przeanalizować co dokładnie sudo uruchamia.

Przykładowo:

sudo systemctl ...
sudo vim ...
sudo less ...
sudo python3 ...
sudo bash ...

to nie są równoważne poziomy ekspozycji.

Im bardziej ogólny mechanizm wykonywania kodu lub poleceń otrzymuje użytkownik z uprawnieniami administracyjnymi, tym bardziej rośnie znaczenie tej reguły.

Mit #4: „Root oznacza wszystkie uprawnienia, a user nie ma żadnych”

To już technicznie nieprawda.

Linux rozbił część tradycyjnych uprawnień roota na capabilities.

Zamiast jednego ogromnego pakietu uprawnień można przyznać procesowi określone możliwości.

Przykładowo:

CAP_NET_ADMIN
CAP_SYS_ADMIN
CAP_SYS_PTRACE
CAP_NET_RAW
CAP_DAC_OVERRIDE
CAP_SETUID

Każda z nich daje określone możliwości.

I tutaj pojawia się bardzo ważna rzecz dla administratora:

proces może nie działać jako UID 0, a mimo to posiadać bardzo potężne uprawnienia.

Dokumentacja kernela opisuje capabilities jako sposób rozdzielania tradycyjnych uprawnień superusera na mniejsze jednostki.

Dlatego podczas hardeningu warto sprawdzać:

getcap -r / 2>/dev/null

oraz dla konkretnego procesu:

grep Cap /proc/<PID>/status

Nie pytamy już tylko:

„Czy ten proces jest rootem?”

Pytamy:

„Jakie możliwości posiada ten proces?”

To znacznie lepsze pytanie bezpieczeństwa.

Mit #5: „Jeżeli aplikacja działa jako zwykły użytkownik, jej kompromitacja jest mało istotna”

To zależy od aplikacji.

Wyobraźmy sobie serwer aplikacyjny:

Internet
   |
   v
Nginx
   |
   v
Application
   |
   v
Database

Aplikacja działa jako:

www-data

Nie jako root.

To dobra decyzja.

Ale co znajduje się w zasięgu www-data?

Może to być:

/var/www/app
/var/www/uploads
/tmp
socket aplikacji
zmienne środowiskowe
pliki konfiguracyjne
sekrety aplikacji
klucze API
dostęp do Redis
dostęp do PostgreSQL

Jeżeli aplikacja ma podatność umożliwiającą wykonanie kodu, atakujący otrzymuje dokładnie takie możliwości, jakie posiada proces aplikacji.

A jeżeli konfiguracja aplikacji zawiera:

DATABASE_PASSWORD=
AWS_ACCESS_KEY_ID=
API_TOKEN=
SECRET_KEY=

to „nie-root” może nadal oznaczać bardzo poważny incydent.

Minimalne uprawnienia procesu nie oznaczają minimalnej wartości danych, do których ma dostęp.

To jedna z najważniejszych zasad hardeningu.

Mit #6: „Wystarczy dobrze ustawić chmod”

Model:

chmod 600
chmod 644
chmod 755

jest fundamentem bezpieczeństwa Linuksa.

Ale nie jest całym modelem bezpieczeństwa.

Klasyczny Unix DAC odpowiada przede wszystkim na pytanie:

„Czy ten użytkownik może wykonać tę operację na tym obiekcie?”

Nowoczesny Linux posiada dodatkowe warstwy.

Jedną z nich jest AppArmor.

Ubuntu opisuje AppArmor jako mechanizm Mandatory Access Control, który uzupełnia tradycyjny model DAC i ogranicza możliwości aplikacji na podstawie profili.

To oznacza, że nawet jeśli proces ma określone uprawnienia systemu plików, polityka AppArmor może dodatkowo ograniczyć jego zachowanie.

Przykładowo aplikacja może mieć dostęp do:

/var/www/

ale profil AppArmor może ograniczyć:

odczyt określonych plików
zapis określonych katalogów
wykonywanie programów
dostęp sieciowy
komunikację IPC
ptrace
wybrane capabilities

To właśnie jest defense in depth.

Nie jedna bariera.

Kilka niezależnych ograniczeń.

Mit #7: „Root zawsze może wszystko”

To również wymaga korekty.

Tradycyjnie root ma ogromne możliwości.

Ale mechanizmy MAC mogą ograniczać nawet proces posiadający UID 0.

AppArmor jest dobrym przykładem.

Dokumentacja Ubuntu opisuje sytuacje, w których profil AppArmor ogranicza uprzywilejowany proces również wtedy, gdy działa on jako root.

To bardzo ważny koncept:

UID 0
  |
  +---- DAC
  |
  +---- capabilities
  |
  +---- AppArmor / SELinux
  |
  +---- namespaces
  |
  +---- seccomp
  |
  +---- kernel security controls

Nie wszystkie warstwy podejmują decyzję na podstawie tego samego kryterium.

Dlatego współczesne hardening Linuxa powinno patrzeć na system jako na zestaw nakładających się granic zaufania.

Mit #8: „Kontener rozwiązuje problem uprawnień”

Nie.

Kontener może bardzo mocno ograniczyć proces.

Ale kontener nie jest automatycznie bezpieczną piaskownicą.

To zresztą jeden z tematów, które omawialiśmy już szerzej w ramach hardeningu Dockera.

Problem pojawia się między innymi wtedy, gdy kontener otrzymuje nadmierne capabilities, dostęp do wrażliwych socketów albo działa z niepotrzebnymi uprawnieniami.

Przykład:

Container
   |
   +-- CAP_SYS_ADMIN
   +-- host filesystem
   +-- privileged
   +-- Docker socket

W takim środowisku słowo „container” nie powinno uspokajać administratora.

Wręcz przeciwnie.

Powinno uruchomić pytanie:

„Jaką dokładnie granicę bezpieczeństwa ten kontener zapewnia?”

To jest dużo lepszy model niż:

„To tylko kontener.”

Mit #9: „Jeżeli zwykły użytkownik może stworzyć user namespace, to ma root”

Tutaj pojawia się jeden z najbardziej interesujących mechanizmów współczesnego Linuksa.

User namespaces pozwalają procesowi posiadać inne identyfikatory użytkownika i capabilities wewnątrz określonej przestrzeni nazw.

Proces może być:

UID 1000

w początkowej przestrzeni użytkownika, a jednocześnie posiadać:

UID 0

we własnym user namespace.

To nie oznacza automatycznie roota całego systemu.

Kernel rozdziela te konteksty.

Dokumentacja Linuxa opisuje właśnie taki model: proces może być UID 0 wewnątrz user namespace i jednocześnie pozostać nieuprzywilejowany wobec początkowego user namespace.

To potężny mechanizm izolacyjny.

Ale jednocześnie zwiększa złożoność modelu bezpieczeństwa.

Dlatego bezpieczeństwo nie może być oceniane wyłącznie przez:

id

Trzeba rozumieć również:

namespace
capabilities
LSM
seccomp
filesystem permissions
cgroups
service boundaries

Najbardziej niebezpieczny mit: „Nie-root = problem rozwiązany”

Ten mit jest szczególnie groźny, ponieważ prowadzi do fałszywego poczucia bezpieczeństwa.

Wyobraźmy sobie serwer:

SSH
 |
 v
user
 |
 +--> sudo
 |
 +--> SUID
 |
 +--> capabilities
 |
 +--> system services
 |
 +--> Docker
 |
 +--> kernel interfaces
 |
 +--> application secrets

Użytkownik zaczyna jako:

UID 1000

Ale jego konto ma dostęp do kilku mechanizmów, które prowadzą do innych granic zaufania.

Dlatego prawidłowe pytanie podczas hardeningu brzmi:

„Jakie ścieżki prowadzą od tego procesu do bardziej uprzywilejowanego kontekstu?”

To zupełnie inne podejście.

Nie patrzymy na pojedynczy parametr.

Patrzymy na graf zależności.

Jak powinien wyglądać prawdziwy model hardeningu?

Dobrze zabezpieczony Linux powinien ograniczać kilka rzeczy jednocześnie.

Pierwsza warstwa to użytkownicy:

Kto może się zalogować?

Druga:

Do jakich grup należy?

Trzecia:

Co może uruchomić przez sudo?

Czwarta:

Jakie SUID/SGID istnieją?

Piąta:

Jakie capabilities zostały przyznane?

Szósta:

Jakie usługi działają jako root?

Siódma:

Jakie pliki mogą być przez nie modyfikowane?

Ósma:

Jakie mechanizmy MAC ograniczają proces?

Dziewiąta:

Jakie interfejsy kernela są dostępne?

Dziesiąta:

Co stanie się po przejęciu konkretnego procesu?

Dopiero taki model zaczyna przypominać rzeczywisty audyt bezpieczeństwa.

Praktyczny test: sprawdź, kim naprawdę jesteś

Na własnym serwerze zacznij od:

id

Potem:

groups

Następnie:

sudo -l

Sprawdź capabilities:

getcap -r / 2>/dev/null

SUID:

find / -perm -4000 -type f 2>/dev/null

Aktywne usługi:

systemctl list-units --type=service --state=running

AppArmor:

sudo aa-status

Aktywne moduły bezpieczeństwa kernela:

cat /sys/kernel/security/lsm

I dopiero wtedy można zacząć zadawać właściwe pytania.

Nie:

„Czy jestem rootem?”

Ale:

„Co może zrobić proces uruchomiony w moim kontekście?”

Co zmienić w praktyce?

Najważniejsza zasada brzmi:

nie usuwaj root jako celu hardeningu — ograniczaj ścieżki prowadzące do uprzywilejowanego działania.

W praktyce oznacza to:

Minimalne konta
      |
      v
Minimalne grupy
      |
      v
Minimalne sudo
      |
      v
Minimalne capabilities
      |
      v
Minimalne SUID
      |
      v
Minimalne usługi
      |
      v
MAC / AppArmor / SELinux
      |
      v
Ograniczenie interfejsów kernela
      |
      v
Monitoring i aktualizacje

Każda warstwa powinna ograniczać konsekwencje awarii następnej.

Jeżeli aplikacja zostanie przejęta, nie powinna automatycznie otrzymać dostępu do całego systemu.

Jeżeli konto użytkownika zostanie przejęte, nie powinno automatycznie oznaczać pełnego dostępu administracyjnego.

Jeżeli proces uzyska podwyższone capabilities, nie powinien bez potrzeby otrzymać całego zestawu możliwości roota.

Jeżeli jedna warstwa zawiedzie, następna powinna nadal działać.

Rzeczywistość zamiast mitu

Linux nie jest bezpieczny dlatego, że istnieje konto root.

Linux nie jest też bezpieczny dlatego, że użytkownik pracuje jako UID 1000.

Bezpieczeństwo wynika z granic.

Granicy pomiędzy użytkownikiem a administratorem.

Granicy pomiędzy procesem a systemem plików.

Granicy pomiędzy procesem a kernelem.

Granicy pomiędzy kontenerem a hostem.

Granicy pomiędzy usługą a innymi usługami.

Granicy pomiędzy tym, co proces może zrobić, a tym, czego zrobić nie może.

I właśnie dlatego współczesny hardening powinien odejść od prostego:

root = zły
user = dobry

na rzecz:

Kim jest proces?
Jakie ma UID?
Jakie ma GID?
Jakie ma capabilities?
Jakie ma uprawnienia DAC?
Jaki profil MAC go ogranicza?
Do jakich namespace należy?
Z jakimi usługami może się komunikować?
Jakie sekrety może odczytać?
Co stanie się po jego przejęciu?

To jest różnica między konfiguracją Linuksa a projektowaniem bezpieczeństwa Linuksa.

I właśnie na tym poziomie zaczyna się prawdziwy hardening.

Powiązane materiały Netbe

Warto zestawić ten materiał z wcześniejszym artykułem „Privilege Escalation w Linuxie: Od użytkownika do roota”, który opisuje konkretne ścieżki eskalacji uprawnień. (Netbe)

Dobrym uzupełnieniem jest również wcześniejszy materiał Netbe o mechanizmie AppArmor oraz artykuły z serii Linux Hardening dotyczące warstwowego modelu bezpieczeństwa.

 

Polecane wpisy
Jak utworzyć zaszyfrowany katalog w Linuksie za pomocą gocryptfs lub encfs – krok po kroku
Jak utworzyć zaszyfrowany katalog w Linuksie za pomocą gocryptfs lub encfs – krok po kroku

🔐 Jak utworzyć zaszyfrowany katalog w Linuksie za pomocą gocryptfs lub encfs – krok po kroku Czasami nie potrzebujesz szyfrować Czytaj dalej

Skonfiguruj SPF, DKIM i DMARC: Poradnik administratora
Skonfiguruj SPF, DKIM i DMARC: Poradnik administratora

Skonfiguruj SPF, DKIM i DMARC: Poradnik administratora Wstęp: SPF, DKIM i DMARC to trzy kluczowe protokoły uwierzytelniania poczty e-mail, które Czytaj dalej

Marek "Netbe" Lampart Inżynier informatyki Marek Lampart to doświadczony inżynier informatyki z ponad 25-letnim stażem w zawodzie. Specjalizuje się w systemach Windows i Linux, bezpieczeństwie IT, cyberbezpieczeństwie, administracji serwerami oraz diagnostyce i optymalizacji systemów. Na netbe.pl publikuje praktyczne poradniki, analizy i instrukcje krok po kroku, pomagając administratorom, specjalistom IT oraz zaawansowanym użytkownikom rozwiązywać realne problemy techniczne.