Od jakiegoś czasu w Google Analytics widziałem regularne wejścia na stronę „nie znaleziono”. Nie jakieś tam pojedyncze przypadki — powtarzający się wzorzec. Zainstalowałem więc wtyczkę logującą błędy 404 i po czterech dniach miałem 1231 wpisów.
Jedną z pierwszych rzeczy, jakie rzuciły mi się w oczy, to był adres /admin. Wyprzedzał w statystykach nawet zwykłe, nieistniejące linki do starych zdjęć z wpisów. Problem w tym, że na highlab.pl nigdy nie było panelu pod tym adresem — panel WordPressa siedzi tam, gdzie zawsze, czyli pod /wp-admin/.
Ten wpis specjalnie umieściłem pod adresem /admin. Trochę na złość botom ale też w celach edukacyjnych.
Z tego artykułu dowiesz się:
/adminbył najczęściej odpytywanym, nieistniejącym adresem na blogu — ale nie w postaci rozłożonego w czasie skanowania, tylko jednego 19-minutowego epizodu./wp-login.phpi/wp-admin/nigdy nie trafiają do logu 404, bo naprawdę istnieją — trzeba ich szukać w surowych logach serwera.xmlrpc.phpdostał realny, świeży atak: 60 żądań z jednego adresu w niecałe 11 minut, podszywających się pod Jetpacka.- To samo zjawisko, tylko w większej skali, potwierdzają niezależne badania (SANS ISC, Stony Brook University).
Co właściwie siedzi w tych 1231 requestach
Zanim zacząłem doszukiwać się ataków, sprawdziłem, ile z tego to „szum” — nieistniejące obrazki po latach zmian w treściach, ikonki, których szuka przeglądarka czy boty wyszukiwarek robiące swoje. Rozkład dzienny wyglądał tak (ostatni dzień to tylko wycinek do godziny eksportu):
| Adres | Żądania |
|---|---|
| /admin | 69 |
| /.well-known/traffic-advice | 48 |
| /apple-touch-icon.png | 16 |
| /wp-content/uploads/…/xiaomi-redmi-note-9-pro…jpg | 16 |
| /apple-touch-icon-precomposed.png | 15 |
| /wp-content/uploads/…/xiaomi-redmi-note-11…jpg | 15 |
| /wp-content/uploads/…/xiaomi-redmi-note-10-pro…jpg | 15 |
| /wp-content/uploads/…/xiaomi-redmi-note-10s…jpg | 15 |
| /wp-content/uploads/…/xiaomi-redmi-note-9…jpg | 14 |
| /wp-content/uploads/2019/05/cropped-image-32×32.png | 12 |
Większość tej listy to pozostałości po starych wpisach na blogu: usunięte zdjęcia oraz favicony, których szuka Safari na iPhonie (apple-touch-icon), oraz /.well-known/traffic-advice — to nie żaden atak, tylko standard sprawdzany przez przeglądarki wspierające Private Relay/prefetch proxy. Tylko jeden wpis na tej liście nie ma żadnego wytłumaczenia „to normalne”: /admin.
Z 1231 żądań 404 udało mi się rozpoznać po adresie User-Agent 98 wejść (ok. 8%) od znanych botów:
- Bingbot (44),
- Googlebot (33),
- YandexBot (17),
- Applebot i Facebook External Hit (po 2).
To ruch, który da się nazwać „nieszkodliwym” z dużą pewnością — te firmy podają się za to, kim naprawdę są. Cała reszta to mieszanka botów udających przeglądarki i tego, co opisuję niżej.
/adminsamo w sobie nie jest luką bezpieczeństwa. Jest po prostu jednym z najbardziej przewidywalnych adresów, jakie automat może sprawdzić bez żadnej wcześniejszej wiedzy o mojej stronie (ani innych).
69 żądań /admin w 19 minut
Kiedy rozłożyłem te 69 wejść na /admin na osi czasu, to okazało się, że to nie rozkłada się równomiernie — tak jak się tego spodziewałem po przeczytaniu typowych opisów tego zjawiska. To był jeden, skoncentrowany skan: 23 września, między 18:51 a 19:10.
18:51:xx POST /admin ref: https://highlab.pl/wp-admin/ 18:52:xx POST /admin ref: https://highlab.pl/wp-admin/ 18:52:xx POST /admin ref: https://highlab.pl/wp-admin/ …65 kolejnych wpisów, ten sam wzorzec… 19:10:xx POST /admin ref: https://highlab.pl/wp-admin/
Po dokładniejszej analizie okazało się, że:
- Metoda POST, nie GET. To nie jest „sprawdzenie, czy strona istnieje” — to próba wysłania danych pod ten adres.
- Identyczny referrer za każdym razem —
https://highlab.pl/wp-admin/, czyli nagłówek sugerujący, że żądanie „przyszło” z panelu WordPressa. Nagłówek Referer wysyła klient, więc równie dobrze może być spreparowany. - Ubogi zestaw User-Agentów — 69 żądań, ale tylko 10 różnych wartości, wszystkie to wersje Chrome/Firefox/Safari z okolic 2021 roku. Prawdziwa przeglądarka nie zmienia swojej tożsamości co request.
Nie mam dowodu, że ktoś logował się do panelu, ani że doszło do włamania. Mogę tylko powiedzieć, że wzorzec (skondensowany czas, powtarzalna metoda, ten sam referrer, wąska pula fałszywych przeglądarek) jest charakterystyczny dla jakiegoś automatycznego skanu, a nie dla organicznego ruchu użytkownika klikającego w linki.
Innymi słowy: gdybym patrzył tylko na sumaryczną liczbę „69 żądań do /admin w cztery dni”, opowiedziałbym sobie historię o ciągłym, rozproszonym skanowaniu z całego internetu. Rozbicie na oś czasu pokazuje coś innego — jeden, krótki epizod, bardziej podobny do uruchomienia konkretnego narzędzia niż do tła, które leci bez przerwy.
A co z prawdziwymi ścieżkami WordPressa?
Tu jest niuans, którego nie było widać w samym logu 404 od wtyczki: /wp-admin/ i /wp-login.php naprawdę istnieją na moim serwerze, więc nigdy nie trafiają do logu błędów — WordPress odpowiada im poprawnym kodem 200 albo przekierowaniem, nie 404. Żeby je zobaczyć, musiałem zajrzeć do surowych logów serwera (access.log) z ostatnich dwóch dni.
| Adres | Metoda | Żądania | Unikalne IP |
|---|---|---|---|
| /wp-admin/admin-ajax.php | POST/GET | 163 | 2 |
| /xmlrpc.php | POST | 79 | 4 |
| /wp-admin/ | GET | 4 | 1 |
| /wp-login.php | POST | 2 | 2 |
admin-ajax.php to fałszywy trop — te 163 żądania z zaledwie 2 adresów IP to najpewniej zwykły ruch WordPressa (heartbeat, licznik komentarzy) oraz mój własny, gdy przeglądam stronę zalogowany. Nie każde trafienie w coś z /wp-admin/ w nazwie to atak.
Jednakże /xmlrpc.php, to już konkretny przykład tego, o czym mówię w tym artykule. 60 z 79 żądań przyszło z jednego adresu IP (z Indii, wg pola geo w logu), w ciągu niecałych 11 minut — od 09:04:56 do 09:15:25. Każde żądanie identyfikowało się jako Jetpack/WordPress.com, ale za każdym razem z innym wariantem wersji i losową, nieistniejącą domeną w nagłówku:
09:04:56 POST /xmlrpc.php 200 UA: Jetpack by WordPress.com (Jetpack 13.0; WordPress 6.3) 09:05:06 POST /xmlrpc.php 200 UA: WordPress.com; https://wordpress.com 09:05:17 POST /xmlrpc.php 200 UA: Jetpack/13.0; WordPress/6.3; http://site27117189.com 09:05:27 POST /xmlrpc.php 200 UA: WordPress.com; https://wordpress.com 09:05:37 POST /xmlrpc.php 200 UA: Jetpack/12.0; WordPress/6.4; http://site59128473.com …w sumie 60 żądań, 32 różne warianty UA, odstępy ~10 sekund…
To nie jest hipotetyczne zagrożenie z listy w poradniku bezpieczeństwa — to konkretny wpis w moim własnym logu z tego tygodnia. xmlrpc.php pozwala m.in. na specjalną metodę XML-RPC system.multicall, która w jednym żądaniu HTTP umożliwia sprawdzenie wielu par login/hasło naraz. Stąd właśnie jego popularność jako celu prób logowania i ataków typu brute-force czy pingback spam, mimo że sam adres nie zwraca 404 i nie widać go w typowym raporcie „strona nie znaleziona”.
Skąd boty w ogóle znają te adresy?
Boty nie muszą wcześniej znać struktury mojej strony. Wystarczy, że mają listę popularnych ścieżek i sprawdzają je po kolei, niezależnie od tego, na jaki adres akurat trafił:
/admin /wp-login.php /administrator/ /xmlrpc.php /user/login /login.php /wp-admin/install.php
Ten sam skrypt bez zmian potrafi sprawdzić WordPressa, Joomlę i pięć innych systemów — po prostu odpytuje kolejne adresy z gotowej listy, aż któryś zwróci coś innego niż 404. Jeśli trafi, dopiero wtedy „wie”, z czym ma do czynienia, i sięga po kolejny, bardziej specyficzny zestaw ścieżek.
Czy to znaczy, że ktoś próbuje się włamać?
Niekoniecznie — i to jest rozróżnienie, które warto mieć w głowie patrząc na własne logi:
- Rekonesans (discovery).
GET /admin— sprawdzenie, czy endpoint w ogóle istnieje. Sam w sobie nieszkodliwy, choć irytujący w logach. - Próba logowania.
POST /wp-login.phpalboPOST /xmlrpc.phpz danymi logowania — to już realna próba uwierzytelnienia, choćby ślepa. - Wykorzystanie luki. Żądanie celujące w konkretną, znaną podatność konkretnej wtyczki czy wersji CMS-a.
Sam request do /admin — nawet gdy jest ich 69 w kwadrans — pasuje do pierwszej kategorii (rekonesans). Nie mam dowodu na drugą ani trzecią. Ale POST zamiast GET, sfałszowany referrer i rotowane UA to już zachowanie bliższe zautomatyzowanemu narzędziu niż nieszkodliwemu robotowi indeksującemu.
Czy to zjawisko jest powszechne poza moim blogiem?
Tak, i są na to konkretne, publikowane dane z dłuższych eksperymentów niż mój czterodniowy wycinek.
1. SANS Internet Storm Center — dwanaście miesięcy jednej domeny
Jan Kopřiva z SANS ISC opublikował w 2020 roku zestawienie najczęściej odpytywanych ścieżek na domenie, która nigdy nie hostowała żadnego z popularnych CMS-ów. Mimo to boty regularnie sprawdzały typowe adresy:
| Adres | Żądania |
|---|---|
| /wp-login.php | 1140 |
| /admin/ | 189 |
| /administrator/ | 104 |
| /wp-admin/install.php | 82 |
| /login.php | 48 |
Innymi słowy: /admin/ był drugim najczęściej sprawdzanym adresem na stronie, która nigdy nie miała pod nim niczego — dokładnie ten sam wzorzec co u mnie, tylko rozłożony na cały rok zamiast na jeden wieczór.
2. Aristaeus — sto przynęt, siedem miesięcy, 26,4 mln żądań
Znacznie większą skalę pokazuje praca badaczy ze Stony Brook University („Good Bot, Bad Bot: Characterizing Automated Browsing Activity”, IEEE S&P 2021). Zbudowali 100 stron-przynęt (honeysite’ów) opartych o WordPressa, Joomlę, Drupala, phpMyAdmin i Webmin — domeny, które nigdy wcześniej nie istniały, więc cały trafiający tam ruch musiał pochodzić od botów.
Wśród najczęściej żądanych adresów na honeysite’ach WordPressa: xmlrpc.php, wp-login.php, /wp-admin/ i /administrator/. Dokładnie te same adresy, które widzę u siebie.
Skoro strony, które nigdy nie istniały i nigdy nie miały żadnego linku prowadzącego do nich z zewnątrz, dostają miliony takich żądań — to jasny dowód, że ten ruch nie zależy od tego, czy ktoś w ogóle wie o moim blogu. To odbywa się ciągle, niezależnie od popularności konkretnej strony.
Czy zmiana /admin na coś innego zwiększa bezpieczeństwo?
Zmiana adresu panelu z /wp-admin na coś w stylu /moj-tajny-panel-x7 to klasyczny przykład tzw. security through obscurity. Sama w sobie niczego nie zabezpiecza — jeśli ktoś pozna nowy adres (np. z linku w kodzie źródłowym, z pliku sitemap albo po prostu przez przypadek), nadal może pod niego wejść tak samo, jak pod stary.
Ale ma praktyczną, mierzalną zaletę: ogranicza ilość szumu generowanego przez automaty, które nie znają tego konkretnego adresu — czyli właśnie te wszystkie skrypty sprawdzające gotowe listy popularnych ścieżek. Mniej takiego ruchu to:
- mniej prób logowania do odfiltrowania,
- czytelniejsze logi (łatwiej zauważyć coś naprawdę nietypowego),
- mniejsze obciążenie serwera generowane przez requesty, które i tak nie miały szans się powieść.
Czyli: „mniej przewidywalny adres” ≠ „zabezpieczony panel”, ale też nie jest to działanie bez żadnej wartości — po prostu adresuje inny problem niż właściwe zabezpieczenie logowania.
Czego nauczyły mnie te cztery dni logów
- Nie każde 404 pochodzi od człowieka — ale też nie każde jest atakiem; spora część to zwykłe nieistniejące linki i favicony.
/adminbył u mnie jednym skoncentrowanym epizodem (69 POST-ów w 19 minut).- Realne ścieżki WordPressa (
/wp-admin/,/wp-login.php) nie trafiają do logu 404, bo naprawdę istnieją — trzeba po nie sięgnąć do surowych logów serwera. xmlrpc.phpokazał się u mnie realnym, aktywnym celem: 60 żądań z jednego adresu w niecałe 11 minut, podszywających się pod Jetpacka.- Ten sam wzorzec, tylko w większej skali, potwierdzają niezależne badania (SANS ISC, Aristaeus/Stony Brook) — to nie jest wina „popularności” mojego bloga.
- Request do
/adminnie oznacza automatycznie próby włamania — to zwykle najpierw rekonesans.
FAQ
01Dlaczego boty próbują wejść na /admin?+
02Czy request na /admin oznacza próbę włamania?+
POST z loginem i hasłem).03Dlaczego akurat WordPress jest tak często skanowany?+
04Czy xmlrpc.php trzeba wyłączyć?+
Jan Kopřiva, „What pages do bad bots look for?”, SANS Internet Storm Center, 1 sierpnia 2020 — isc.sans.edu/diary/26414.
Xigao Li, Babak Amin Azad, Amir Rahmati, Nick Nikiforakis, „Good Bot, Bad Bot: Characterizing Automated Browsing Activity”, IEEE Symposium on Security and Privacy 2021, Stony Brook University.