Blog
PolskiEnglish

Co naprawdę siedzi na dziewięciu zwykłych serwerach

Piszę w tej serii od kilku tygodni, że liczba podatności nic nie znaczy. Łatwo tak twierdzić, trudniej pokazać. Więc pokazuję, na własnej infrastrukturze, z surowymi liczbami zamiast anegdoty.

Zanim przejdę do danych, trzy zastrzeżenia, bo bez nich ten tekst byłby nieuczciwy. Po pierwsze, to jest moja własna infrastruktura i ona nie podlega żadnemu audytowi. To materiał operacyjny, nie dowód zgodności i nie studium przypadku wdrożenia. Po drugie, mówię o tym, co widać na dziewięciu maszynach jednoosobowej firmy. Wasze liczby będą inne, struktura prawdopodobnie podobna. Po trzecie, wszystkie liczby pochodzą z jednego pomiaru z 19 września 2026, poza jednym miejscem, gdzie wracam do sierpnia, i tam mówię o tym wprost, bo o tę różnicę chodzi.

Co to za maszyny

Dziewięć serwerów, z czego osiem aktywnych. Nic egzotycznego: cztery razy Ubuntu 24.04, dwa razy Ubuntu 22.04, jeden Ubuntu 26.04, dwa razy Debian 13. Mieszanka tego, co zwykle stoi w małej firmie technicznej. Trzy z nich są wystawione do internetu, pięć stoi w sieci wewnętrznej. Trzy uruchamiają kontenery, pięć nie, i to rozróżnienie okaże się ważniejsze niż cała reszta.

Żadna z nich nie jest po końcu wsparcia. Najbliższy termin to Ubuntu 22.04, kwiecień 2027, i dotyczy dwóch maszyn. To akurat wyjątkowo nudna część tej historii i dobrze, bo system po EOL to temat na osobny artykuł, w którym nudno już nie jest.

Pierwsza liczba, ta bez znaczenia

Na ośmiu aktywnych maszynach rejestr ma dziś 165 608 otwartych pozycji. Gdybym chciał zrobić na kimś wrażenie, zatrzymałbym się na tym zdaniu i dołożył wykrzyknik.

Rozbicie na wagi wygląda tak:

Waga Instancji Unikalnych CVE
Krytyczne 376 46
Wysokie 10 382 756
Średnie 141 949 5075
Niskie 12 862 817
Nieokreślone 39 18

Pierwsza kolumna to liczba, którą pokazuje panel: 165 608. Druga sumuje się do 6649 unikalnych podatności. Różnica bierze się stąd, że jedna podatność siedząca na czterech maszynach liczy się cztery razy, i słusznie, bo naprawiać trzeba w czterech miejscach. Ale rozstrzyga się to raz, klika cztery razy.

Kolumna pierwsza mówi, ile jest roboty. Kolumna druga, ile jest myślenia. Mylenie ich to jest dokładnie ten moment, w którym rejestr podatności zamienia się w pustą tabelę odhaczaną raz na kwartał.

Skąd bierze się tłum

Tu jest najciekawsze i zupełnie nie tam, gdzie się spodziewałem. Pierwsze dwadzieścia pięć miejsc na liście pakietów zajmuje jedna rzecz: jądro. Pogrupowałem tę dwudziestkę piątkę po wersji jądra, bo tak widać, z czego składa się ta liczba.

Wersja jądra Pakietów w czołówce Instancji na pakiet Razem
6.8.0-139 5 8694 43 470
6.8.0-134 5 3929 19 645
7.0.0-27 5 3913 19 565
6.8.0-136 5 3016 15 080
wspólne dla wszystkich 2 11 193 22 386

Trzecia kolumna jest tu najważniejsza. Wszystkie pakiety jednej wersji jądra mają dokładnie tę samą liczbę pozycji, co do sztuki, bo to jest ta sama lista podatności policzona tyle razy, ile pakietów dystrybucja z tego jądra wykroiła: osobno headers, osobno modules, osobno tools.

Jądro Linuksa to największy pojedynczy kawałek oprogramowania na serwerze i ma najwięcej opublikowanych podatności ze wszystkiego, co na nim leży. Do tego na mojej flocie leżą równolegle cztery wersje z tabeli plus piąta, 6.8.0-137, która nie zmieściła się w czołówce w komplecie. Stare jądra zostają po aktualizacji, dopóki ktoś nie zrobi porządku, i każde nosi własny komplet pakietów.

Pięć wierszy tej tabeli to 120 146 pozycji. Z resztą pakietów jądra, które weszły do czołówki, wychodzi około stu dwudziestu dziewięciu tysięcy, czyli blisko osiemdziesiąt procent całego rejestru. Całe pozostałe oprogramowanie na ośmiu serwerach, wszystkie bazy danych, serwery WWW, biblioteki i narzędzia, mieści się w tej jednej piątej, która zostaje.

Dla porównania: w sierpniu na szczycie tej listy stał vim z 688 pozycjami. Usunąłem go tam, gdzie nie był potrzebny, i dziś ma jedną. To była jedna komenda i nadal uważam, że to najtańsza redukcja listy, jaka istnieje. Tyle że przy jądrze taka sztuczka nie przejdzie, bo jądra nie odinstalujesz.

Ile z tego jest w ogóle do zrobienia

I tu pada liczba, która zmienia cały obrazek: ile z tego ma w ogóle dostępną wersję naprawiającą. Jedno zastrzeżenie techniczne, bo bez niego te liczby nie zgadzałyby się z poprzednimi. Informacja o wersji naprawiającej nie siedzi w rejestrze, tylko w ostatnim skanie, więc trzeba zestawić jedno z drugim, a takie zestawienie daje 166 961 dopasowań zamiast 165 608 pozycji. Nadmiar bierze się stąd, że ta sama podatność potrafi wyjść dwa razy na jednej maszynie, raz w systemie, raz w obrazie kontenera. Wrócę do tego niżej, bo to jest ważniejsze, niż wygląda. Proporcja jest w każdym razie jednoznaczna:

Stan Instancji Pakietów
Jest wersja naprawiająca 17 230 310
Brak, czeka na dystrybucję 149 731 449

Dziewięćdziesiąt procent tej listy to nie jest niczyja zaległość. To są podatności, na które producent albo dystrybucja nie wypuścili jeszcze poprawki. Nie da się ich naprawić, można je tylko znać. Wśród krytycznych proporcja jest podobna: 111 dopasowań z łatką, 276 bez.

Zwróć uwagę na prawą kolumnę. Te 17 230 instancji, które da się naprawić, mieści się w 310 pakietach. Tyle wierszy miałaby lista rzeczy do zrobienia, gdyby zamiast podatności pokazywać zadania. Sto sześćdziesiąt pięć tysięcy pozycji kontra trzysta dziesięć wierszy, i to jest ta sama flota w tej samej chwili.

Co z tego jest realnie groźne

Waga to opinia o tym, jak bardzo podatność mogłaby zaszkodzić, gdyby ktoś jej użył. Nie mówi nic o tym, czy ktokolwiek jej używa. Dlatego sprawdziłem dwie rzeczy poza wagą.

Pierwsza to katalog KEV prowadzony przez amerykańską agencję CISA, czyli lista podatności, przy których są dowody, że ktoś ich naprawdę użył w ataku. Sprawdziłem to w sierpniu, wyszło zero i prawie tak to tutaj zostawiłem. Dobra wiadomość, powiedziana wprost, bo dobre wiadomości też są wynikiem.

Zero było nieprawdą.

Sprawdzenie porównuje moje podatności z kopią katalogu, którą trzymam u siebie. Miesiąc później zajrzałem do samej kopii. Katalog CISA ma dziś 1716 pozycji. Moja baza znała 322 w sierpniu i 390, kiedy do niej zajrzałem. Odpowiedź "żadna z twoich podatności nie jest wykorzystywana w atakach" powstawała więc z porównania z niecałą jedną czwartą listy i brzmiała dokładnie tak samo pewnie, jak brzmiałaby prawda. Nic nie zapaliło się na czerwono, bo z punktu widzenia mechanizmu wszystko było w porządku: pytanie zadane, odpowiedź zwrócona.

Po uzupełnieniu katalogu do kompletu wynik wygląda inaczej. Siedem unikalnych podatności z mojej floty jest w katalogu KEV, w 95 miejscach na pięciu maszynach. Wszystkie siedem wychodzi na maszynach wystawionych do internetu, a dwie z nich, obie w pakietach jądra, dodatkowo na wewnętrznych. Pozostałe to apache2, git, inetutils-syslogd, starlette i libfreetype6. Cztery z nich mają gotową wersję naprawiającą, czyli do zrobienia jest aktualizacja pakietu, a nie projekt. Libfreetype6 nie ma jej wcale i akurat on stoi po stronie internetu. Przy dwóch jądrowych tego nie sprawdziłem i nie będę zgadywał.

I rzecz, która mnie w tym zestawieniu zatrzymała na dłużej: ani jedna z tej siódemki nie ma wagi krytycznej. Pięć jest wysokich, dwie średnie. Sortując listę po wadze, nie zobaczyłbym żadnej z nich na górze. Te same podatności, co do których wiadomo, że ktoś ich właśnie używa, leżą w mojej tabeli poniżej czterdziestu sześciu unikalnych podatności oznaczonych jako krytyczne.

Siedem podatności, o których wiadomo, że ktoś ich używa w atakach gdzieś na świecie, siedzi na mojej flocie. To nie jest wypadek przy pracy ani dowód zaniedbania, i nie znaczy też, że ktokolwiek zaatakował akurat mnie. Jądro, apache2, git, demon syslogu, biblioteka do czcionek, framework webowy. Zwyczajne oprogramowanie, w którym ktoś znalazł dziurę już po tym, jak je zainstalowałem. Prowadzę firmę, która żyje z pilnowania podatności, i mam je u siebie tak samo jak każdy inny. Pod latarnią najciemniej, tyle że tu nie ma czego ukrywać: podatności nie są oznaką bałaganu, tylko czymś, co przychodzi samo, co tydzień, do każdego, kto trzyma włączony serwer. Różnica między firmą, która nad tym panuje, a firmą, która nie panuje, nie polega na tym, ile ich ma. Polega na tym, czy wie, które ze stu sześćdziesięciu pięciu tysięcy pozycji to te siedem, i czy zauważy ósmą w dniu, w którym się pojawi.

Zostawiam tu obie liczby, sierpniowe zero i wrześniową siódemkę, bo różnica między nimi mówi więcej niż którakolwiek z osobna. Zielony status znaczy tylko tyle, że sprawdzenie się wykonało. Nie mówi, ile ono objęło. Dziś obejmuje cały katalog i całą flotę, 6649 podatności z 6649, więc odpowiedź "nie ma" wreszcie znaczyłaby "nie ma". Przez miesiąc nie umiałem odróżnić tych dwóch rzeczy na własnym narzędziu, więc jeśli masz u siebie mechanizm, który od dawna niczego nie znalazł, sprawdź nie jego status, tylko jego zasięg.

Druga rzecz poza wagą to EPSS, czyli statystyczna prognoza prawdopodobieństwa, że dana podatność będzie wykorzystywana w atakach w ciągu najbliższych trzydziestu dni. Tu od razu zastrzeżenie, bo właśnie opisałem, co się dzieje, gdy się go nie zrobi: EPSS znam dla 4841 z 6649 podatności. Dla pozostałych 1808 nie wiem i nie zamierzam tego przemilczeć.

W tych 4841, które znam, czternaście podatności spoza katalogu KEV trafia do górnych dziesięciu procent pod względem prawdopodobieństwa wykorzystania. Sześć z nich ma wagę wysoką, osiem średnią. Znowu ani jednej krytycznej.

Podsumujmy te dwie listy. Z 6649 unikalnych podatności dwadzieścia jeden ma albo dowód wykorzystania, albo wysokie prawdopodobieństwo. Dwadzieścia jeden rzeczy do przemyślenia z pięciu tysięcy stron wydruku, i żadna z nich nie jest tam, gdzie patrzy ktoś, kto sortuje po wadze.

Czyja to w ogóle robota

Teraz podział, o którym w tekstach o podatnościach prawie się nie mówi, a który w większej firmie decyduje o wszystkim. Skaner patrzy na dwie zupełnie różne warstwy: na system operacyjny hosta i na zawartość obrazów kontenerów, które na tym hoście chodzą. Rozdzieliłem je.

Warstwa Instancji Unikalnych CVE Pakietów Maszyn
Host 163 840 5936 505 8
Obrazy kontenerów 3121 1099 279 3

Na pierwszy rzut oka sprawa wygląda jednoznacznie: kontenery to niecałe dwa procent listy, szum, można pominąć. I to jest dokładnie ten błąd, przed którym ostrzega cały ten artykuł, bo licznik znowu nie mówi nic o tym, co się liczy. Ta sama tabela z jedną kolumną więcej:

Warstwa Instancji Z dostępną łatką Udział
Host 163 840 14 632 8,9%
Obrazy kontenerów 3121 2598 83%

Na hoście da się dzisiaj naprawić niecałe dziewięć procent pozycji, cała reszta czeka na dystrybucję. W obrazach kontenerów da się naprawić osiemdziesiąt trzy procent, bo poprawka jest już w nowszej wersji obrazu, a nie w kolejce u dystrybucji. To nadal znaczy przebudowę i ponowne wdrożenie, czyli realną robotę. Ale to jest robota, którą da się zaplanować, a nie czekanie na kogoś innego.

Warstwa, która w rankingu wygląda na szum, jest więc tą, w której praca faktycznie coś zamyka. I na tym polega cała rzecz: to są dwa różne rodzaje roboty, dwa różne kalendarze i, w firmie większej niż moja, dwie różne osoby. Pakiety systemowe, jądra i okno serwisowe to biurko administratora infrastruktury. Obrazy, pliki Dockerfile, tagi i pipeline to biurko zespołu od kontenerów. Żadna z tych osób nie zamknie tej listy sama, a rejestr, który ich nie rozdziela, pokaże obu tę samą stustronicową tabelę i obie odłożą ją na później.

Najbardziej obrazowe są konkretne obrazy:

Obraz Instancji Unikalnych CVE Krytycznych Z łatką
check-mk-raw 1346 540 0 1152
uptime-kuma 502 323 15 380
glitchtip 409 224 4 217

Największy obraz na liście nie ma ani jednej pozycji krytycznej i prawie wszystko da się w nim załatać. Drugi, prawie trzy razy mniejszy, ma ich piętnaście. Gdyby sortować po liczbie pozycji, zająłbym się najpierw tym niewłaściwym.

Przy okazji tego podziału wyszła mi rzecz, której się nie spodziewałem i która jest czysto moją winą. Ten sam portainer/agent chodzi u mnie na dwóch maszynach w dwóch różnych wersjach, 2.39.2 i 2.39.3. traefik:v3.7 to na dwóch maszynach dwa różne obrazy o tym samym tagu. To samo z postgres:18-alpine. Tag mówi, że wszędzie stoi to samo. Skan mówi, że nie. Żeby to zobaczyć, trzeba patrzeć na identyfikator obrazu, a nie na jego nazwę, i to jest dokładnie ten rodzaj rozjazdu, który w większej flocie rośnie po cichu przez rok.

Kto jest na świeczniku

Ekspozycja maszyny to jedyny kontekst, który zamienia tę listę w kolejność działania:

Klasyfikacja Maszyn Instancji Krytycznych
Wystawione do internetu 3 85 205 33
Wyłącznie wewnętrzne 5 80 403 24

Liczby są prawie równe, co samo w sobie jest ciekawe. Ale tamtych trzydzieści trzy krytycznych i wszystkie siedem podatności z katalogu KEV siedzą po stronie, którą widać z internetu, a pozostałe dwadzieścia cztery są za zamkniętymi drzwiami. To ta sama waga i zupełnie inna pilność.

Czego nie wiedziałem w sierpniu, i dlaczego to była moja wina

Miesiąc temu nie umiałbym narysować tej tabeli, bo większość maszyn nie miała u mnie żadnego oznaczenia ekspozycji. W momencie, w którym dane wreszcie podpowiadają, czym zająć się najpierw, brakowało mi ostatniego elementu układanki: czy ta maszyna jest osiągalna z internetu, czy stoi w zamkniętej sieci.

To nie był brak narzędzia, tylko mój dług. Oznaczenie dziewięciu maszyn to praca na dziesięć minut, której nie zrobiłem, bo nie wydawała się pilna. W końcu te dziesięć minut poświęciłem i to one zamieniły zdanie o KEV z listy identyfikatorów w zdanie "wszystkie siedem stoi po stronie internetu".

Piszę o tym, bo to jest typowe. Kontekst, który zamienia listę podatności w listę decyzji, prawie zawsze jest wiedzą, którą ma się w głowie, a nie w systemie. Dopóki tam siedzi, nie da się jej ani przeszukać, ani przekazać, ani pokazać audytorowi.

Czego w tym tekście nie ma

Uczciwość wymaga pokazania także tego, czego u siebie nie zrobiłem.

Mój rejestr decyzji jest pusty. Nie "prawie pusty". Wszystkie 165 608 otwartych pozycji nie ma przy sobie ani jednego wiersza decyzji: ani przypisanego właściciela, ani terminu, ani świadomej akceptacji ryzyka z uzasadnieniem. Zbieram dane i patrzę na nie, a to jest dopiero połowa roboty. Druga połowa zaczyna się w chwili, gdy ktoś napisze przy pozycji "zostawiamy do marca, bo X, i wtedy to przeglądamy".

Nie wszystko, co znika z listy, jest zasługą procesu. W ostatnim miesiącu z rejestru wypadło 419 pozycji. Z tego 238 zamknął zwykły apt upgrade, który uruchomiłem ręcznie na trzech maszynach, bo tak mi wypadło w kalendarzu. Gdybym tego nie rozdzielił, tekst przypisałby produktowi zasługę za decyzję, którą podjąłem sam i podjąłbym tak samo bez niego.

Nie mówię, że to jest naprawione. Kiedy pozycja znika ze skanu, wiem tylko tyle, że skaner jej już nie widzi. Mogła zostać zaktualizowana, ale równie dobrze pakiet mógł zostać odinstalowany albo skaner zmienił sposób rozpoznawania. To dwie różne rzeczy i nazywanie obu "naprawą" jest tym samym rodzajem kłamstwa co zielony status na niepełnym katalogu.

Co z tego wynika

Sto sześćdziesiąt pięć tysięcy to rozmiar problemu, którego nie ogarnia nikt. Jeśli twoim narzędziem jest eksport do arkusza, dostajesz tę liczbę i nic poza nią. Nie dlatego, że skaner jest zły, tylko dlatego, że skaner mówi wszystko, co widzi, a arkusz nie ma jak odróżnić piątej wersji jądra od demona syslogu wystawionego do internetu.

Ta sama flota, w tej samej chwili, opisana inaczej: dwadzieścia jeden podatności z dowodem wykorzystania albo wysokim prawdopodobieństwem, trzysta dziesięć pakietów, które da się dziś zaktualizować, i jeden obraz kontenera z piętnastoma pozycjami krytycznymi. To już jest lista, którą da się przeczytać przy kawie i rozdzielić między dwie osoby.

Żadna z tych liczb nie wymagała nowej funkcji. Wymagała trzech rzeczy, które od początku leżały w danych i nie były używane: kompletnego katalogu wykorzystywanych podatności, informacji, czy istnieje wersja naprawiająca, i oznaczenia, które maszyny widać z internetu.

Jak to sprawdzić u siebie

Nie musisz mi wierzyć na słowo i nie musisz niczego kupować, żeby powtórzyć ten rachunek. Odpal skaner na jednej swojej maszynie i zadaj wynikowi cztery pytania. Ile jest unikalnych CVE, a nie pozycji. Ile z nich ma w ogóle dostępną wersję naprawiającą. Ile siedzi w obrazach kontenerów, a ile w systemie hosta. I na koniec: które z nich są na maszynie osiągalnej z internetu.

Jeśli odpowiedzi będą podobne do moich, a obstawiam, że będą, to masz materiał na rozmowę z zarządem lepszy niż jakakolwiek prezentacja. Nie "mamy sto sześćdziesiąt pięć tysięcy podatności", tylko "siedem z nich jest potwierdzonych jako wykorzystywane w atakach i wszystkie stoją na maszynach widocznych z internetu, dziewięćdziesiąt procent listy czeka na poprawkę od dystrybucji i nikt nic z tym nie zrobi, a to, co realnie da się zamknąć, mieści się w trzystu dziesięciu pakietach i w obrazach, które i tak przebudowujemy co tydzień".

To jest różnica między rejestrem, który straszy, a rejestrem, który da się prowadzić.

Jedna uwaga na koniec, bo wynika wprost z tego podziału na warstwy. Ta lista rozchodzi się na dwa biurka i w wielu firmach te biurka stoją w różnych pokojach: administrator infrastruktury odpowiada za jądra i okna serwisowe, zespół od kontenerów za obrazy i pipeline. Prowadzę SYSDEVOPS.EU właśnie dlatego, że robię obie te rzeczy i widzę, gdzie one się rozjeżdżają. Ten tekst jest w całości o mojej własnej flocie, więc traktujcie to jako informację, skąd się bierze, a nie jako ofertę.

Cotygodniowy digest CVE

Jeden mail w tygodniu z nowo opublikowanymi podatnościami, o których warto wiedzieć. Bez zakładania konta.

Digest dotyczy ogólnie nowych podatności, nie Twoich serwerów. Jeśli chcesz wiedzieć, które z nich faktycznie działają w Twojej infrastrukturze, tym zajmuje się Secvalis : skanuje Twoje maszyny i zgłasza wyłącznie to, co ich dotyczy.

Zgadzam się na otrzymywanie cotygodniowego digestu CVE na podany adres e-mail. Zgodę mogę wycofać w każdej chwili linkiem w stopce każdej wiadomości.

Dane podatności pochodzą z NVD (NIST) · CISA KEV · EPSS