Dwa serwery Proxmox utrzymywały skrajnie wysokie obciążenie, chociaż żadna z maszyn wirtualnych nie zużywała odpowiadających mu zasobów. Standardowe narzędzia diagnostyczne nie pokazywały źródła problemu, ponieważ ich wyniki były filtrowane przez rootkita. Analiza ujawniła koparkę kryptowalut, mechanizmy automatycznego ponownego infekowania, dodatkowy dostęp administracyjny oraz celowo wyłączone logi. Poniżej opisujemy, jak rozpoznaliśmy kompromitację, zabezpieczyliśmy dane i odcięliśmy dostęp atakującemu.

Wysokie obciążenie, którego nie generowały maszyny wirtualne

Zgłoszenie początkowo wyglądało na rutynowy problem z wydajnością. Dwa hosty Proxmox utrzymywały load average na poziomie około 33, ale podgląd zasobów w UI Proxmox nie wskazywał żadnej maszyny wirtualnej, która mogłaby generować takie obciążenie. Coś tu się nie zgadzało - obciążenie niby jest, ale źródła brak?

Pierwsza próba diagnostyki z poziomu powłoki przyniosła kolejny niepokojący sygnał. Podstawowe polecenia używane do kontroli procesów i zalogowanych użytkowników (top, who, w) zwracały błędy, jakby nie były zainstalowane. Na produkcyjnym hoście Proxmox, tuż po uruchomieniu systemu, nie był to stan, który można było uznać za przypadkowy.

Na tym etapie były to jednak jedynie dwa luźne fakty, a nie obraz całości. Mogła to być awaria pakietów, problem z macierzą, przeciążenie operacjami I/O albo zawieszone zasoby sieciowe. Diagnostykę rozpoczęliśmy więc od najbardziej prawdopodobnych przyczyn, zanim przyjęliśmy scenariusz włamania.

Dlaczego to nie była zwykła awaria

Diagnostykę prowadziliśmy od najbardziej prawdopodobnych przyczyn ku najmniej. Kolejno odpadały: obciążenie dyskowe (parametry I/O nie potwierdzały przeciążenia), problemy z macierzą, zawieszone montowania sieciowe (backup w OVH po NFS), wreszcie hipoteza uszkodzonych pakietów. Ta ostatnia wydawała się najbardziej obiecująca - brak top to klasyczny objaw uszkodzonego pakietu procps. Sprawdziliśmy wprost, reinstalując pakiet. Polecenie wróciło. Jednak obraz systemu nadal się nie zgadzał.

Obciążenie wyglądało na sztucznie zamrożone i nie zmieniało się tak, jak powinno przy zmiennym ruchu generowanym przez sklep na magento2. Część procesów oraz połączeń sieciowych pozostawała niewidoczna, mimo że hosty ewidentnie wykonywały dodatkową pracę. Najbardziej znacząca była zbieżność czasowa: dwa niezależnie skonfigurowane serwery (konfigurowane osobno, działające niezależnie, zupełnie ze sobą nie związane) zaczęły zachowywać się nieprawidłowo w odstępie zaledwie kilku minut.

Zestaw tych objawów nie pasował już do typowej usterki. Od tego momentu standardowe wyniki diagnostyczne należało traktować jako potencjalnie zafałszowane, a główną hipotezą stała się kompromitacja hostów - czyli po prostu włamanie na serwery.

Koparka ukryta pod nazwami usług Proxmox

Analiza przeprowadzona z pominięciem standardowego kontekstu systemowego ujawniła proces XMRig wydobywający Monero i łączący się z zewnętrzną pulą. To on odpowiadał za wysokie wykorzystanie procesora. Nie działał jednak pod przypadkową nazwą. Atakujący ukrył go w zestawie usług systemd udających legalne elementy środowiska Proxmox.

Binarkę koparki umieszczono jako /var/lib/systemd/PVE-1, z launcherem /var/lib/systemd/ld-svc. Uruchamiała ją usługa PVE-1.service z opisem "Proxmox VE node health monitor". Daty modyfikacji plików cofnięto do roku 2018, aby przy pobieżnej inspekcji uchodziły za oryginalne. Trwałość zapewniał taki zestaw plików:

/etc/systemd/system/PVE-1.service          # koparka, Restart=always
/etc/systemd/system/PVE-1.timer            # strażnik restartu co ~3 min
/etc/systemd/system/PVE-1-maintain.service
/etc/systemd/system/PVE-1-update.timer     # codzienny auto-updater XMRiga
/etc/systemd/system/PVE-1-update.service

Poziom dopracowania mówił wiele. Jeden z timerów pilnował ponownego uruchamiania koparki, a osobny mechanizm regularnie pobierał jej nowszą wersję, sprawdzał sumę SHA-256 i podmieniał plik. Nie był to pojedynczy skrypt uruchomiony jednorazowo, lecz samoodtwarzająca się instalacja przygotowana do długotrwałego działania, naprawiania się i wykorzystywania zasobów serwera. Na jednym z hostów wykryliśmy również narzędzie monetyzujące łącze oraz komponent utrzymujący kontakt z serwerem sterującym. Zasoby klienta były więc wykorzystywane na kilka sposobów równocześnie.

Rootkit, który ukrywał procesy, pliki i połączenia

Pozostawało wyjaśnić mechanizm, dzięki któremu narzędzia diagnostyczne wcześniej wprowadzały w błąd. Odpowiadała za to biblioteka /var/lib/systemd/.hide/libhide.so ładowana za pomocą mechanizmu LD_PRELOAD. Wpięto ją w dwóch niezależnych miejscach, tak by aktywowała się przy każdym logowaniu i wpływała na polecenia wykonywane również w sesjach administracyjnych:

/etc/environment                  # export LD_PRELOAD=...
/etc/profile.d/sysenv.sh

Biblioteka przechwytywała wywołania systemowe i filtrowała ich wyniki: ukrywała proces koparki przed ps, jej port 20004 przed ss i netstat, a powiązane pliki przed ls i find - również w sesji roota. To tłumaczyło każdą wcześniejszą anomalię naraz: pozorny brak top, brak właściciela przy podejrzanym połączeniu w ss, zamrożony load.

Weryfikacja polegała na odebraniu narzędziom sfałszowanego kontekstu - uruchomieniu ich z wyczyszczonym LD_PRELOAD:

env -u LD_PRELOAD ps aux | grep -iE 'xmrig|PVE-1|ld-svc'
env -u LD_PRELOAD ss -tulpn | grep ':20004'
env -u LD_PRELOAD find /var/lib/systemd -type f

Dopiero wtedy ukryte procesy, porty i pliki stały się widoczne. Był to kluczowy moment analizy i pierwszy wniosek:

Na skompromitowanym hoście typowe polecenia, jak ls, ps, find i ss nie są źródłem prawdy, nawet uruchamiane jako root. Każdy odczyt stanu systemu trzeba było odtąd traktować jako podejrzany, dopóki nie potwierdzimy go z pominięciem preload.

Dodatkowy dostęp pozostawiony w konfiguracji SSH

Na jednym z hostów koparka nie była jedynym mechanizmem zapewniającym trwałość infekcji. W konfiguracji SSH znajdował się niezależny kanał dostępu, który pozostałby aktywny nawet po usunięciu koparki.

Oprócz dodatkowego wpisu w standardowym pliku authorized_keys, atakujący wykorzystał dyrektywę AuthorizedKeysCommand umieszczoną na końcu /etc/ssh/sshd_config:

Match User root
    AuthorizedKeysCommand /etc/ssh/.sshd-keys-cache
    AuthorizedKeysCommandUser root
    PubkeyAuthentication yes

Wskazany skrypt /etc/ssh/.sshd-keys-cache zwracał zawartość ukrytego /etc/ssh/.cache/root.keys, gdzie znajdował się klucz ed25519 należący do atakującego. Aktywność konfiguracji potwierdziliśmy na podstawie ustawień faktycznie używanych przez działający proces sshd, a nie tylko przez odczyt pliku konfiguracyjnego.

Dalsza kontrola ujawniła podmieniony moduł uwierzytelniania Proxmox, dodatkowe konto administracyjne oraz token API roota z wyłączoną separacją uprawnień. Działał też mechanizm cyklicznego czyszczenia historii poleceń. Te elementy wskazywały, że przynajmniej na jednym hoście atakujący wykonywał działania ręcznie i pozostawił sobie kilka niezależnych dróg powrotu.

Restart zamienił incydent w awarię

Na drugim serwerze naruszona została również spójność obszaru rozruchowego systemu. Dopóki host działał, problem pozostawał niewidoczny. Klient, samodzielnie próbując przywrócić prawidłowe działanie maszyny, wykonał restart. System nie uruchomił się ponownie i zatrzymał się w awaryjnej konsoli GRUB.

Odzyskanie serwera wymagało uruchomienia trybu ratunkowego u dostawcy, analizy partycji rozruchowej i odtworzenia poprawnej konfiguracji startowej. Ten przypadek pokazuje, dlaczego serwera podejrzanego o kompromitację nie należy restartować odruchowo. Atakujący często przygotowują się na taką ewentualność i albo niszczą dowody stosując taktykę spalonej ziemi, albo zacierają po sobie ślady - mimo to zostawiając backdoory.

Zabezpieczenie danych i śladów

Przed rozpoczęciem usuwania infekcji zabezpieczyliśmy materiał potrzebny do dalszej analizy i odtworzenia środowiska. Każda zmiana wykonywana na przejętym hoście może nadpisać ślady, dlatego remediacja nie powinna poprzedzać zebrania dowodów.

Atakujący aktywnie ograniczał możliwość rekonstrukcji zdarzeń. Pliki auth.log, wtmp, btmp, lastlog oraz secure zastąpiono dowiązaniami do /dev/null, a journald skonfigurowano z opcją Storage=none. Daty modyfikacji wielu plików zostały zmienione. Przy ustalaniu chronologii pomocny okazał się czas utworzenia plików, trudniejszy do podrobienia niż standardowy mtime:

stat -c '%w %n' /var/lib/systemd/PVE-1

Z trybu ratunkowego wykonaliśmy obrazy kluczowych partycji, zabezpieczyliśmy konfigurację klastra Proxmox, konfigurację hostów oraz obrazy dysków maszyn wirtualnych. Kopie przenieśliśmy do S3 i zweryfikowaliśmy ich integralność. Dopiero po zabezpieczeniu danych rozpoczęliśmy neutralizowanie mechanizmów dostępu.

Opanowanie incydentu i przywrócenie usług

Działania przeprowadziliśmy w kolejności ograniczającej ryzyko ponownego przejęcia hostów. Najpierw usunęliśmy dodatkowy dostęp przez SSH oraz nieznane konta i tokeny. Następnie wykonaliśmy rotację kluczy, wyłączyliśmy mechanizm LD_PRELOAD, zatrzymaliśmy jednostki odpowiedzialne za koparkę i usunęliśmy powiązane pliki. Ruch do wykrytej puli wydobywczej oraz serwerów sterujących został zablokowany.

Podmienione lub uszkodzone składniki systemu odtworzyliśmy z pakietów dystrybucyjnych. Przywróciliśmy również poprawną konfigurację DNS, plików hosts i mechanizmów logowania. Dostęp do panelu administracyjnego ograniczyliśmy do zaufanych źródeł, a na serwerze z uszkodzonym rozruchem naprawiliśmy konfigurację startową i przywróciliśmy wymagane usługi Proxmox.

Najbardziej prawdopodobna droga wejścia

Nie udało się jednoznacznie ustalić, w jaki sposób atakujący uzyskał pierwszy dostęp. Tu natrafiliśmy na celowe zacieranie śladów: logi panelu Proxmoxa (/var/log/pveproxy/access.log) wyczyszczono dokładnie dla okna czasowego, w którym doszło do kompromitacji. Bezpośredni dowód wektora wejścia został więc świadomie usunięty - i tego konkretnego elementu nie udało się odtworzyć. Podobnie jak poniższych plików:

lrwxrwxrwx 1 root root 9 Sep 8 21:39 auth.log -> /dev/null
lrwxrwxrwx 1 root root 9 Sep 8 21:39 btmp -> /dev/null
lrwxrwxrwx 1 root root 9 Sep 8 21:39 faillog -> /dev/null
lrwxrwxrwx 1 root root 9 Sep 8 21:39 lastlog -> /dev/null
lrwxrwxrwx 1 root root 9 Sep 8 21:39 secure -> /dev/null
lrwxrwxrwx 1 root root 9 Sep 8 21:39 tallylog -> /dev/null
lrwxrwxrwx 1 root root 9 Sep 8 21:39 wtmp -> /dev/null

Rekonstrukcję oparliśmy zatem na tym, co było wspólne dla wszystkich dotkniętych maszyn. Mimo drobnych różnic w szczegółach łączył je jeden element: publicznie dostępny panel Proxmox VE na porcie 8006. Ta konfiguracja tworzyła szeroką powierzchnię ataku i jest najbardziej prawdopodobnym punktem wejścia, ale na podstawie zachowanych danych nie można wskazać konkretnej podatności ani uznać tego scenariusza za definitywnie potwierdzony.

Niemal równoczesna infekcja obu serwerów w bardzo podobny sposób przemawia natomiast za kampanią automatyczną, a nie atakiem przygotowanym wyłącznie przeciwko jednemu klientowi. Publicznie dostępne i nieaktualne interfejsy administracyjne są regularnie wyszukiwane przez skanery, dlatego ich ochrona nie może opierać się wyłącznie na ekranie logowania.

Wskaźniki kompromitacji (IOC)

Poniżej zebrałem wskaźniki z tego incydentu. Jeśli administrujesz hostem Proxmox i podejrzewasz podobną infekcję, są to konkretne artefakty, których warto szukać - po nazwach plików, portach, domenach pul wydobywczych oraz adresie portfela. Kampania trafiła w kilka hostów, stąd dwa warianty puli i różne porty.

Pliki i katalogi

/var/lib/systemd/PVE-1 # binarka XMRig
/var/lib/systemd/ld-svc # launcher
/var/lib/systemd/.hide/libhide.so # rootkit LD_PRELOAD
/var/lib/systemd/.cache/ # pliki stanu
/etc/systemd/system/PVE-1.service
/etc/systemd/system/PVE-1.timer
/etc/systemd/system/PVE-1-maintain.service
/etc/systemd/system/PVE-1-update.service
/etc/systemd/system/PVE-1-update.timer
/etc/profile.d/sysenv.sh # wpięcie LD_PRELOAD
/etc/environment # wpięcie LD_PRELOAD
/etc/ssh/.sshd-keys-cache # skrypt backdoora SSH
/etc/ssh/.cache/root.keys # klucz ed25519 atakującego
/usr/share/perl5/PVE/AccessControl.pm # podmieniony moduł uwierzytelniania PVE

Nazwy komponentów

install_and_mine.sh # skrypt instalacyjny
komari-agent # agent C2
tm.sh / TraffMonetizer # monetyzacja łącza

Sieć - pule wydobywcze i C2

gulf.moneroocean.stream:20004 # pula Monero (wariant 1)
hello.itsmybirthdaytoday.pw:53535 # pula Monero (wariant 2)
tz.5671234.xyz # serwer sterujący (C2)

Portfel Monero

46tqTcQnmZGTaqtknE28x86wgKfQCWYrmYgAtGEqEmnKLzTt9kHzChijdRFKt77jVggvQjLz3FSmfD6TepMuPwLUHCVAnSo
# pass: dytgl

Co warto zmienić zanim pojawi się podobny incydent

  • Nie udostępniaj panelu Proxmox VE ani SSH publicznie - ogranicz dostęp za pomocą VPN, firewalla lub po prostu listy zaufanych adresów IP.
  • Utrzymuj wspierane wersje Proxmox VE i systemu operacyjnego oraz regularnie instaluj poprawki bezpieczeństwa.
  • Włącz uwierzytelnianie wieloskładnikowe (2FA) i okresowo kontroluj konta administracyjne, klucze SSH oraz tokeny API.
  • Monitoruj obciążenie, połączenia wychodzące, zmiany w usługach systemd i modyfikacje konfiguracji.
  • Przechowuj sprawdzone kopie zapasowe poza hostem i regularnie testuj możliwość ich odtworzenia.
  • Przy podejrzeniu włamania - nie restartuj serwera bez wcześniejszej oceny sytuacji i zabezpieczenia danych, które możesz bezpowrotnie stracić.
  • Po przejęciu uprawnień roota odbuduj host z zaufanego źródła i zmień wszystkie hasła i klucze, do których atakujący mógł uzyskać dostęp.

Nietypowe obciążenie może być pierwszym sygnałem włamania

Wysokie użycie procesora bez widocznego procesu, znikające polecenia, nieznane konta lub podejrzane połączenia sieciowe nie muszą oznaczać zwykłej awarii. Jeżeli standardowe narzędzia pokazują obraz sprzeczny z zachowaniem hosta, trzeba brać pod uwagę, że sam system może ukrywać obecność intruza.

Pomagam w audytach bezpieczeństwa, analizach powłamaniowych i odzyskiwaniu kontroli nad serwerami Proxmox oraz Linux. Jeśli Twój serwer zachowuje się podobnie, skontaktuj się ze mną przed jego restartem.

Ze względu na poufność nazwy hostów oraz dane pozwalające zidentyfikować infrastrukturę klienta zostały zmienione. Przebieg incydentu, wykryte mechanizmy i opisane działania naprawcze odpowiadają rzeczywistemu zdarzeniu.

Podobne wpisy