Malware, którego nie widać: jak „parasite SEO” po cichu przejął stronę klienta w Google

·

Malware, którego nie widać: jak „parasite SEO” po cichu przejął stronę klienta w Google

Większość właścicieli firm wyobraża sobie włamanie na stronę jako coś spektakularnego: podmieniona strona główna, komunikat hakera, wielki napis „HACKED”. W praktyce najgroźniejsze infekcje wyglądają zupełnie inaczej – strona działa normalnie, wygląda normalnie, a mimo to od tygodni pracuje przeciwko Tobie. Nie dla Ciebie, tylko dla kogoś, kto zarabia na Twojej reputacji w Google.

Taki właśnie przypadek trafił do mnie niedawno. To historia strony firmowej opartej na WordPressie, która padła ofiarą infekcji typu parasite SEO z całym zestawem ukrytych backdoorów. Opisujemy ją (z ukryciem danych klienta), bo świetnie pokazuje trzy rzeczy: dlaczego ten rodzaj ataku jest tak podstępny, dlaczego standardowe „wyczyść wirusa” wtyczką zwykle zawodzi, i co naprawdę trzeba zrobić, żeby się go pozbyć na dobre.

Co to jest „parasite SEO” i dlaczego powinno Cię to obchodzić

Zacznijmy od tego, na czym ten atak zarabia – bo to nie jest przypadkowy wandalizm. To biznes.

Przestępcy budują sieci spamerskich stron (najczęściej udających sklepy) i chcą, żeby te strony wysoko rankowały w Google. Problem w tym, że świeże, spamerskie domeny nie mają żadnego zaufania w wyszukiwarce. Rozwiązanie? Zamiast budować własną reputację, pasożytują na cudzej – włamują się na wiarygodne, istniejące od lat strony firmowe i podpinają się pod ich autorytet w Google. Stąd nazwa: pasożyt.

Na zainfekowanej stronie pojawiają się setki ukrytych podstron „produktów”, które Google widzi i indeksuje tak, jakby należały do Twojej firmy. Ruch z wyszukiwarki, który trafia na te podstrony, jest następnie przekierowywany na spamerski cel przestępców. Twoja domena, Twoja reputacja, Twoje pozycje – cudzy zarobek.

Do tego dochodzi mechanizm zwany cloakingiem (maskowaniem). Strona rozpoznaje, kto ją odwiedza:

  • Robotowi Google pokazuje starannie przygotowaną, zaindeksowaną treść „produktów”, żeby wszystko ładnie się rankowało.
  • Zwykłemu użytkownikowi przychodzącemu z wyników wyszukiwania serwuje przekierowanie na obcą stronę.
  • Tobie – właścicielowi – wchodzącemu bezpośrednio na stronę pokazuje… normalną, niezmienioną witrynę.

I tu jest sedno podstępności: właściciel zwykle niczego nie widzi. Wchodzisz na swoją stronę, wpisując adres ręcznie albo z zakładki – i wygląda dokładnie tak, jak powinna. Bo malware celowo omija ludzi, którzy wchodzą bezpośrednio. Problem widać dopiero „z zewnątrz”: w wynikach Google, w spadkach ruchu, w dziwnych adresach w Search Console, w mailu od klienta, który kliknął Twój link i wylądował na chińskim sklepie.

Dlaczego to jest realne zagrożenie dla firmy

Zignorowanie takiej infekcji kosztuje – i to na kilku poziomach naraz:

Utrata pozycji i ruchu w Google. Gdy Google zorientuje się, że Twoja domena serwuje spam i przekierowania (a zorientuje się), potrafi drastycznie obniżyć jej widoczność, a w skrajnych przypadkach nałożyć ręczną karę. Odbudowa pozycji po takim zdarzeniu to miesiące pracy.

Ostrzeżenia „ta strona może być niebezpieczna”. Jeśli infekcja przekierowuje na złośliwe treści, Twoja domena może trafić na listy ostrzeżeń (Google Safe Browsing). Przeglądarki zaczynają wtedy straszyć czerwonym ekranem każdego, kto próbuje wejść. Dla firmy, która sprzedaje albo buduje zaufanie online, to katastrofa wizerunkowa.

Utrata zaufania klientów. Klient, który z Twojego linku trafia na spam, nie myśli „pewnie ich zhakowano”. Myśli „coś jest z nimi nie tak”. Ten koszt trudno policzyć, ale jest realny.

Backdoory to znacznie więcej niż SEO. W tym konkretnym przypadku parasite SEO był tylko wierzchołkiem. Pod spodem siedział zestaw furtek dających napastnikowi pełną kontrolę: ukryte konto administratora, mechanizm wgrywania dowolnych plików i „wejście na skróty”, które logowało intruza jako administrator bez znajomości hasła. Innymi słowy – mieli klucze do całego domu, nie tylko do skrzynki na listy.

Sygnał alarmowy: trzy wtyczki bezpieczeństwa i nadal infekcja

Warto zatrzymać się na jednym fakcie, bo jest wymowny. Ta strona miała zainstalowane aż trzy popularne wtyczki zabezpieczające – firewall aplikacyjny, monitoring i dziennik aktywności. Mimo to była zainfekowana, a malware działał tygodniami.

To nie znaczy, że wtyczki bezpieczeństwa są bezużyteczne – są ważnym elementem obrony. To znaczy, że nie są srebrną kulą. Nowoczesne malware jest projektowane właśnie po to, żeby je omijać: ukrywa się pod nazwami udającymi legalne komponenty („WP Cache Optimizer”, „WP Compatibility Patch”), koduje swój kod, a nawet aktywnie ukrywa własne pliki i konta przed panelem administratora. Poleganie wyłącznie na wtyczce, która ma „przeskanować i wyczyścić”, to trochę jak liczenie na to, że alarm sam złapie włamywacza, który zna kod do alarmu.

Dlaczego zwykłe „usuwanie wirusa” nie działało

To jest część, którą znają wszyscy, którzy kiedykolwiek walczyli z uporczywą infekcją WordPressa: usuwasz zainfekowany plik, a on po chwili wraca. Kasujesz podejrzaną wtyczkę – odtwarza się. Zmieniasz nazwę katalogu – pojawia się nowy. I tak w kółko, jak w grze w „a kuku”.

W tym przypadku wyglądało to wyjątkowo niepokojąco: pliki odbudowywały się bez wchodzenia na stronę. Można było odnieść wrażenie, że na serwerze działa jakiś tajemniczy proces w tle (inny niż cron), który pilnuje infekcji. To właśnie ten fałszywy trop potrafi zablokować cały proces czyszczenia na długie dni.

Prawda okazała się inna – i to ona jest kluczem do zrozumienia takich infekcji.

Malware nie miał żadnego magicznego procesu w tle. Miał kilka wzajemnie odtwarzających się „ładowaczy” (loaderów), rozsianych w różnych, nieoczywistych miejscach strony:

  • jeden ukryty w folderze wtyczek „obowiązkowych” (tzw. must-use plugins, które WordPress ładuje zawsze i których nie da się wyłączyć klikiem w panelu),
  • drugi schowany w plikach motywu graficznego, udający zminifikowany plik CSS,
  • trzeci jako fałszywa wtyczka – na dodatek istniejąca w dwóch kopiach w różnych katalogach,
  • czwarty jako katalog z backdoorami, również zdublowany.

Każdy z tych loaderów potrafił odtworzyć pozostałe. Usunięcie jednego nic nie dawało – przy najbliższej okazji któryś z pozostałych odbudowywał całość. A „najbliższa okazja” pojawiała się bez przerwy, bo wystarczyło jakiekolwiek wejście na stronę. Nie Twoje – jakiekolwiek. Roboty indeksujące, skanery, automatyczne zadania WordPressa (tzw. WP-Cron uruchamiany przy ruchu) – internet puka do każdej strony setki razy dziennie. Każde takie puknięcie uruchamiało kod, a kod odbudowywał to, co przed chwilą skasowano.

Do tego doszedł drugi „korzeń” infekcji, ukryty w bazie danych: konto administratora niewidoczne w panelu. Malware dopisał sobie własnego admina i jednocześnie zainstalował filtr, który ukrywał to konto na liście użytkowników. Można było przeglądać użytkowników w kółko i go nie zobaczyć – istniał tylko w bazie danych. Co gorsza, to konto również potrafiło się odtwarzać: skasowane, wracało przy kolejnym uruchomieniu strony (widzieliśmy to na własne oczy – konto wróciło z nowym numerem ID).

Przełom: najpierw zamrozić, potem sprzątać

Skoro infekcja odbudowuje się przy każdym uruchomieniu kodu, wniosek jest prosty, choć nieoczywisty: nie da się jej wysprzątać, dopóki kod może się uruchamiać. To jak próba wytarcia podłogi pod cieknącym kranem – najpierw trzeba zakręcić wodę.

Dlatego zamiast gonić pojedyncze pliki, zaczęliśmy od zamrożenia wykonywania kodu na całej stronie. Jedną, precyzyjną regułą na poziomie serwera zablokowaliśmy uruchamianie jakichkolwiek skryptów PHP z zewnątrz – strona na czas operacji odpowiadała komunikatem „brak dostępu” na każde żądanie. Sprawdziliśmy też, czy nie ma ukrytego zadania cyklicznego (crona), które mogłoby uruchamiać malware poza ruchem WWW. Nie było.

Efekt był natychmiastowy i diagnostyczny zarazem: skasowaliśmy zainfekowany katalog i po raz pierwszy on nie wrócił. To był moment, w którym infekcja przestała mieć przewagę. Dopiero wtedy, przy „zakręconej wodzie”, usunęliśmy wszystkie loadery naraz – łącznie z ich zdublowanymi kopiami, które wcześniej pozwalały infekcji odradzać się jak hydra.

Reszta była już metodyczna:

  1. Usunięcie wszystkich loaderów jednocześnie (do kwarantanny, nie na stałe – żeby zachować materiał dowodowy).
  2. Pełne przeskanowanie strony pod kątem jakichkolwiek pozostałości odwołujących się do malware – aż wynik był zerowy.
  3. Wyczyszczenie bazy danych z ukrytego administratora i powiązanych wpisów (teraz, przy zamrożonym kodzie, konto nie mogło się już odtworzyć).
  4. Wymiana kluczy bezpieczeństwa WordPressa (co unieważnia wszystkie sesje – także ewentualną sesję intruza) i reset haseł wszystkich kont.
  5. Odmrożenie strony i test końcowy.

Ostatni test był najważniejszy: przy ponownie włączonym kodzie wymusiliśmy dokładnie ten mechanizm, który wcześniej odbudowywał infekcję – i tym razem nic nie wróciło. Strona wstała normalnie, a malware nie miał już z czego się odrodzić. To jest różnica między „wygląda, że czysto” a „udowodniono, że czysto”.

Czego ta historia uczy każdego właściciela strony

Nie trzeba być informatykiem, żeby wyciągnąć z tego przypadku konkretne, praktyczne wnioski.

„Strona wygląda dobrze” to nie to samo co „strona jest zdrowa”. Najgroźniejsze infekcje są niewidoczne dla właściciela z założenia. Warto regularnie sprawdzać, jak Twoja domena wygląda z zewnątrz: co pokazuje w wynikach Google (wpisz site:twojadomena.pl i przejrzyj, czy nie ma tam obcych podstron), czy Search Console nie zgłasza dziwnych adresów, czy nie ma nagłych spadków ruchu.

Aktualizacje to nie fanaberia, to podstawa. Większość takich włamań wchodzi przez lukę w nieaktualnej wtyczce lub motywie. Ślady w tej infekcji wskazywały, że weszła przez starszy, niezałatany komponent. Aktualny WordPress, wtyczki i motyw to najtańsze ubezpieczenie, jakie masz.

Wtyczka bezpieczeństwa to element obrony, a nie cała obrona. Trzy takie wtyczki nie zatrzymały tej infekcji. Traktuj je jak jeden z zamków w drzwiach, a nie jak gwarancję.

Kopie zapasowe muszą być regularne i odseparowane. Przy tak głębokiej infekcji czasem najszybszą i najpewniejszą drogą jest odtworzenie czystej wersji sprzed włamania. To działa tylko wtedy, gdy masz sprawną, aktualną kopię – przechowywaną poza samym serwerem.

Higiena kont i dostępów. Im mniej kont administratora, tym lepiej. Każde konto to potencjalne wejście. Silne, unikalne hasła i (tam, gdzie się da) logowanie dwuskładnikowe realnie podnoszą poprzeczkę.

Po incydencie trzeba zamknąć furtkę, nie tylko posprzątać pokój. Samo usunięcie plików nie wystarczy, jeśli napastnik ma nadal Twoje hasła (np. do FTP czy panelu hostingu) albo zainfekowane narzędzie na Twoim komputerze. Po każdym poważnym włamaniu trzeba zrotować wszystkie poświadczenia i sprawdzić stacje robocze, z których zarządza się stroną – w tym rozszerzenia przeglądarki, które bywają wektorem kradzieży dostępów.

Reakcja na incydent ma swoją kolejność. Zamrozić → usunąć wszystko naraz → wyczyścić bazę → zrotować dostępy → odmrozić → zweryfikować. Gonienie pojedynczych plików na żywej, działającej stronie to najczęstszy powód, dla którego czyszczenie ciągnie się tygodniami.

Podsumowanie

Parasite SEO to jeden z tych ataków, które są groźne właśnie dlatego, że są ciche. Nie niszczą strony – przejmują ją, żeby po cichu zarabiać na jej reputacji, a rachunek za to płaci właściciel: pozycjami w Google, ruchem, zaufaniem klientów i bezpieczeństwem danych. Dobra wiadomość jest taka, że nawet uporczywą, samoodbudowującą się infekcję da się usunąć na dobre – pod warunkiem, że rozumie się jej mechanizm i podchodzi do sprawy metodycznie, a nie po omacku.

Jeśli podejrzewasz, że z Twoją stroną dzieje się coś podobnego – dziwne podstrony w Google, przekierowania, spadki ruchu bez wyraźnej przyczyny – nie czekaj, aż zrobi się z tego kara od Google albo ostrzeżenie w przeglądarce. Im wcześniej, tym mniejszy koszt.

Dodatek techniczny (dla webmasterów i administratorów)

Poniższa część jest dla osób, które same ogarniają WordPressa i chcą wiedzieć, „jak to wyglądało pod maską”. Dane identyfikujące klienta zostały usunięte; wskaźniki infrastruktury napastnika (C2) zostawiamy jako IOC – mogą się przydać innym do wykrycia i blokady.

Mechanizm (łańcuch loaderów)

Infekcja opierała się na dołączaniu spakowanych payloadów (compress.zlib:// do plików .gz) przez kilka redundantnych loaderów:

  • mu-plugin (np. hostinger-auto-updates.php) – odszyfrowywał AES-em ścieżkę do payloadu ukrytego w motywie i dołączał go. Ładowany zawsze, niewyłączalny z panelu.
  • payload w motywie – plik udający CSS, np. wp-content/themes/<motyw>/assets/css/fusion-styles-min-<hash>.gz.
  • fałszywa wtyczka (wp-compat-cache, „WP Cache Optimizer” / „WP Compatibility Patch”) – z filtrem all_plugins ukrywającym ją z listy wtyczek; występowała w dwóch kopiach (m.in. w zagnieżdżonym wp-content/plugins/plugins/).
  • katalog backdoorów o nazwie 32-znakowego skrótu hex – kopie w wp-content/uploads/ oraz w katalogu tymczasowym systemu (/tmp/<SITE_ID>/), odbudowywane z serwera C2 przy każdym żądaniu, jeśli zniknęły.

Bootstrap w katalogu backdoorów przy każdym uruchomieniu sprawdzał obecność plików w /tmp/<SITE_ID>/ i, jeśli ich brakowało, dociągał je z C2. To tłumaczyło „odbudowę bez wchodzenia na stronę” – napędzał ją zwykły ruch botów i WP-Cron.

Backdoory dostępu:

  • ukryty administrator tworzony przez wtyczkę, z filtrem pre_user_query ukrywającym konto na liście użytkowników; ID zapamiętane w opcji _pre_user_id. Konto odtwarzane na hooku init.
  • web shell do zapisu/uploadu plików, aktywowany magicznym parametrem custom_key.
  • auto-login – wejście pod adres skryptu logowało intruza jako pierwszego administratora (ustawienie ciasteczka auth).
  • puller z GitHuba (sync.php) – zaciągał pliki z prywatnego repo na podstawie tokena podawanego przez… rozszerzenie przeglądarki. Potencjalny pierwotny wektor.

Wskaźniki infekcji (IOC)

Infrastruktura C2 (do blokady ruchu wychodzącego):

  • gateway.gvhfdahg.com
  • code-server.gvhfdahg.com
  • server.qrstabcd.com

Pliki / artefakty:

  • mu-plugins/hostinger-auto-updates.php (i jego kopie po zmianie nazwy)
  • wtyczka wp-compat-cache/ (pliki: wp-compat-cache.php, wp-compat-cache.gz, sync.php, compat-env-check.php, cache-purge-trigger.php)
  • payload w motywie: .../assets/css/fusion-styles-min-<hash>.gz
  • katalog o nazwie 32-znakowego skrótu hex w uploads/ oraz /tmp/<SITE_ID>/
  • podejrzane index.php o nietypowo dużym rozmiarze w wp-content/uploads/

Baza danych:

  • ukryty admin: login admin001, e-mail adminbackup@wordpress.org
  • opcja _pre_user_id w tabeli *_options

Sygnatury w kodzie: compress.zlib://, all_plugins (ukrywanie wtyczki), pre_user_query (ukrywanie usera), stała magiczna typu custom_key, cookie WORDPRESS_ADMIN_USER.

Szkic postępowania (defensywnie)

  1. Zamrożenie wykonywania PHP (reguła serwera na czas czyszczenia), np. blokada dostępu do plików .php:
<FilesMatch "\.php$">
  Require all denied
</FilesMatch>
  1. Wykluczenie crona systemowego: crontab -l (żeby upewnić się, że nic nie odpala payloadu z CLI).
  2. Test zamrożenia: usuń /tmp/<SITE_ID> i sprawdź po chwili, czy nie wrócił.
  3. Skan i usunięcie wszystkich loaderów naraz (do kwarantanny), a następnie kontrola do zera:
grep -rIl -e 'compress.zlib' -e '<domena-C2>' -e '<nazwa-loadera>' wp-content
find . -type d -regextype posix-extended -regex '.*/[0-9a-f]{10,}$'
grep -rnE 'auto_prepend_file' .htaccess .user.ini
  1. Baza: znajdź i usuń ukrytego admina po _pre_user_id, a następnie zweryfikuj listę administratorów:
SELECT * FROM wp_options WHERE option_name = '_pre_user_id';
-- usuń konto o wskazanym ID (users + usermeta) oraz opcję _pre_user_id
  1. Rotacja: wymiana kluczy (wp config shuffle-salts), reset haseł wszystkich kont, a poza WordPressem – zmiana haseł FTP/SSH i bazy danych oraz audyt stacji roboczych i rozszerzeń przeglądarki.
  2. Odmrożenie i test końcowy: przywróć .htaccess, wymuś WP-Cron/ruch i potwierdź, że nic się nie odbudowuje.

Uwaga: to szkic poglądowy, a nie gotowa instrukcja „kopiuj-wklej” dla każdego środowiska. Każdy hosting ma swoją specyfikę (ścieżki, uprawnienia, kontenery), a nieostrożne operacje na żywej stronie potrafią zaszkodzić bardziej niż sama infekcja. Jeśli nie masz pewności – skorzystaj z pomocy kogoś, kto robił to wcześniej.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *