Proces kdevtmpfsi w kontenerze z WordPressem to koparka kryptowalut z rodziny kinsing. Zabijanie jej cronem z pkill tylko ukrywa objaw, bo furtka, którą weszła, zostaje otwarta. Trzeba ustalić wektor wejścia, zamknąć porty, które Docker wystawia z pominięciem UFW, i zmienić sekrety. Poniżej opisujemy dwa serwery, na których znaleźliśmy ten sam cron, i co z niego wynikło.
Co to jest kinsing i proces kdevtmpfsi?
kinsing to malware znane z atakowania usług wystawionych do internetu: niezabezpieczonego Docker API, Redisa bez hasła, źle skonfigurowanych aplikacji. Po wejściu uruchamia koparkę, która w liście procesów zwykle widnieje jako kdevtmpfsi. Objaw jest prosty. Serwer zwalnia, a jeśli na jednym VPS-ie stoi kilka sklepów w osobnych kontenerach, zwalniają wszystkie naraz, bo koparka w jednym kontenerze zabiera procesor całej maszynie.
Dlaczego pkill kinsing w cronie nie wystarcza?
Na pierwszym serwerze koparka siedziała w kontenerze WordPressa, na VPS-ie współdzielonym z innymi sklepami. Ktoś przed nami dodał na hoście wpis, który w uproszczeniu wyglądał tak:
*/2 * * * * pkill kinsing; pkill kdevtmpfsiObjaw zniknął z widoku. Przyczyna nie została jednak usunięta, wektora nikt nie ustalił, a cron przy okazji zacierał jedyny sygnał, że atakujący wciąż ma dostęp. Koparka między kolejnymi uruchomieniami crona miała do 2 minut pracy, a każdy, kto potrafił ją wgrać, mógł wgrać też coś innego.
Przegląd reszty serwera pokazał, dlaczego wejście było łatwe:
- panel Nginx Proxy Manager dostępny publicznie na porcie 81, po HTTP,
- origin osiągalny z pominięciem Cloudflare,
- Ubuntu po końcu wsparcia, bez łatek bezpieczeństwa od około roku,
- 357 katalogów zapisywalnych dla wszystkich (
o+w), - UFW nieaktywne, brak fail2ban,
- codzienny
docker restartwszystkich kontenerów jako „łatka”.
Jak sprawdzić, czy koparka nadal działa?
Na drugim serwerze był dokładnie ten sam cron. Tu audyt dał inny wynik: infekcja historyczna. Sprawdziliśmy:
- Plik
/etc/ld.so.preload. Rootkity używają go do ukrywania procesów przedpsitop. Tu go nie było. - Klucze SSH. Pliki
authorized_keyswszystkich kont. Obcych kluczy brak. - Procesy i połączenia wychodzące. Nic podejrzanego w
psani wss -tunp. - Katalogi tymczasowe.
/tmpczyste. - Logi. Wpisy z „kinsing” w syslogu okazały się echem samego crona z
pkill, a nie śladem aktywnej koparki.
Zarekomendowaliśmy zamianę crona zabijającego na taki, który tylko loguje. Działa jak kanarek: jeśli koparka wróci, zobaczysz to w logach, zamiast ją po cichu zabijać.
*/2 * * * * pgrep -a 'kinsing|kdevtmpfsi' | logger -t miner-canaryUwaga: cron z pkill nie jest dowodem infekcji, ale jego brak w logach też nic nie mówi. Rozstrzyga dopiero audyt hosta i kontenerów.
Masz ten sam problem? Widzisz kdevtmpfsi albo cron z pkill na swoim serwerze? Sprawdzimy, czy infekcja jest aktywna i którędy weszła. Opisz sytuację →
Dlaczego Docker omija UFW?
Publikacja portu w docker-compose (ports:) dodaje regułę prosto do iptables. Ruch do kontenera nie przechodzi przez reguły UFW, a ufw status tej reguły nie pokazuje. Administrator widzi zamkniętą zaporę i uważa temat za załatwiony, podczas gdy baza danych, Redis czy panel phpMyAdmin odpowiadają na połączenia z dowolnego adresu w internecie.
Każdy port, który nie musi być publiczny, bindujemy na localhost:
services:
db:
ports:
- "127.0.0.1:3306:3306"Druga warstwa to reguły DROP w łańcuchu DOCKER-USER, który Docker sprawdza przed własnymi regułami. Na jednym serwerze zablokowaliśmy tak porty PHP-FPM 9000–9300:
iptables -I DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 9000:9300 -j DROPTe reguły nie przeżywają restartu serwera, dlatego zapisaliśmy je w skrypcie uruchamianym z crona przez @reboot. Na innym VPS-ie podobne DROP-y objęły Redis, phpMyAdmin, Typesense, serwer vite i API.
IPv6: druga furtka obok zapory
W innym audycie zapora dla IPv4 była w porządku, ale IPv6 omijał ją całkowicie. Po IPv6 otwarte były Redis bez hasła, MariaDB, Typesense i phpMyAdmin, a aplikacja łączyła się z bazą jako root. Reguły iptables nie obejmują IPv6, do tego służy osobne ip6tables, więc serwer trzeba skanować z zewnątrz po obu adresach, a nie tylko po tym, który widnieje w DNS sklepu.
Jak sprawdzić, czy Redis w kontenerze ma hasło?
redis-cli -h <adres-serwera> pingDwa kontenery na jednym z audytowanych serwerów odpowiedziały PONG bez autoryzacji. Firewall je osłaniał, ale requirepass nie był ustawiony, więc jeden błąd w regułach wystarczyłby do pełnego dostępu. Docelowo ustawiamy hasło i bindujemy Redisa na 127.0.0.1 albo nie publikujemy portu wcale, jeśli korzysta z niego tylko aplikacja w tej samej sieci Dockera.
Co zrobić po wykryciu kinsinga na serwerze?
- Zabezpiecz dowody przed sprzątaniem: listę procesów, crontaby, logi.
- Ustal wektor. Sprawdź każdą usługę dostępną z internetu: Docker API, Redis, bazy danych, PHP-FPM na
0.0.0.0, panele administracyjne. - Zamknij porty. Bind na
127.0.0.1, DROP wDOCKER-USER, osobno IPv6. - Postaw kontenery od nowa z czystych obrazów zamiast sprzątać działające.
- Zaktualizuj system. Ubuntu bez wsparcia to otwarte znane luki.
- Panel proxy i origin schowaj za VPN albo ogranicz do adresów Cloudflare.
- Zamień pkill na kanarka, żeby wiedzieć, czy koparka wraca.
- Zmień sekrety: hasło bazy, klucze API, dane SMTP, hasła administratorów, i wyczyść sesje.
Rotację sekretów zalecamy także wtedy, gdy audyt pokazuje infekcję historyczną. Nie wiadomo, co atakujący odczytał, zanim cron zaczął zabijać koparkę.
Jeśli na tym samym serwerze stoją strony WordPress, sprawdź też je. Objawy włamania od strony aplikacji opisaliśmy w artykule o tym, jak rozpoznać wirusa na stronie WordPress.
Kiedy zlecić to specjalistom?
Gdy na jednym VPS-ie działa kilka sklepów, a nikt nie wie, co publikuje Docker i dlaczego cron zabija procesy. W ASAPDevs zaczynamy od audytu hosta i kontenerów, potem zamykamy porty i sprzątamy. Tak wygląda nasze usuwanie malware i zabezpieczenie serwera WordPress. Jeśli wolisz, żeby ktoś stale pilnował aktualizacji i reguł zapory, zobacz opiekę nad serwerem w Pogotowiu IT.
