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.

Wykonanie sekwencyjne

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.

Dwa przykłady podatności

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.

Kody rabatowe

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.

Wysłanie tego samego kodu w tym samym momencie

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.

Przykładowy sklep

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.

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.

Burp Intruder

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.

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.

Wynik działania

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.

Instalacja Turbo Intrudera

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.

Send to turbo intruder

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.

Turbo intruder

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,

Wynik działania

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.

Wysyłanie plików 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.

Backdoor

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ć.

Dwa okna

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.

Wykonanie kodu

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.