Zadania związane z obfuskowanym kodem JavaScript zostały rozwiązane, głównie przez Krzysztofa Kotowicza. Planowałem przygotować jeszcze jedną mutację tego zadania, ale ostatecznie chyba ją sobie odpuszczę, przynajmniej na razie. Do rozwiązania pozostają jeszcze inne zadania, związane z phishingiem na formatce logowania, oraz kontrolą dostępu do funkcji. Pora na wyjaśnienie zadania dotyczącego kontroli dostępu do funkcji.
To jak z tą kontrolą dostępu do funkcji?
Wprowadzenie
Na początek należy przypomnieć wpis O testowaniu kontroli dostępu (do funkcji), w którym pisałem o tym, jak testowanie kontroli dostępu, w tym kontroli dostępu do funkcji, wygląda. Przypominam, że do przykładu dopisałem jeszcze jedną funkcję o nazwie Wysyłanie zapytania, bez zmian pozostał natomiast podział użytkowników, nadal istnieją trzy grupy, które mają zróżnicowany dostęp do funkcji. W tym wypadku do funkcji Wyślij zapytanie dostęp mają użytkownicy należący do grup (i przy okazji nazywający się dokładnie tak samo) user oraz admin. W przypadku użytkownika guest funkcja ta nie jest dostępna w menu. Do sprawdzenia pozostaje to, czy preparując odpowiednie żądania HTTP nie można jednak z niej skorzystać.
Jak wyglądają żądania
W porównaniu z wcześniejszymi przykładami różnica polega na tym, że w tym przypadku żądania są dwa:
POST http://bootcamp.threats.pl/lesson17/ HTTP/1.1 action=questionForm&SessionState=(...)
POST http://bootcamp.threats.pl/lesson17/ HTTP/1.1 question=test&SessionState=(...)&action=questionSend
Pierwsze żądanie służy do otwarcia formatki, drugie - wysłania zapytania. Należy zweryfikować oba żądania.
Weryfikacja kontroli dostępu
W przypadku pierwszego żądania należy ustawić wartość parametru action na questionForm. Po wysłaniu takiego żądania nie dzieje się nic, nie pojawia się formatka. Dokładnie tak jak w przypadku innych akcji, do których użytkownik guest nie ma dostępu (i w których implementacji nie zostały popełnione błędy).
Drugie żądanie zawiera dwa parametry, pierwszy z nich action powinien mieć wartość questionSend, parametr question zawiera natomiast treść zapytania. Wysłanie takiego spreparowanego żądania przez użytkownika z uprawnieniami użytkownika guest "niespodziewanie" kończy się sukcesem.
Test empiryczny pokazał, że kontrola dostępu do funkcji Wyślij zapytanie nie jest realizowana prawidłowo. Operacja ta składa się z dwóch kroków:
- wyświetlenie formatki wysyłania zapytania,
- wysłanie formatki zapytania,
Kontrola dostępu obejmuje tylko pierwszy krok, nie jest możliwe wyświetlenie formatki przez użytkownika, który nie posiada stosownych uprawnień. W drugim kroku weryfikacja uprawnień nie jest już wykonywana, w związku z czym ktoś, kto jest w stanie spreparować odpowiednie żądanie (a nie jest to wcale trudne), może wysłać zapytanie nawet wówczas, gdy dysponuje uprawnieniami użytkownika guest.
Jak często występują takie błędy
Można powiedzieć, że przykład jest banalny i takie błędy nie występują w prawdziwych aplikacjach. Tak, ten przykład jest banalny, ale dokładnie takie banalne błędy niestety występują w aplikacjach. Oczywiście można dyskutować jak w takim przypadku zachowa się określony framework, w części przypadków rzeczywiście samo środowisko nie pozwoli na zaistnienie tak trywialnych błędów. Nie zmienia to jednak faktu, że problem istnieje.
Występowanie błędów kontroli dostępu do danych i do funkcji jest niestety dość powszechne. Jest to o tyle niepokojące, że ryzyko związane z tego typu podatnościami jest wysokie. Warto zwrócić uwagę na to, że:
- skutki wykorzystania podatności są znaczne (dostęp do cudzych danych, wykonanie operacji, do których użytkownik nie ma dostępu, eskalacja uprawnień),
- znalezienie podatności nie jest trudne (co obrazuje ten przykład),
Tak, "prawdziwy atakujący" miałby pewien problem odgadnąć, że w celu wywołania tej określonej operacji musi wywołać akcję questionSend. Mógłby mieć szczęście i odgadnąć lub domyślić się na podstawie konwencji nazewniczych stosowanych w pozostałej części aplikacji. Mógłby spróbować użyć fuzzera. Może trafić również na inne ślady (np. zdarzały się nazwy akcji zaszyte w statycznym kodzie JavaScript wspólnym dla wszystkich użytkowników, o tak abstrakcyjnych scenariuszach jak analiza stron zachowanych w cache przeglądarki nawet nie będę pisać). Może wreszcie mieć dostęp do tej samej aplikacji (lub jej różnych wdrożeń) z różnymi uprawnieniami. Wówczas, tak jak w tym przykładzie, atakujący będzie wiedział jak ma wyglądać żądanie. Pozostanie mu tylko sprawdzić, czy wysłanie odpowiedniego żądania bez odpowiednich uprawnień zakończy się sukcesem.
Kontrola dostępu zasługuje na więcej uwagi
Błędy związane z kontrolą dostępu nie są tak efektywne medialnie jak XSS czy SQLi, choć kilka przypadków tego typu błędów w różnych serwisach rekrutacyjnych "dotarło do mediów". Warto zwrócić na temat kontroli dostępu więcej uwagi, zarówno w procesie projektowania i implementacji aplikacji, jak i w procesie jej testowania. Ponieważ weryfikacja poprawności kontroli dostępu jest technicznie dość prosta (co starałem się pokazać w tych przykładach), podstawowe testy w tym zakresie mogą wykonywać "zwykli" testerzy, nie koniecznie specjaliści od spraw bezpieczeństwa.