WordPress: Ukrycie wp-login.php oraz wp-admin/
Spis treści
By dokonywać jakichkolwiek zmian w WordPressie (i mowa tu nie tylko o dodawaniu treści ale także
zmianie jego plików źródłowych), to musimy uzyskać dostęp do panelu administracyjnego, a tu z kolei
możemy się dostać tylko za pośrednictwem formularza logowania. Panel admina możemy wywołać przez
dopisanie do adresu strony wp-admin/ , z tym, że jeśli nie jesteśmy zalogowani, to automatycznie
zostaniemy przekierowani na wp-login.php . Nie oznacza to bynajmniej, że nie możemy wykonać
zapytania do plików znajdujących się w katalogu wp-admin/ . Obie te powyższe lokalizacje muszą być
traktowane ze szczególną ostrożnością, a dostęp do nich limitowany i bardzo restrykcyjny.
Dodatkowe wtyczki
Możemy się spotkać z szeregiem pluginów, które w mniejszym lub większym stopniu próbują adresować ten problem ale czy jest sens zaciągać do pracy dodatkowy kod, gdy możemy w bardzo prosty sposób limitować dostęp do tych krytycznych miejsc w serwisie? W tym artykule są opisane z grubsza dwa wektory ataku na skrypt WordPressa. Pierwszym są podatności samego kodu tego CMSa, natomiast drugi z nich dotyczy ataku Brute Force z wykorzystaniem formularza logowania w celu złamania hasła do konta administratora.
Pliki w wp-admin/ oraz skrypt wp-login.php
Jak już wspomniałem wyżej, osobie niezalogowanej, która przegląda naszego bloga, zostanie
przedstawiony formularz logowania, gdy ta odwiedzi wp-admin/ lub wp-login.php . Jeśli prowadzimy
serwis samotnie i mamy przy tym zaimplementowany zewnętrzny system komentarzy, np. disqus, to mamy
bardzo uproszczone zadanie, bo ochrona naszej strony sprowadzać będzie się do wskazania serwerowi
komu ma wyświetlać formularz logowania i panel admina.
Analiza zapytania
Zamieńmy się na chwilę w internetowego bota i odwiedźmy naszą stronę próbując wyciągnąć jakiś plik z
katalogu wp-admin/ . W tym celu posłużymy się narzędziem wget , przykładowo:
$ wget --spider https://morfitronik.lh/wp-admin/about.php --no-check-certificate
Poniżej przeanalizujemy sobie log z tego co się tak naprawdę stanie po wykonaniu powyższego
polecenia. Opcja --no-check-certificate jest dodana by obejść problemy z własnoręcznie
wygenerowanym certyfikatem i bez niej nie dałoby rady wykonać zapytania. Z kolei parametr --spider
sprawi, że wget będzie zachowywał się bardziej jak zwykły bot (coś na wzór googlowskiego) i nie
pobierze żadnej części witryny, jedynie zwróci szereg komunikatów.
Po wklepaniu powyższego polecenia w terminalu, najsampierw jest rozwiązywana nazwa hosta
morfitronik.lh na adres IP, po czym pod ten adres jest wysyłane żądanie:
...
Spider mode enabled. Check if remote file exists.
--2015-05-31 15:27:10-- https://morfitronik.lh/wp-admin/about.php
Resolving morfitronik.lh (morfitronik.lh)... 192.168.10.10
Connecting to morfitronik.lh (morfitronik.lh)|192.168.10.10|:80... connected.
...
Jako, że ja u siebie w środowisku testowym mam wymuszone korzystanie z protokołu SSL, to chwilę po
wysłaniu zapytania, zostanie zwrócony komunikat, że zasób został przeniesiony permanentnie (kod
błędu 301 ) na inny adres:
...
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://morfitronik.lh/wp-admin/about.php [following]
...
Po czym zapytanie zostanie przetworzone jeszcze raz, z tym, że w oparciu o nowy URL:
...
Spider mode enabled. Check if remote file exists.
--2015-05-31 15:27:10-- https://morfitronik.lh/wp-admin/about.php
Connecting to morfitronik.lh (morfitronik.lh)|192.168.10.10|:443... connected.
WARNING: The certificate of 'morfitronik.lh' is not trusted.
WARNING: The certificate of 'morfitronik.lh' hasn't got a known issuer.
The certificate's owner does not match hostname 'morfitronik.lh'
...
Po wysłaniu zapytania, klient czeka na odpowiedź z serwera i po chwili ją uzyskuje, ale jako że ten
bot nie jest zalogowany w systemie, zamiast żądanego pliku zostanie przekierowany na wp-login.php:
...
HTTP request sent, awaiting response... 302 Found
Location: https://morfitronik.lh/wp-login.php?redirect_to=https%3A%2F%2Fmorfitronik.lh%2Fwp-admin%2Fabout.php&reauth=1 [following]
...
Po przekierowaniu, zostaje przetworzone kolejne zapytanie, tym razem w oparciu o skrypt logowania
wp-login.php :
...
Spider mode enabled. Check if remote file exists.
--2015-05-31 15:27:11-- https://morfitronik.lh/wp-login.php?redirect_to=https%3A%2F%2Fmorfitronik.lh%2Fwp-admin%2Fabout.php&reauth=1
Connecting to morfitronik.lh (morfitronik.lh)|192.168.10.10|:443... connected.
WARNING: The certificate of 'morfitronik.lh' is not trusted.
WARNING: The certificate of 'morfitronik.lh' hasn't got a known issuer.
The certificate's owner does not match hostname 'morfitronik.lh'
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Remote file exists and could contain further links,
but recursion is disabled -- not retrieving.
Kod błędu 200 oznacza, że zasób istnieje i został zwrócony do bota. Serwer nie odmówił dostępu do
pliku wp-admin/about.php . Zamiast tego przekierował nas na stronę logowania i tutaj również
serwer nie dorzucił żadnych obostrzeń ze swojej strony.
Dla porównania, jeśli byśmy spróbowali uzyskać dostęp do nieistniejącego pliku w katalogu
wp-admin/ , to zostanie mam zwrócony poniższy log:
$ wget --spider https://morfitronik.lh/wp-admin/about.php-test --no-check-certificate
...
HTTP request sent, awaiting response... 404 Not Found
Remote file does not exist -- broken link!!!
I tu już mamy kod błędu 404 i właśnie z tego powodu, powinniśmy zablokować możliwość wejścia w
katalog wp-admin/ nieuprawnionym osobnikom.
Trzeba jednak pamiętać, że niektóre pluginy czy motywy wymagają do poprawnego działania pewnych
plików z katalogu wp-admin/ , np. admin-ajax.php .
Na tym blogu tylko ja będę miał możliwość korzystania z logowania do panelu WordPressa, no bo wszyscy ludzie, którzy będą chcieli komentować wpisy, będą to już robić przy pomocy disqus'a, a tam już jest osobny mechanizm logowania i kompletnie niepowiązany ze tym WordPressowskim. Dzięki czemu nie muszę utrzymywać i martwić się o bazę danych użytkowników, oraz jeśli ktoś posiada konto na disqus'ie, to będzie miał możliwość komentowania nie tylko moich treści ale również i kontentu całej masy innych blogerów.
Zakładanie blokady na wp-login.php i wp-admin/
Mamy kilka opcji co do wprowadzenia restrykcji na te dwa zasoby. Pierwszą z nich jest moduł Apache
mod_rewrite , przy pomocy którego to przepiszemy odpowiednie adresy chowając w ten sposób te dwie
lokalizacje. Drugi sposób zaś wykorzystuje dyrektywy Files oraz MatchFiles do odmawiania dostępu
do plików w obrębie instalacji WordPressa.
Moduł mod_rewrite
Jeśli chcemy skorzystać z modułu mod_rewrite , to w konfiguracji serwera Apache dla katalogu z
instalacją WordPressa lub w pliku .htaccess dopisujemy poniższy kod:
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_URI} wp-login.php|wp-admin
RewriteCond %{REMOTE_ADDR} !^192.168.10.100$
RewriteRule . - [R=403,L]
</IfModule>
Mamy tam zdefiniowane dwa warunki i oba z nich muszą być spełnione by serwer zwrócił kod błędu 403
i zakończył przetwarzanie zapytania. Pierwszym z nich jest wywoływanie adresów mających w nazwie
wp-login.php lub wp-admin . Drugim zaś adres IP i w tym przypadku każdy adres, za wyjątkiem
192.168.10.100 będzie pasował.
Dyrektywy Files i MatchFiles
Jeśli nie chcemy korzystać z powyższego rozwiązania, możemy zaprzęgnąć do roboty dyrektywę Files
lub MatchFiles . Nie różnią się one zbytnio, jednak ten drugi powinien być używany w kontekście
szerszego dopasowania plików, np. za pomocą
wyrażeń regularnych. Zatem jeśli chcemy przy pomocy tych dyrektyw zablokować dostęp do panelu admina
i formularza logowania, dopisujemy w konfiguracji serwera albo również w pliku .htaccess poniższe
zwrotki:
<Files wp-login.php>
Require all denied
Require ip 192.168.10.100
</Files>
<Files wp-admin>
Require all denied
Require ip 192.168.10.100
</Files>
Test zabezpieczeń
Odpalmy zatem jeszcze raz wget i sprawdźmy czy jest zwracany ten komunikat co trzeba:
$ wget --spider https://morfitronik.lh/wp-admin/about.php --no-check-certificate
...
HTTP request sent, awaiting response... 403 Forbidden
Remote file does not exist -- broken link!!!
Zatem blokada zadziałała. Istnieje możliwość przestawienia kodu błędu na 404 ale nie zalecam tego
robić, bo niekoniecznie googlowi może się podobać, że w naszym serwisie są linki do nieistniejących
zasobów. Od tej pory jedynie klienci z adresu IP 192.168.10.100 będą mieć dostęp do panelu admina
i formularzu logowania.