Nie możesz wejść do panelu po włamaniu?Zgłoś włamanie
← Blog
Bezpieczeństwo

Zhakowany WordPress: 403 na wp-login, 404 na podstronach i webshelle w wp-includes

Przypadek z hostingu bez SSH: podmieniony .htaccess odciął właściciela od panelu, a kopie webshella siedziały w katalogach rdzenia WordPressa.

Jeśli po włamaniu WordPress zwraca 403 na wp-login, a podstrony 404, to prawie na pewno atakujący nadpisał główny plik .htaccess. Rozwiązanie: odtworzyć .htaccess z regułami WordPressa, usunąć webshelle (także te ukryte w katalogach rdzenia) i zablokować wykonywanie PHP tam, gdzie nie jest potrzebne. Poniżej opisujemy taki przypadek z hostingu cPanel/LiteSpeed, na którym mieliśmy tylko FTP, bez SSH.

Jakie objawy daje podmieniony .htaccess?

  • 403 na stronie logowania. Właściciel nie może wejść do panelu, mimo że zna hasło.
  • 404 na wszystkich podstronach. Działa tylko strona główna.
  • Nowe pliki index.php w dziwnych miejscach. Każdy z własnym .htaccess obok.

Te dwa błędy mają jedną przyczynę. Atakujący zastąpił główny .htaccess własnym: zablokował wykonanie każdego pliku *.php poza index.php i przy okazji usunął reguły rewrite WordPressa. Bez tych reguł serwer nie wie, że adres w rodzaju /kontakt/ ma obsłużyć index.php, więc szuka katalogu, którego nie ma, i zwraca 404. Plik wp-login.php wpadł pod blokadę, stąd 403. Właściciel, który w takiej sytuacji resetuje hasło albo wyłącza wtyczki przez FTP, traci czas, bo problem siedzi w jednym pliku konfiguracyjnym serwera.

Jak znaleźć webshell w WordPressie bez SSH?

Webshelle z funkcją zdalnego wykonywania poleceń i wgrywania plików leżały w katalogu głównym. To było łatwe. Trudniejsze były kopie tego samego shella zapisane jako index.php z plikiem .htaccess zawierającym „Allow from all” w katalogach, do których nikt nie zagląda:

  • wp-includes/blocks/...
  • wp-admin/css/colors/...
  • wp-content/themes/twentytwentyfour/patterns/

Nazwa nie jest przypadkowa. Reguła atakującego przepuszczała tylko pliki index.php, więc jego kopie działały dalej, a lokalny .htaccess z „Allow from all” znosił blokady z katalogów wyżej.

Najpewniejsza metoda wykrywania to porównanie listy plików z czystym rdzeniem tej samej wersji. Przy innym włamaniu znaleźliśmy w ten sposób 27 webshelli z jednego dnia, w tym menedżer plików, którego hasło było sumą MD5 pustego ciągu (czyli wchodziło się bez hasła), i fałszywe obrazki: nagłówek JPEG lub GIF, a dalej kod PHP. Bez SSH pobierasz pliki przez FTP i porównujesz lokalnie:

cd czysty-wordpress && find . -type f | sort > ../czysty.txt
cd ../pobrana-strona && find . -type f | sort > ../strona.txt
comm -23 ../strona.txt ../czysty.txt   # pliki, których nie ma w oryginale

Wynik obejmie też wtyczki, motywy i uploads, ale w wp-admin i wp-includes każdy dodatkowy plik jest podejrzany. Samo wp core verify-checksums nie wystarczy: sprawdza zmienione pliki rdzenia, a nowych plików w podkatalogach nie wykrywa.

Którędy atakujący dostał się do strony?

Definitywnie tego nie ustaliliśmy. Access logi na tym hostingu były symlinkiem do katalogu domlogs i nie dało się ich pobrać przez FTP. Zostały statystyki awstats z panelu:

  • 38 tys. żądań do xmlrpc.php i 8 tys. do wp-login.php w ciągu miesiąca, czyli zgadywanie haseł,
  • 15 wywołań plugin-editor.php miesiąc wcześniej.

Najbardziej prawdopodobny scenariusz to złamane hasło administratora, a potem wbudowany edytor plików w panelu, którym da się zapisać dowolny kod PHP w pliku wtyczki lub motywu bez żadnego dodatkowego narzędzia. Bez pełnych logów to jednak hipoteza, dlatego zablokowaliśmy zarówno xmlrpc, jak i edytor plików.

Masz ten sam problem? Usuwamy webshelle także wtedy, gdy hosting daje tylko FTP i panel. Opisz sytuację →

Jak naprawić .htaccess po włamaniu do WordPressa?

Zaczęliśmy od nowego .htaccess w katalogu głównym: standardowe reguły rewrite WordPressa plus linia z handlerem PHP, którą hosting wstawia dla wybranej wersji PHP (najlepiej skopiować ją z panelu albo z kopii sprzed włamania). Do tego blokady:

Co blokujemyPo co
xmlrpc.phpgłówny kanał zgadywania haseł w tym przypadku
pliki wrażliwe i ukryte (z kropką na początku nazwy)wyciek haseł do bazy
PHP w wp-includestam nie ma czego wywoływać bezpośrednio
PHP w uploads (osobny .htaccess)uploads to ulubione miejsce na shelle
zapytania ?author=enumeracja loginów użytkowników
Options -Indexeslisting katalogów

Doszły nagłówki HSTS, X-Frame-Options i X-Content-Type-Options: nosniff. Blokada PHP w uploads wygląda tak (Apache 2.4 i LiteSpeed):

# wp-content/uploads/.htaccess
<FilesMatch "\.(php|phtml|phar)$">
  Require all denied
</FilesMatch>

W samym WordPressie: define('DISALLOW_FILE_EDIT', true); w wp-config.php (wyłącza edytor, którym prawdopodobnie wszedł atakujący), nowe klucze salts, które wylogowują wszystkie sesje, i usunięcie wtyczki wp-file-manager. Menedżer plików w panelu WordPressa to gotowe narzędzie dla każdego, kto przejmie konto admina.

Co jeszcze znaleźliśmy po sprzątaniu?

Publiczne kopie zapasowe. Archiwum Duplicatora (2,6 GB), plik .wpress i wp-config.php.bk leżały w katalogu strony i zwracały HTTP 200, czyli każdy mógł je pobrać razem z hasłem do bazy. Przenieśliśmy je poza katalog publiczny.

Na koniec uruchomiliśmy monitoring integralności plików (FIM) z crona w cPanelu co 30 minut, bo brak SSH nie oznacza braku crona. Bazą odniesienia jest 42 814 plików. Każdy nowy lub zmieniony plik wychodzi w raporcie, więc kolejny webshell nie przeleży na serwerze miesięcy, zanim ktoś go zauważy.

Kilka stron WordPress na jednym hostingu: czy infekcja się przenosi?

Tak. Na jednym koncie hostingowym wszystkie strony działają jako ten sam użytkownik systemowy, więc webshell wgrany do jednej instalacji może czytać i zapisywać pliki wszystkich pozostałych, łącznie z ich wp-config.php i hasłami do baz.

W innym przypadku na jednym koncie było ok. 41 stron WordPress. Ukryte konto administratora o loginie wordpress było aktywne na 11 z nich, webshell siedział w uploads/2023/index.php, a atakujący na bieżąco wykorzystywał lukę w dodatku do motywu, która pozwalała wgrywać pliki bez logowania. Efekt: pliki .yo-<hex>.php w uploads. Zrobiliśmy wtedy:

  1. mu-plugin, który odrzuca anonimowe żądania do admin-ajax zawierające kod PHP lub plik .php (403 i wpis w logu),
  2. zakaz wykonywania PHP w uploads na wszystkich stronach,
  3. usunięcie ukrytego konta na 11 stronach,
  4. monitoring co godzinę: lista administratorów według uprawnień (capabilities, nie samej roli), pliki PHP w uploads i sumy kontrolne rdzenia.

Uwaga: czyszczenie jednej strony na współdzielonym koncie nic nie daje. Atakujący wraca z sąsiedniej instalacji.

Kiedy zlecić usunięcie wirusa ze strony?

Gdy nie masz dostępu do panelu, gdy hosting nie daje SSH albo gdy na koncie stoi kilka stron. Wtedy ręczne kasowanie widocznych plików zwykle kończy się powrotem infekcji po kilku dniach. Ogólny przebieg sprzątania opisaliśmy w artykule wirus na stronie WordPress, a tu jest nasza usługa usuwania malware z WordPressa. ASAPDevs zaczyna od diagnozy i zabezpieczenia śladów, a dopiero potem kasuje.

Źródła

Kamil Perzowski
Kamil Perzowski
CEO i co-founder ASAPDevs. Programuje i utrzymuje sklepy, aplikacje webowe i serwery klientów. LinkedIn · Zgłoś włamanie
Masz ten sam problem?

Nie możesz wejść do panelu po włamaniu?

Kamil Perzowski, ASAPDevs
Kamil Perzowski
CEO & Co-founder, ASAPDevs
Usuwamy webshelle i malware z WordPressa, także na hostingu bez SSH, i zabezpieczamy stronę przed powrotem atakującego. Pilne? Zadzwoń: +48 884 220 764.
★★★★★
Rekomenduję. Jestem zadowolona ze współpracy. Firma jest terminowa, rzetelnie wywiązuje się ze zobowiązań. Pełen profesjonalizm i rzadka cecha u informatyków – empatia, życzliwe i cierpliwe wsparcie.
Beata M., opinia w Google
  1. Czytam zgłoszenie i odpisuję z pytaniami albo propozycją terminu rozmowy.
  2. Krótka rozmowa o zakresie, dostępach i terminie.
  3. Dostajesz plan i wycenę. Decydujesz, bez zobowiązań.

Wolisz od razu porozmawiać? +48 884 220 764 · [email protected]

Wysyłając formularz, przekazujesz nam dane, żebyśmy mogli odpowiedzieć. Szczegóły w polityce prywatności.

FAQ

Pytania i odpowiedzi

Dlaczego po włamaniu nie mogę zalogować się do WordPressa (błąd 403)?

+
Najczęściej atakujący podmienił plik .htaccess w katalogu głównym. W opisanym przypadku nowa reguła blokowała wykonanie każdego pliku PHP poza index.php, więc wp-login.php zwracał 403. Hasło nie ma tu znaczenia, trzeba naprawić .htaccess przez FTP lub menedżer plików hostingu.

Czy da się usunąć webshell z WordPressa bez dostępu SSH?

+
Tak. Wystarczy FTP i panel hostingu: pobierasz pliki, porównujesz je z czystą paczką WordPressa tej samej wersji, usuwasz obce pliki i podmieniasz .htaccess. Monitoring zmian w plikach da się uruchomić z crona w cPanelu.

Czy wp core verify-checksums wykryje webshell?

+
Tylko częściowo. Polecenie wskaże zmienione pliki rdzenia, ale nie wykrywa nowych plików dodanych w podkatalogach. Webshell zapisany jako index.php w katalogu z blokami albo stylami panelu może przejść niezauważony.

Mam kilka stron na jednym hostingu. Czy wystarczy wyczyścić tę zainfekowaną?

+
Nie. Na jednym koncie hostingowym wszystkie strony działają jako ten sam użytkownik systemowy, więc infekcja jednej daje dostęp do wszystkich. Sprawdzić trzeba każdą instalację: pliki, konta administratorów i katalogi uploads.

Czy kopie zapasowe w katalogu strony są bezpieczne?

+
Nie. W jednym z naszych przypadków archiwum Duplicatora (2,6 GB), plik .wpress i kopia wp-config.php.bk były dostępne publicznie z kodem HTTP 200. Kopie trzymaj poza katalogiem publicznym albo poza serwerem.

Przeczytaj też