Zgodnie z prawem Murphiego, kiedy coś może pójść źle to na pewno pójdzie źle oraz jeśli teraz jest najmniej odpowiednia chwila, aby się to stało, to stanie się to dokładnie teraz. Nie inaczej było dzisiaj, kiedy każda z moich stron ulokowanych na tym samym serwerze przestała odpowiadać. Był to precyzyjnie zaplanowany atak DDOS, stworzony przeze mnie i wycelowany we mnie samego.

Zwykły czwartkowy dzień

Przecież co złego może stać się w czwartek? Rano, na forum pomocy technicznej jednej z moich wtyczek zauważyłem wiadomość, w której było napisane, że strona nie odpowiada, a raczej odpowiada z błędem 508 Resource Limit Is Reached. Zapytałem więc, czy ogólnie moja wtyczka to spowodowała i czy tylko na swoich ekranach konfiguracyjnych. Dostałem odpowiedź, że to moja strona z dokumentacją nie odpowiada.

Szybka kontrola panelu hostingu pokazała ciekawe statystyki zużycia zasobów. 100% zajętości procesora, praktycznie cały czas 15 aktywnych procesów (czyli limit mojego konta).

Zalogowałem się na serwer i potwierdziłem, htop pokazywał wpadające cały czas procesy, gdzie plikiem wejściowym był index.php tego bloga.

Zużycie jednak nie pojawiło się nagle. Rosło sobie powoli od kilku dni, aby dzisiaj w nocy osiągnąć 100% zajętości procesora i limit aktywnych procesów.

A czym jest ten cały DDOS?

To rozproszony atak Denial Of Service. Polega na tym, że wiele źródeł odpytuje jeden adres i robi to do momentu wykorzystania wszystkich dostępnych zasobów serwera.

Debug

Wyłączyłem wszystkie wtyczki.

Wyłączyłem wszystkie motywy.

Żadnego skutku, procesy nadal wpadają.

Zakomentowałem w ogóle ładowanie WordPressa – o! Procesów jakby trochę mniej.

Cofnąłem wersję WordPress do 4.9.9, bez skutku.

Zakomentowałem ładowanie wszystkich wtyczek, łącznie z tymi MU. Ilość procesów na chwilę spadła, aby za chwilę powrócić z pełną mocą.

Sprawdziłem access_log, wszystko wyglądało dobrze, raz na kilka minut wchodziły boty, głównie skanując kanał RSS, nic nadzwyczajnego w ciągu ostatnich kilku godzin. To przecież nie może być nic z zewnątrz, prawda?

Styczeń 2017

Na WPART powstaje sklep z wtyczkami, gdzie głównym produktem jest Advanced Cron Manager PRO. Wdrażam licencje, które odnawiają się co roku. Wdrażam również klasę, która automatycznie aktualizuje wtyczkę o ile jest aktywna licencja. Wszystko dzięki wtyczce Easy Digital Downloads z rozszerzeniem Software Licensing. Wszystko pięknie działa.

Styczeń 2018

Sklep z WPART ewoluuje do BracketSpace, ze względu na trudności i architekturę wtyczki decyduję, że zamówień i licencji nie będę przenosił do nowego sklepu. Zostawiam wszystko na WPART tak jak jest z myślą, że za rok posprzątam, kiedy ostatnie licencje wygasną.

Styczeń 2019

Podjąłem decyzję o gruntownym sprzątaniu bloga. Usunąłem większość wtyczek, w tym EDD i Software Licensing. Nowy motyw został wypuszczony, statystyki wydajnościowe prezentują się bardziej niż zadowalająco.

A jednak DDOS

Żadne próby naprawy sytuacji nie pomagają. Jedynie wyłączanie całego WordPressa daje efekt, ale przecież nie zostawię nie działającej strony?

Nagle spływa na mnie blask geniuszu, ostatni promyk nadziei. Wrzucam w index.php plik logujący zawartość tablicy $_REQUEST. Bingo!

W ciągu kilku sekund log zapełnia się wpisami. Pierwszy rzut oka i wszystko wiadomo:

Array
(
    [edd_action] => check_license
    [license] => 331ad0b05f22df5454dxxxxxxxxxx
    [item_name] => advanced-cron-manager-pro
    [url] => https://xxxxxxxxxxxxx.com
)

Kilkadziesiąt zewnętrznych stron pinguje mojego bloga w celu uzyskania informacji o licencji na ACM PRO. Strona nie wie jak obsłużyć takie pytanie więc po prostu wyświetla bloga.

Z moich obliczeń wynika, że główny plik WordPressa odpalany był 10-15 razy na sekundę.

Plan autozagłady

Dlaczego strona nie odpytała mojego bloga o licencję i nie dała sobie spokoju? Bo kod, który odpowiada za sprawdzanie licencji został napisany w taki sposób, aby pytać API do skutku. Jeśli nie wróci satysfakcjonująca odpowiedź, to próbuje jeszcze raz. I jeszcze raz. I jeszcze raz.

Jakiś dziwny HTML z pewnością nie jest satysfakcjonującą odpowiedzią. Mechanizm oczekiwał prostego JSONa z informacją, że licencja nie jest aktywna.

Z początku wyglądało jakby na serwerze działał jakiś zasobożerny cron, stron jednak przybywało i pojawiało się coraz więcej procesów.

Rozwiązanie

Wystarczyło napisać prostą wtyczkę MU, która odpowie na takie pytanie:

if ( isset( $_REQUEST['edd_action'] ) ) {
	header( 'Content-type: application/json' );
	die( '{"success":false,"license":"invalid","item_id":false,"item_name":"","checksum":"none"}' );
}

W ciągu kilku minut zużycie procesora spadło niemal do zera. ACM PRO uzyskał odpowiedź na którą czekał.

Podsumowanie

Wiem, sam jestem sobie winny. To tak naprawdę nieodpowiedzialne kodowanie doprowadziło do tej sytuacji. Nie przewidziałem, że API przestanie tutaj kiedyś istnieć i sprawi taki problem.

Najdziwniejsze jest w tym wszystkim to, że żadne wywołania nie były zapisane w access_log. Dzięki temu problem udałoby się namierzyć niemal od razu, a nie po 8 godzinach. Obsługa serwera jednak bada sprawę dlaczego tak się stało.

Zobaczenie w końcu takiego wykresu przyniosło niemałą ulgę.

Opublikowany przez Kuba Mikita

Miłośnik minimalizmu i prostoty, bo nie potrafi stworzyć niczego ładnego. Ma kołdrę, na której wypisane są funkcje WordPressa.

6 odpowiedzi na “Jak 2 lata temu stworzyłem atak DDOS na siebie, który uruchomił się teraz”

  1. Po tytule myślałem, że chciałeś sam siebie sprawdzić dwaata temu, jak zareagujesz i czy sprostasz. Potem tak czytam i czytam… Zacząłem się uśmiechać pod nosem.

    Wydawałoby się – taka głupota mała.

  2. Dobry case! Fajnie pokazuje dlaczego świat programowania jest tak skomplikowany i dlaczego wkoło pojawiają się różne, nieprzewidziane błędy. Chyba najbardziej boli te 8 godzin na debugowanie. Ja ostatnio spędziłem kilka dni na analizie skryptu aktualizującego pewien CMS. Jeden błąd, który namierzyłem polegał na tym, że w którymś momencie do ścieżki wkradła się kropka (middot), która nie zawsze była widoczna w podglądzie pliku konfiguracyjnego, ale na serwerze skutkowała utworzeniem innego folderu. Następnie zorientowałem się, że aktualizacja nie przebiega poprawnie bo zamiast 15 GB plików pobrałem 1.5 GB – po prostu nie zauważyłem, że transfer padł w połowie ;-) Także radość ;)

Możliwość komentowania została wyłączona.