Ten wpis nie będzie zawierał jakiejś wiedzy tajemnej. Będzie raczej pokazywał w jaki sposób można podejść do tematu testowania kontroli dostępu. A kontrola dostępu jest bardzo ważnym elementem bezpieczeństwa aplikacji. Jeśli nie jest zrealizowana w sposób konsekwentny i prawidłowy, może się to skończyć takimi informacjami: Poważny błąd w Gadu-Gadu i Gadu Air.
Jak szukać błędów kontroli dostępu (do danych) - przykład
Krótki wstęp
Tematowi kontroli dostępu do danych poświęcony był przykład Nieprawidłowa kontrola dostępu do danych w moim krótkim przewodniku po bezpieczeństwie aplikacji internetowych. Dzisiejszy wpis będzie odnosił się do innego przykładu (Demonstracja koncepcji identyfikatorów pośrednich) i będzie czymś w rodzaju "zrób to sam w weekend".
Nasza ofiara
W pierwszym kroku przyjrzyjmy się naszej ofierze, jest nią przykład dostępny pod adresem http://bootcamp.threats.pl/lesson13/. Jej głównym "zasobem" jest lista linków, które są wyświetlane użytkownikowi. Wygląda ona mniej więcej tak, jak na poniższym obrazku. Mniej więcej, bo dla każdej nowej sesji generowana jest nowa baza, nowe obiekty i nowe identyfikatory.

Po kliknięciu na dowolny z elementów, otrzymuje się podgląd i możliwość jego usunięcia.

Wyświetlana jest również informacja o identyfikatorze właściciela. W tym przypadku wyświetlane są wyłącznie te elementy, których właścicielem jest użytkownik o identyfikatorze 0.
Request HTTP, który jest generowany przez przeglądarkę w chwili kliknięcia dowolnego linku, wygląda w sposób następujący (po wycięciu nieistotnych rzeczy):
GET http://bootcamp.threats.pl/lesson13/?id=769692787 HTTP/1.1 Cookie: PHPSESSID=6209675ad09d35011333a130bd2fe497;
Jak widać w parametrze id przekazywany jest identyfikator obiektu. Można domyślać się, że będzie to identyfikator globalny. Na korzyść tej tezy mogą przemawiać pozostałe identyfikatory widoczne na tej stronie:

Na podstawie tej (niewielkiej) próbki, można przypuszczać, że:
- obiekty identyfikowane są przy pomocy liczby,
- identyfikatory są monotonicznie rosnące (nie są losowe),
- identyfikatory mogą być nieciągłe,
To, że identyfikatory są nieciągłe może wynikać między innymi z dwóch powodów:
- obiekt o określonym identyfikatorze został usunięty (przykład ma taką funkcjonalność),
- obiekt o określonym identyfikatorze istnieje, ale należy do innego użytkownika,
Testujemy kontrolę dostępu
Przy testowaniu kontroli dostępu interesuje nas ten drugi przypadek. Ten, w którym obiekt o określonym identyfikatorze istnieje w systemie (bazie), ale należy do innego użytkownika, więc nie jest wyświetlany na liście. To, że coś nie istnieje na liście (nie ma linku, który można kliknąć) wcale nie oznacza, że klient stosownego żądania z takim identyfikatorem wygenerować nie jest w stanie. Jak zachowa się wówczas aplikacja? Jeśli dane należące do innego użytkownika zostaną wyświetlone, mamy błąd kontroli dostępu.
W "normalnym" przypadku w trakcie testów bezpieczeństwa zwykle posiadamy różnych użytkowników z unikalnym dla każdego z nich zestawem danych. Test polega więc na spisaniu identyfikatorów obiektów na użytkowniku A i próbie odwołania się do nich z kontekstu użytkownika B. Nie ma w tym żadnej magii, prosta, monotonna robota. W tym przypadku nie mamy jednak innego użytkownika, odrobina magii się więc przyda.
Wiemy, że istnieją obiekty o identyfikatorach 769692787, 769692833 i 769692835. Zróbmy prosty test - zobaczmy jak aplikacja zachowa się dla pozostałych wartości identyfikatorów w zakresie od 769692787 do 769692835. Stosowne żądania można wygenerować ręcznie, ale z natury jesteśmy (no, przynajmniej ja jestem) leniwi, ułatwmy sobie więc życie z pomocą prostego skryptu w Pythonie. W zasadzie to jest właśnie to miejsce, w którym należałoby użyć mrocznego narzędzia określanego złowrogim słowem fuzzer, ale w celach edukacyjnych sami sobie takiego fuzzera napiszemy.
W tym konkretnym przypadku piekielne to narzędzie będzie wyglądało tak:
import httplib def getUrl(url, data, headers): h = httplib.HTTPConnection('127.0.0.1:8888') h.request('GET', url, data, headers) h.getresponse() URL = "http://bootcamp.threats.pl/lesson13/?id=" headers = {'Cookie': 'PHPSESSID=6209675ad09d35011333a130bd2fe497'} start = 769692787 stop = 769692835 for i in range(start, stop + 1): getUrl("%s%d" % (URL,i), None, headers)
Kod ten dla wartości z zakresu od 769692787 do 769692835 wygeneruje odpowiednie żądania, dodając do nich właściwy identyfikator sesji (PHPSESSID). Żądanie to wysyłane jest do serwera przy pomocy funkcji getUrl, przy czym jest ono wysyłane za pośrednictwem local proxy, które słucha na porcie 8888. W tej roli występuje oczywiście mój ulubiony Fiddler (patrz też: Lekcja 1: absolutne podstawy).
Rezultatem uruchomienia tego skryptu jest 49 sesji przechwyconych przez Fiddlera. Pozostaje wyszukać które z nich "różnią się" od pozostałych.

W tym przypadku możemy poszukać frazy Owner ID, która się pojawia w szczegółach wiadomości. Fidddler pozwala w łatwy sposób podświetlić te sesje, które taką wartość zawierają (CTRL+F). Dodatkowo poszczególne odpowiedzi różnią się rozmiarem, więc poza podświetleniem sesji zawierających określoną wartość, posortuję je jeszcze po wielkości odpowiedzi. Tak wygląda rezultat:

Co widać? Najpierw na kolor żółty podświetliłem wszystkie te sesje, w których odpowiedź zawiera wartość Owner ID, następnie na zielono te sesje, które zawierają wartość Owner ID: 0. Po tej drugiej operacji pozostały podświetlone te sesje, w których wyświetlone zostały informacje najprawdopodobniej należące do innych użytkowników.
Zgodnie z oczekiwaniami na zielono zostały podświetlone sesje, w których odwołanie dotyczyło jednego ze znanych identyfikatorów. Co z pozostałymi? Zgodnie z oczekiwaniami w aplikacji istnieje błąd kontroli dostępu, możliwy jest dostęp do cudzych danych. A potwierdza to między innymi poniższy obrazek. Wiadomość o identyfikatorze 769692834 nie była dostępna na liście do wybrania, należy też do zupełnie innego użytkownika:

Prawda, że proste? Zachęcam do eksperymentowania. Najpierw przy wykorzystaniu identyfikatorów globalnych, a później po przejściu na identyfikatory "lokalne".
Dzisiaj kontynuacja cyklu "zrób to sam w weekend". Tym razem przykład jak szukać błędów typu sql injection (patrz: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')) Podobnie jak ostatnim razem wpis nie będzie zawierał j
Przesłany: Dec 11, 16:18
Raz na jakiś czas przeglądam stronę z rozszerzeniami do Fiddlera. Dziś natknąłem się na nowe rozszerzenie: intruder21. Rozszerzenie to jest bardzo świeże, ale w zasadzie robi to, co do niego należy. Do tej pory z tematem fuzzowania radziłem sobie nieco in
Przesłany: Feb 15, 20:43
Bardzo wiele zapytań ofertowych dotyczących "audytu bezpieczeństwa" (w rzeczywistości przedmiot zamówienia z audytem ma niewiele wspólnego) zawiera założenie/wymaganie odnośnie zastosowania "metodologii black box". Moim zdaniem w zdecydowanej większości w
Przesłany: Aug 18, 21:50