Skończmy z fetyszem czarnego pudełka

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 wypadków takie podejście jest błędne, charakteryzuje się wyjątkowo niekorzystnym stosunkiem price/performance , lub mówiąc jeszcze bardziej “profesjonalnie”, niskim ROI. Proste pytanie – jakie zalety daje podejście black box? Ja nie jestem w stanie wskazać praktycznie żadnej wartości dodanej takiego podejścia, potrafię natomiast wskazać kilka istotnych jego wad.

Krótki rys tła

Na początek warto zarysować pewne tło – o co właściwie chodzi, o jakich sytuacjach piszę. Sytuacja jest prosta – firma/organizacja przygotowuje aplikację (być może internetową), która ma służyć do realizacji określonych celów biznesowych. Aplikacja taka powinna spełniać szereg wymagań odnośnie tego co powinna robić i w jaki sposób powinna to robić. Istotnym wymaganiem będzie również prawdopodobnie bezpieczeństwo aplikacji. Taka aplikacja nie musi być najbezpieczniejszym produktem na świecie, powinna jednak realizować cele biznesowe przy akceptowalnym poziomie ryzyka. By ten cel osiągnąć, trzeba nad nim pracować od samego początku tworzenia systemu, trzeba zidentyfikować ryzyka, zastosować odpowiednie środki zaradcze. Dobrze jest też zweryfikować, że model zagrożeń jest kompletny (może są jakieś cele atakującego, które nie zostały uwzględnione), trzeba sprawdzić, czy zastosowane środki zaradcze zostały zaimplementowane prawidłowo i czy są skuteczne. Część z tych zadań będzie realizowane prawdopodobnie przez zewnętrznych konsultantów, którzy oczywiście nie pracują dla większej chwały korporacji, tylko za konkretne pieniądze. Moim zdaniem całkiem naturalne jest oczekiwanie, by zwrot z zainwestowanych pieniędzy (wynagrodzenie konsultantów) był jak największy.

“Prawdziwy intruz nie będzie miał konta”

To teraz wyobraźmy sobie następującą sytuację: na wejściu zespół konsultantów otrzymuje wyłącznie adres aplikacji. Nie otrzymuje żadnych kont, w końcu chcemy symulować działania prawdziwego intruza. Tylko, że to jest bzdura. Bo jeśli przykładową aplikacją jest na przykład system bankowości internetowej, to ktoś, kto chce “zbadać jego bezpieczeństwo”, może wykonać zaawansowaną magię, czyli... pójść do banku i założyć konto. Jeśli wolałby nie zostawiać po sobie tak wyraźnych śladów, mógłby zastosować starą technikę ze słupem, który konto założy i przekaże jego dane intruzowi. Równie dobrze mogę sobie wyobrazić zdobycie takich danych z wykorzystaniem phishingu lub malware , a podejrzewam, że i dało by się stosowne konta po prostu kupić. Czy aby na pewno właśnie o takie ćwiczenia chodzi przy testach bezpieczeństwa aplikacji? Jeśli ktoś będzie się upierał, że tak, to mam jeszcze jeden, zabójczy argument. Często testy bezpieczeństwa są wykonywane przed udostępnieniem aplikacji, więc wszystkie opisane wcześniej sposoby uzyskania dostępu do aplikacji mają małe szanse powodzenia. Do czasu udostępnienia aplikacji użytkownikom. A jeśli nawet aplikacja jest już na produkcji, to czy aby na pewno dobrym pomysłem jest, by przy testach bezpieczeństwa aplikacji atakować klientów banku? Jestem w stanie sobie wyobrazić jeden przypadek, w którym to ma sens: prowadzona była akcja uświadamiająca klientów i teraz ma zostać zweryfikowana jej skuteczność. Ale to nie jest tożsame z testem bezpieczeństwa aplikacji , poza tym nawet wówczas bałbym się ryzyka wizerunkowego.

Przykład z brakiem kont w aplikacji jest absurdalny, więc zostawmy go z boku. Załóżmy, że dostaliśmy to upragnione jedno konto. Teraz wypadałoby sprawdzić na przykład kontrolę dostępu do funkcji i kontrolę dostępu do danych. Trochę ciężko to zrobić na jednym koncie. O metodzie sprawdzania kontroli dostępu pisałem tutaj: O testowaniu kontroli dostępu (do funkcji) i tutaj: Jak szukać błędów kontroli dostępu (do danych) – przykład. A co jeśli mamy tylko jedno konto, z najmniejszymi możliwymi przywilejami? Tak, można testować w ciemno próbując zgadnąć jak być może nazywa się określona funkcja i jakie żądanie należy wysłać, by do niej się dostać. Być może to zadziała. Może mamy dobrą intuicję, może mamy dobry słownik (ach ten fuzzing). Na pewno mamy mało czasu. Mało w porównaniu z “prawdziwymi atakującymi”. Jest ich więcej, mogą dysponować większą ilością czasu, mogą mieć więcej szczęścia. Celem testu nie jest (prawdopodobnie) sprawdzenie intuicji testującego czy jakości słowników, które posiada. Moim zdaniem celem testu jest, w tym konkretnym zakresie, zweryfikowanie mechanizmu kontroli dostępu do danych i do funkcji. To zadanie jest stosunkowo proste do zrealizowania, jeśli dysponuje się kilkoma różnymi kontami o różnym poziomie uprawnień i z różnymi zbiorami dostępnych danych.

Wciąż nie jesteś przekonany? Powiedzmy, że w systemie istnieją na razie dwa obiekty, z których jeden należy do “naszego” użytkownika, drugi jest “obcy”. Obiekty mają identyfikatory 4661 oraz 515c. Jak długo zajmie przeszukanie całej możliwej przestrzeni jeśli czas obsługi żądania to jedna sekunda (bo w końcu środowiska testowe często nie są specjalnie wydajne)? Intruz musi to zrobić? No dobrze, ale akurat jego czas nie jest opłacany przez zamawiającego test...

Bezpieczeństwo aplikacji, czy skuteczność obfuskatora?

Zostawmy teraz na chwilę aplikacje internetowe. Teraz popularne stają się aplikacje mobilne. A w ramach testów aplikacji mobilnych często pojawia się temat analizy aplikacji. Jeśli się pod uwagę weźmie na przykład Androida, sprawa jest (prawie) trywialna. Aplikację można dekompilować na kilka sposobów do kilku różnych postaci. Pewnym problemem może być natomiast zastosowanie obfuskacji na etapie kompilacji. Kod, który otrzymuje się po dekompilacji jest, mówiąc oględnie, mało czytelny. Mimo wszystko z mniejszym lub większym nakładem środków (czasu), uda się kod doprowadzić do postaci zrozumiałej i poddać go dalszej analizie. Moim zdaniem w trakcie testu lepiej jest operować na kodzie “czystym”, niż upierać się przy dekompilacji. No chyba, że celem ćwiczenia jest nie tyle sprawdzenie istnienia błędów w kodzie, co sprawdzenie jakości zastosowanego zaciemniacza. Ponownie będę się jednak upierał, że nie do końca właśnie o to chodzi w typowym przypadku.

Umówmy się, każdą aplikację da się przeanalizować, to jest tylko kwestią czasu. Upierając się przy “czarnym pudełku” i odmawiając dostępu do źródeł ryzykuje się tym, że zamówione testy skończą się tam, gdzie oni tak naprawdę dopiero zaczną zabawę. Trzeba uświadomić sobie również, że w przypadku aplikacji mobilnych sytuacja wygląda inaczej, niż w przypadku aplikacji internetowych. W tym drugim przypadku kod aplikacji jest po stronie serwera i użytkownik (intruz) nie ma do niego dostępu (pomijam tu część kodu wykonującą się po stronie klienta). Intruz może więc badać aplikację trochę jak czarną skrzynkę właśnie – przekazywać do niej różne rzeczy i patrzeć, co z drugiej strony z niej wyjdzie. W przypadku aplikacji mobilnych aplikacja udostępniana jest klientowi, który uruchamia ją w kontrolowanym przez siebie środowisku, może poddać ją analizie dynamicznej, może ją również zdekompilować i przeanalizować tak otrzymany kod. To samo można zrobić w trakcie analizy bezpieczeństwa aplikacji, ale wówczas trzeba liczyć się z tym, że spora część opłaconego czasu idzie na udowadnianie rzeczy oczywistych.

Za każdym razem nowe czarne pudełko, choć nic się nie zmieniło

Wróćmy do aplikacji internetowych. Coraz częściej testy aplikacji odbywają się okresowo, po to, by spełnić wymagania compliance. Przy podejściu typu black box za każdym razem testy odbywają się praktycznie od nowa. Znów – uważam, że jest to podejście dalekie od efektywnego. Choćby dlatego, że nie można przetestować wszystkiego (patrz: You just can't test everything). Siłą rzeczy przy testach przyjmuje się jakieś założenia, zawęża zakres, testuje rzeczy najbardziej istotne, obarczone największym ryzykiem wystąpienia błędu, te, w których błąd spowodowałby największe skutki. Jeśli przy każdej iteracji testów praca rozpoczynana jest od początku, w praktyce testowane jest z grubsza to samo, co poprzednim razem. Powtarzana jest cała seria testów, które wykonywane były poprzednio. Rezultaty tych testów są zwykle zgodne z wynikami poprzedniej iteracji, bo... jak się okazuje aplikacja w tym zakresie nie uległa zmianie , podczas gdy istotne zmiany wykonane były gdzie indziej, dodana nowa funkcjonalność, w istotnym stopniu zmianie uległ sposób realizacji wcześniej istniejącej funkcjonalności, choć ze strony użytkownika nic się nie zmieniło (GUI pozostało takie samo). Gdyby ta wiedza (changelog) była dostępna przed rozpoczęciem kolejnej iteracji testów, lista zadań mogłaby zostać skonstruowana w inny sposób. Większy nacisk można położyć na przykład na nowe funkcjonalności, moduły zmienione w istotny sposób czy też te fragmenty aplikacji, które poprzednim razem potraktowane były pobieżnie. Mniej czasu można poświęcić natomiast tym obszarom, które były już w miarę dokładnie sprawdzone, poprawność funkcji bezpieczeństwa (np.: kontrola dostępu, zarządzanie sesją) została potwierdzona, a które nie uległy zmianie.

Czy można to zrobić lepiej?

Przede wszystkim trzeba sobie odpowiedzieć na pytanie co jest celem testów? Moim zdaniem chodzi o ograniczenie ryzyka związanego z wykorzystaniem określonej aplikacji lub systemu do akceptowalnego poziomu. A skoro tak, to takim bezpośrednim celem powinna być identyfikacja jak największej ilości podatności i słabości (nie każda słabość jest podatnością, przynajmniej do czasu, gdy ktoś wpadnie na pomysł jak ją wykorzystać). Istotne może być również zweryfikowanie konkretnych scenariuszy ataku, zwłaszcza jeśli mają one bardziej biznesowy, niż techniczny charakter. Identyfikacja podatności, weryfikacja skuteczności/kompletności stosowanych mechanizmów bezpieczeństwa, weryfikacja założonych scenariuszy ataków powinna być jak najbardziej efektywna. By tak było, dobrze jest dysponować jak największą wiedzą o obiekcie testów. Warto mieć też możliwość szybkiej weryfikacji podejrzeń, sprawdzenia, z czego wynika zaobserwowane dziwne zachowanie. Mogę wymieniać dalej, ale w zasadzie sprowadza się do jednego stwierdzenia: białe pudełko jest lepsze niż czarne. Jeśli nie całkiem białe, niech będzie przynajmniej szare , w tym jaśniejszym odcieniu szarości. I bardzo ważna rzecz: potrzebna jest chęć współpracy, a z tym bywa różnie. Bywa, że znalezienie podatności traktowana jest jak osobista zniewaga. To błąd. Lepiej zidentyfikować podatność i ją usunąć, zanim zrobią to ONI. To chyba jednak mniej traumatyczne przeżycie, niż zobaczenie swoich danych gdzieś w internecie, prawda?

Błędem, moim zdaniem, jest podejście typu “capture the flag” , które skupia się głównie na osiągnięciu jednego, z góry określonego celu, na przykład na uzyskaniu uprawnień administratora. Takie podejście często prowadzi do skupienia się na znalezieniu jednej nisko zwisającej wisienki a następnie na wyeksploitowaniu znalezionej podatności. Choć założony cel zostaje osiągnięty, to czy bezpieczeństwo aplikacji/systemu jako całości ulegnie poprawie? Ile innych mniej lub bardziej istotnych podatności nie zostało zidentyfikowanych, ponieważ cała energia została poświęcona na wykorzystaniu jednej podatności? Być może rzeczywiście jest to jedyna istotna podatność istniejąca w systemie, ale tak naprawdę po takich testach nie możemy być o tym pewni, bo znaczna część aplikacji mogła zostać nawet nietknięta. Uważam nawet, że w sytuacji ograniczonego czasu lepiej jest odpuścić sobie przygotowanie PoC dla znalezionych podatności i wykorzystać zaoszczędzony czas na przetestowanie jak największej części aplikacji. Zwłaszcza, jeśli podatności są dość oczywiste.

A jakie jest Wasze zdanie na ten temat?

Prawie na sam koniec dwa przykłady: jeśli idziesz do lekarza, bo coś jest nie tak ze zdrowiem, współpracujesz z nim, opisujesz swoje dolegliwości, czy podchodzisz na zasadzie “sam znajdź, co jest nie tak”? Ponieważ wielu facetów do lekarza ma stosunek “specyficzny”, inny przykład: jeśli coś jest nie tak z samochodem, oddajesz go do warsztatu. Mówisz na czym polega problem, czy liczysz na to, że warsztat sam wpadnie na to samo, co zauważyłeś? W tym drugim przypadku dla uproszczenia załóżmy, że samochód jest już po gwarancji i płacisz za godzinę pracy mechanika.

Ciekawy jestem, jakie jest Wasze zdanie na ten temat. Czy widzicie jakąś wielką wartość dodaną w stosowaniu podejścia black box? Jakie, według Was, są oczekiwane rezultaty testów bezpieczeństwa i czy “strzelanie w ciemno” jest rzeczywiście najlepszym i najbardziej efektywnym sposobem osiągnięcia tego celu? Dlaczego wciąż żywe jest przeświadczenie, że testujący powinien mieć na początku zerową wiedzę o obiekcie testów i nie powinien otrzymywać żadnych informacji “ze środka”? A może to wcale nie jest tak, że zamawiający chce tego typu testy, ale tak naprawdę nie wie, czego chce i akurat “testy black box “ pierwsze rzucają się w oczy?

Oryginał tego wpisu dostępny jest pod adresem Skończmy z fetyszem czarnego pudełka

Autor: Paweł Goleń