13-01-2020 / Od 0 do pentestera

Atak race condition w praktyce – kody rabatowe w sklepie internetowym

Standardowe aplikacje wykonują swój kod sekwencyjnie – to znaczy linia po linii.

Programiści jednak, zazwyczaj patrzą na kod aplikacji jako na całokształt.

To znaczy interesuje ich to co jest na wejściu oraz to co jest na wyjściu.

Po drodze jest jednak sporo drobnych kroków, które muszą się wykonać po kolei.

A każdy kolejny krok niejako korzysta z danych przetworzonych przez krok poprzedni.

Dzisiaj o podatności Race Condition i wykorzystaniu narzędzia Burp Interuder.

Z tym atakiem mamy do czynienia gdy zdążymy zmodyfikować wartość jakiegoś parametru przed aplikacją, która z niego korzysta.

Najprościej wytłumaczyć to na przykładzie.

W tym odcinku pokaże dwa przykłady – jeden w oparciu o sklep internetowy drugi – w oparciu o wysyłkę plików na serwer.

Sklepy internetowe to popularne witryny.

Pozwalają na dodawanie produktów do koszyka a następnie ich zakup.

Polacy lubią promocje – stąd możliwość podawania specjalnych kodów rabatowych.

Istnieją różne rodzaje kodów – nas jednak interesują takie, które można wykorzystać raz.

To znaczy, że po podaniu kodu rabatowego nie można go już wykorzystać w innym zamówieniu.

Aplikacja po podaniu kodu sprawdza zatem czy takowy istnieje i jeżeli wszystko się zgadza, usuwa go z bazy danych tak, aby jego ponowne użycie nie było możliwe.

Tylko, w przypadku źle zaprojektowanych aplikacji istnieje pewne okno czasowe pomiędzy sprawdzeniem czy kod jest poprawny a jego usunięciem.

Teoretycznie, jeżeli wyślemy dwa zamówienia na raz dokładnie w tym samym momencie – to serwer dla obu z nich zwróci informacje, że kod rabatowy jest prawidłowy.

Dlaczego? Bo usunięcie to kolejna krok do wykonania w aplikacji, który chwilę trwa.

W tym przykładzie rolę sklepu internetowego będzie pełnić prosty formularz z jednym polem.

Można w nim podać kod rabatowy.

Jeżeli serwis go przyjmie – wyświetli odpowiedni komunikat.

Ponowne wykorzystanie zużytego kodu się nie powiedzie – otrzymujemy wtedy informacje o tym, że kod był już użyty.

Dodatkowo na dole strony wyświetla się identyfikator klienta – inny dla każdego zamówienia.

Dzięki temu wiemy, czy mamy do czynienia z tym samym zamówienie a może jednak z innym żądaniem.

Pora na podatność race condition.

Naszym celem jest co najmniej dwukrotne wykorzystanie tego samego kodu rabatowego.

Jeżeli więc będziemy w stanie wyświetlić koszyk z dwoma różnymi identyfikatorami – odniesiemy sukces.

Na początku spróbujemy wbudowane w Burp narzędzie Intruder.

Wybieramy interesujące nas żądanie, w którym wykorzystujemy kod rabatowy.

Chcemy, żeby to żądanie zostało wysłane kilkanaście razy.

Z zakładki payload wybieram zatem Numbers i podane wartości początkowe i końcowe.

Chcemy wiedzieć ile żądań się powiodło – będziemy zatem poszukiwać odpowiedniego ciągu znaków w odpowiedzi od serwera.

Dane te można ustawić w zakładce Options -> grep match.

Po wciśnięciu start attack – narzędzie zacznie wysyłać żądania do serwera.

W bezpłatnej wersji, atak ten jest bardzo wolny.

Nie dziwi zatem brak pozytywnych rezultatów – udało się nam wykorzystać kod rabatowy tylko raz – tak jakbyśmy korzystali z przeglądarki.

Zanim bowiem zdążyliśmy wysłać kolejne żądanie – poprzednie zamówienie zdążyło już usunąć kod rabatowy z bazy danych.

Pora na turbo intruder – rozszerzenie do Burp, które pozwala na wysyłanie setki żądań na sekundę.

Najpierw musimy je zainstalować.

Przechodzimy do zakładki Extender -> BApp Store.

Tam wybieramy Turbo Intruder i klikamy przycisk install.

Po chwili oczekiwania możemy już korzystać z dobrodziejstwa nowego narzędzia.

Wybieramy nasze żądanie z listy history i klikamy prawym przyciskiem myszy.

Teraz, możemy już wybrać opcję send to turbo intruder.

W nowym oknie widzimy kawałek kodu w języku Python.

Turbo Intruder nie posiada rozbudowanego interfejsu graficznego do projektowania ataku.

Zamiast tego – piszemy kawałek kodu.

Dzięki temu jest on bardzo elastyczny a jednocześnie prosty w użyciu.

Przykładowy kod przedstawia jak można używać tego narzędzia.

My, potrzebujemy lekko go zmodyfikować.

Chcemy wykonać żądanie 1000 razy – stąd funkcja range.

Potem dodajemy żądania do kolejki – używając engine.queue.

Gdy już wszystkie 1000 żądań zostaną zakolejkowane pora na uruchomienie narzędzia – metodą start.

Teraz musimy się zająć zwracanymi wynikami.

Nie interesują nas wszystkie odpowiedzi a tylko te ze słowem Kupon.

Wtedy wiemy, że kupon zadziałał i rabat został naliczony.

Sprawdzamy wiec czy taki ciąg znaków występuje w odpowiedzi serwera.

W zależności od wymagań możemy jeszcze zmodyfikować ilość jednoczesnych połączeń i ilość żądań na połączenie – ja jednak pozostawiam te wartości bez modyfikacji,

Po kliknięciu Attack – o ile mamy szczęście powinnyśmy otrzymać co najmniej dwa wyniki – z różnymi identyfikatorami.

Oznacza to, ze udało się nam wykorzystać kod 2 razy dla różnych koszyków.

Pora na drugi, szkolny przykład.

Tym razem aplikacja wysyłająca pliki na serwer.

Użytkownik może wysłać zdjęcia na stronę.

Aby zapewnić bezpieczeństwo skrypt sprawdza, czy plik rzeczywiście jest zdjęciem – bazując na rozszerzeniu pliku.

Czyli – jeżeli jest to plik z końcówką gif lub jpg – wszystko jest w porządku.

W innym wypadku – jest to prawdopodobny atak.

Dzięki temu przynajmniej w teorii użytkownik nie może wysłać złośliwego pliku PHP, który mógłby zostać wykonany na serwerze.

Tylko, ze programista popełnił jeden błąd.

Nie sprawdza rozszerzenia na początku.

Zamiast tego każdy przesłany przez użytkownika plik zapisuje w katalogu i dopiero wtedy sprawdza jego rozszerzenie.

Gdy jest ono nieprawidłowe – usuwa wcześniej zapisany plik z dysku.

To daje nam pole do nadużyć.

Przy każdym wysłanym pliku istnieje bowiem moment, w którym plik jest już na dysku a jeszcze nie został usunięty przez skrypt.

Dzięki temu – jeżeli wyślemy złośliwy plik PHP i trafimy na odpowiedni moment – może dojść do zdalnego wykonania kodu na serwerze – pomimo teoretycznych zabezpieczeń,

Niestety, ten moment trwa ułamek sekundy i trafienie na niego nie jest takie proste.

Tutaj ponownie do gry wchodzi Turbo Intruder.

Przygotowałem sobie prosty skrypt w PHP – który wyświetla tekst zhakowany oraz nazwę użytkownika, z uprawnieniami którego został on wykonany.

Wysyłam ten plik przy pomocy formularza a następnie próbuje wejść na jego ścieżkę.

Wiem bowiem, ze skrypt wysyła zdjęcia do podkatalogu uploads.

Oczywiście, otrzymałem błąd 404 – ponieważ w między czasie plik został usunięty.

Pora na atak.

Musze otworzyć dwa okna Tubro Intrudera.

Jedno – w którym wysyłam plik i jedno, w którym próbuje go wykonać.

W obu przypadkach ustawiam ilość żądań na odpowiednio dużą – w naszym wypadku 1000 powinno wystarczyć.

Wiemy, że w przypadku sukcesu – nasz skrypt wyświetla tekst zhakowany.

Sprawdzam wiec czy odpowiedź z serwera zawiera taki ciąg znaków.

Teraz możemy już równocześnie uruchomić oba ataki.

Jeśli tylko mamy szczęście – nasz złośliwy plik php pomimo jego usunięcia powinien zostać wykonany co najmniej kilka razy.

Tak o to ominęliśmy zabezpieczenia – tylko przez to, ze programista pomylił ich kolejność.

Oczywiście to nie jedyne zastosowania tego narzędzia.

Zachęcam do dalszych eksperymentów.