Poradnik dotyczący testowania fuzzowania (fuzzowania).

⚡ Inteligentne podsumowanie

Testowanie rozmyte polega na wprowadzaniu do programu nieprawidłowych, nieoczekiwanych lub losowych danych i wykrywaniu awarii, zawieszeń i błędów pamięci, ujawniając w ten sposób luki w zabezpieczeniach, których skryptowe testy funkcjonalne prawie nigdy nie wykrywają samodzielnie.

  • 🔘 Pochodzenie: Barton Miller wymyślił ten termin na Uniwersytecie Wisconsin–Madison, a pierwsze testy przeprowadzone w 1989 r. spowodowały awarię około jednej trzeciej przetestowanych narzędzi UNIX.
  • Pętla sześcioetapowa: Zidentyfikuj cel, zidentyfikuj dane wejściowe, wygeneruj dane rozmyte, wykonaj zadanie, monitoruj zachowanie, a następnie zarejestruj każdy wykryty defekt.
  • Strategie trzech generacji: Rozmycia mutacyjne przekształcają prawidłowe próbki, rozmycia generacyjne tworzą dane wejściowe z modelu, a rozmycia protokołów działają w oparciu o specyfikację.
  • 🧪 Informacje zwrotne dotyczące zasięgu zmieniły sytuację: Nowoczesne silniki przechowują wszystkie dane wejściowe docierające do nowego kodu, co pozwala na wykrywanie znacznie poważniejszych błędów niż w przypadku czysto losowych danych.
  • 🛠️. Narzędzia poszły naprzód: Peach Fuzzer i WebScarab zostały zarchiwizowane, natomiast AFL++, libFuzzer, OSS-Fuzz, boofuzz ​​i OWASP ZAP zostały zachowane.
  • ⚙️ Znane ograniczenia: Testowanie niejawne wykrywa awarie, a nie błędy logiczne. W związku z tym stanowi ono uzupełnienie, a nie zamiennik przeglądu kodu i testów penetracyjnych.

Poradnik dotyczący testowania fuzzowania (fuzzowania).

Co to jest testowanie fuzzowe?

Testowanie rozmycia lub rozmycie Testowanie rozmyte (FUZZ) to technika testowania oprogramowania polegająca na wprowadzaniu nieprawidłowych lub losowych danych, zwanych FUZZ, do systemu w celu wykrycia błędów w kodzie i luk w zabezpieczeniach. Celem testowania rozmytego jest wstawianie danych za pomocą zautomatyzowanych lub półautomatycznych technik i testowanie systemu pod kątem różnych wyjątków, takich jak awaria systemu lub błąd wbudowanego kodu.

Testowanie rozmycia (fuzz testing) zostało pierwotnie opracowane przez Bartona Millera z Uniwersytetu Wisconsin–Madison, który ukuł ten termin po tym, jak zakłócenia na linii modemowej powodowały awarię używanych przez niego programów. Jego studenci uruchomili pierwsze testy rozmycia (fuzz testing) w 1989 roku i odkryli, że około jedna trzecia testowanych przez nich narzędzi UNIX ulegała awarii lub zawieszała się. Testowanie rozmycia to Testowanie oprogramowania technika i jest to rodzaj Testowanie bezpieczeństwa.

Poniższy diagram przedstawia podstawową pętlę rozmycia, w której wygenerowane dane są przesyłane do testowanej aplikacji, a następnie obserwowana jest reakcja.

Przebieg pracy w testowaniu rozmytym: tester rozmywający generuje nieprawidłowe dane wejściowe i przekazuje je do testowanej aplikacji

Dlaczego warto przeprowadzać testy Fuzz?

Fuzzing zasługuje na swoje miejsce w planie testów, ponieważ bada dane wejściowe, dla których nikt nie pomyślał o napisaniu przypadku testowego. Poniżej wymieniono główne powody, dla których zespoły stosują tę metodę.

  • Testowanie nieścisłe zwykle pozwala na wykrycie najpoważniejszych błędów i usterek bezpieczeństwa, ponieważ awaria stanowi bezpośredni dowód nieobsłużonej ścieżki wejściowej.
  • Testowanie rozmyte daje skuteczniejszy wynik, gdy jest stosowane z Czarny Box Testy, testy beta i inne metody debugowania.
  • Testowanie rozmyte służy do sprawdzania podatności oprogramowania na ataki. Jest to bardzo ekonomiczna technika testowania, ponieważ dane wejściowe są generowane automatycznie, a nie wpisywane ręcznie.
  • Testowanie rozmyte (fuzz testing) to jedna z technik testowania typu „czarna skrzynka”. Fuzzing to również jedna z najczęstszych metod wykorzystywanych przez hakerów do wyszukiwania luk w zabezpieczeniach systemu, dlatego jego uruchomienie w pierwszej kolejności eliminuje najłatwiejszą drogę atakującemu.

Rodzaje testów rozmytych

Fuzzery są zazwyczaj grupowane według wiedzy o programie, który atakują. Im więcej fuzzer wie, tym głębiej może wniknąć w kod.

Typ Co wie fuzzer Typowe zastosowanie
Rozmycie czarnej skrzynki Nic o układzie wewnętrznym; widzi tylko dane wejściowe i wyjściowe. Szybki test działa na pliku binarnym lub na żywo.
Rozmycie białego pola Pełny kod źródłowy, często połączony z symbolicznym wykonywaniem w celu rozwiązania problemów w trudno dostępnych gałęziach. Głęboka analiza komponentu, którego źródło jest dostępne.
Szare pole rozmycia Brak przeglądu źródła, lecz informacja zwrotna z czasu wykonania, np. do których rozgałęzień kodu dotarło wejście. Domyślna wartość dla nowoczesnych silników, takich jak AFL++ i libFuzzer.

Drugi, starszy podział rozdziela głupie rozmywanie od inteligentne rozmywanie. Głupi fuzzer odwraca bity bez pojęcia o formacie danych wejściowych, więc większość danych jest odrzucana przez pierwszy napotkany parser. Inteligentny fuzzer rozumie sumy kontrolne, pola długości i strukturę wiadomości, dzięki czemu jego dane wejściowe przechodzą walidację i docierają do logiki leżącej u ich podstaw. Fuzzing sterowany zasięgiem jest udoskonaleniem metody „szarej skrzynki”, które uczyniło rozmycie głównym nurtem: silnik instrumentuje kod binarny, przechowuje wszelkie dane wejściowe docierające do nowej gałęzi i mutuje te, które pozostały, dzięki czemu korpus stopniowo ewoluuje w kierunku niezbadanego kodu zamiast zaczynać od nowa od losowego szumu.

Jak przeprowadzić test Fuzz

Kroki testowania rozmytego obejmują podstawowe kroki testowania:

Krok 1) Zidentyfikuj system docelowy — wybierz punkt końcowy pliku binarnego, biblioteki, usługi lub protokołu, który będzie przedmiotem ataku i potwierdź, że masz uprawnienia do jego przetestowania.

Krok 2) Zidentyfikuj dane wejściowe — wypisz wszystkie punkty wejścia, z których obiekt docelowy odczytuje dane: pliki, argumenty wiersza poleceń, zmienne środowiskowe, pakiety sieciowe, pola formularzy i ładunki API.

Krok 3) Wygeneruj rozmyte dane — wytwarzać nieprawidłowe dane wejściowe poprzez mutowanie prawidłowych próbek, generowanie ich z modelu formatu lub łączenie obu tych metod.

Krok 4) Wykonaj test, używając danych rozmytych — uruchom cel na wygenerowanych danych wejściowych, najlepiej w pętli, która automatycznie uruchamia proces ponownie po każdym błędzie.

Krok 5) Monitoruj zachowanie systemu — zwracaj uwagę na awarie, zawieszenia, błędy potwierdzenia, niekontrolowane wykorzystanie pamięci i raporty dotyczące dezynfekcji, zamiast sprawdzać tylko wydruk.

Krok 6) Wady dziennika — zapisz dokładne dane wejściowe, które wywołały każdą awarię, zmniejsz je do najmniejszego powtarzającego się przypadku i umieść w stosie trace załączone.

Przykłady Fuzzerów

Fuzzery klasyfikuje się również według sposobu, w jaki budują dane wejściowe. Trzy podejścia przedstawione poniżej to te, z którymi spotkasz się najczęściej.

  • Fuzzery oparte na mutacjach Modyfikuj istniejące próbki danych, aby utworzyć nowe dane testowe. To bardzo proste i przejrzyste podejście: zaczyna się od prawidłowych próbek protokołu i nieustannie modyfikuje każdy bajt lub plik.
  • Fuzzery oparte na generacjach Definiują nowe dane na podstawie danych wejściowych modelu. Zaczynają generować dane wejściowe od podstaw, zgodnie ze specyfikacją.
  • Fuzzery oparte na protokole Zależą one od szczegółowej wiedzy na temat testowanego formatu protokołu, a zrozumienie to pochodzi ze specyfikacji. Polega ona na zapisaniu tablicy specyfikacji w narzędziu, a następnie zastosowaniu techniki generowania testów opartej na modelu do prześledzenia specyfikacji i dodania nieprawidłowości do zawartości danych, sekwencji itd. Jest to również znane jako testowanie składni, testowanie gramatyczne lub testowanie odporności. Fuzzer może generować przypadki testowe z istniejącego przypadku lub może wykorzystywać prawidłowe lub nieprawidłowe dane wejściowe.

Istnieją dwa ograniczenia fuzzowania opartego na protokole:

  1. Testowanie nie może być kontynuowane, dopóki specyfikacja nie będzie dojrzała.
  2. Wiele przydatnych protokołów jest rozszerzeniem opublikowanych protokołów. Jeśli testy fuzz opierają się na opublikowanych specyfikacjach, Pokrycie testowe dla nowych protokołów będzie ograniczona.

Najprostszą formą techniki fuzzingu jest wysyłanie losowych danych wejściowych do oprogramowania w postaci pakietów protokołu lub zdarzeń. Ta technika przekazywania losowych danych wejściowych jest bardzo skuteczna w wyszukiwaniu błędów w wielu aplikacjach i usługach. Dostępne są również inne techniki, które są bardzo łatwe do wdrożenia. Aby je wdrożyć, wystarczy zmienić istniejące dane wejściowe, a zmiana danych wejściowych może nastąpić poprzez prostą zamianę bitów.

Rodzaje błędów wykrytych przez Fuzz Testing

Ponieważ analiza rozmyta ocenia przebieg na podstawie zachowania programu, a nie na podstawie wartości oczekiwanej, wykryte defekty grupują się w trzy rodziny.

  • Błędy asercji i wycieki pamięci: Ta metodologia jest powszechnie stosowana w przypadku dużych aplikacji, w których błędy mogą mieć wpływ na bezpieczeństwo pamięci, co stanowi poważne zagrożenie. Buffer tutaj pojawiają się przepełnienia, błędy użycia po zwolnieniu i odczyty poza zakresem.
  • Nieprawidłowe dane wejściowe: W testowaniu rozmytym, rozmycia służą do generowania nieprawidłowych danych wejściowych, które są następnie wykorzystywane do testowania procedur obsługi błędów, co jest istotne w przypadku oprogramowania, które nie kontroluje swoich danych wejściowych. Proste rozmycie można postrzegać jako sposób na automatyzację. testy negatywne.
  • Błędy poprawności: Fuzzing może być również używany do wykrywania niektórych rodzajów błędów „poprawności”, takich jak uszkodzona baza danych lub słabe wyniki wyszukiwania. Fuzzing różnicowy, który przekazuje te same dane wejściowe do dwóch implementacji i porównuje uzyskane wyniki, jest standardową metodą ich wykrywania.

Narzędzia do testowania Fuzza

Narzędzia wykorzystywane w zabezpieczeniach sieci WWW mogą być szeroko stosowane w testach niejasności, takich jak: Burp Suite i Peach Fuzzer. Kilka z poniższych klasycznych nazwisk jest już zarchiwizowanych, więc ich aktualny status jest podany obok każdego wpisu.

  • Brzoskwiniowy Fuzzer:Peach Fuzzer zapewnia bardziej kompleksową ochronę bezpieczeństwa niż skaner. Inne narzędzia testowe mogą wyszukiwać tylko znane zagrożenia, podczas gdy Peach Fuzzer umożliwia użytkownikom znajdowanie znanych i nieznanych zagrożeń. Peach Tech został przejęty przez GitLab, a wersja Community Edition v3 nie jest już utrzymywana; utrzymywanym następcą jest… GitLab Protocol Fuzzer Community Edition.
  • Pełnomocnik Spike'a: Profesjonalne narzędzie, które wyszukuje luki w zabezpieczeniach aplikacji internetowych na poziomie aplikacji. SPIKE Proxy obejmuje podstawy, takie jak: SQL Wstrzykiwanie i ataki między witrynami w całkowicie otwartym środowisku Python infrastruktura i była dostępna dla Linux oraz Windows. Nie był on aktualizowany od wielu lat i został tu zamieszczony ze względu na kontekst historyczny.
  • WebScarab:WebScarab jest napisany w Java i dlatego jest przenośny na wiele platform. Framework WebScarab komunikuje się za pomocą protokołów HTTP i HTTPS i działa jako serwer proxy przechwytujący: pozwala operatorowi przeglądać i modyfikować żądania utworzone przez przeglądarkę przed ich otrzymaniem przez serwer, a także przeglądać i aktualizować odpowiedzi wygenerowane przez serwer przed ich otrzymaniem przez przeglądarkę. Wszelkie luki znalezione przez WebScarab są dodawane do listy zgłoszonych problemów. Repozytorium zostało zarchiwizowane w kwietniu 2024 roku i jest teraz dostępne tylko do odczytu.
  • OWASP WSFuzzer:WSFuzzer to program napisany na licencji GPL Python która była ukierunkowana na usługi sieciowe i w swojej ostatniej wersji bazowała na protokole HTTP Usługi SOAP był głównym celem. Został wydany jako część WebScarab i wraz z nim wycofany; OWASP ZAP i jego dodatek Fuzzer są zalecaną wymianą.
  • Utrzymane alternatywy: AFL++ oraz libFuzzer są standardowymi silnikami opartymi na pokryciu dla kodu natywnego, OSS-Fuzz uruchamia je nieprzerwanie i bezpłatnie w przypadku projektów typu open source, buczenie obejmuje rozmycie protokołu sieciowego w Python. Szersza lista jest zebrana w przewodniku narzędzia do testowania bezpieczeństwa.

Najlepsze praktyki testowania rozmytego

Fuzzer skierowany na cel i pozostawiony sam sobie rzadko znajduje cokolwiek. Poniższe praktyki odróżniają kampanię generującą zgłoszone defekty od kampanii, która jedynie zużywa czas procesora.

  • Zacznij od dobrego korpusu nasion. Zbieraj prawdziwe, prawidłowe dane wejściowe, które aplikacja już akceptuje. Mutowanie oryginalnego pliku pozwala na uzyskanie kodu parsującego znacznie szybciej niż mutowanie losowych bajtów.
  • Napisz małą, szybką uprząż. Punkt wejścia powinien wykonywać jedną czynność na raz, unikać wywołań sieciowych i zapisów na dysku oraz szybko wracać, ponieważ przepustowość mierzy się liczbą wykonań na sekundę.
  • Włącz środki dezynfekujące. Ciche uszkodzenie pamięci często nie powoduje awarii. AddressSanitizer i UndefinedBehaviorSanitizer przekształcają je w natychmiastową, możliwą do zdiagnozowania awarię.
  • Biegaj długo i bez przerwy. Jedno- lub dwugodzinne skanowanie wyłapuje płytkie błędy; wykrycie głębokich ścieżek zajmuje zwykle wiele godzin, dlatego też skanowanie metodą fuzzingu powinno odbywać się w ramach nocnego zadania ciągłej integracji, a nie w ramach sesji ręcznej.
  • Zminimalizuj i usuń duplikaty każdej awarii. Zmniejsz błędne dane wejściowe do najmniejszej formy i zgrupuj awarie według stosu trace, w przeciwnym wypadku jeden błąd pojawi się w postaci setek biletów.
  • Utrzymuj korpus regresji. Dodaj każde odtwarzane wejście do stałego zestawu, który będzie uruchamiany przy każdej kompilacji, dzięki czemu naprawiony błąd nie będzie mógł niepostrzeżenie powrócić.
  • Określ cel zgodnie z prawem. Próba zamaskowania działającej usługi innej firmy bez jej pisemnej zgody jest nieodróżnialna od ataku.

Zalety testów fuzzowych

Stosowane w połączeniu z realistycznymi oczekiwaniami, fuzzing dodaje wartości, której inne techniki mają problem dorównać.

  • Testowanie niejasne poprawia bezpieczeństwo oprogramowania.
  • Błędy wykryte w analizie rozmytej są niekiedy poważne i często pokrywają się z tymi, których używają atakujący, w tym awarie, wycieki pamięci i nieobsłużone wyjątki.
  • Jeśli testerzy nie zauważą jakichś błędów z powodu ograniczeń czasowych i zasobowych, błędy te zostaną wykryte również podczas testów nieprecyzyjnych.
  • Dane wejściowe są generowane maszynowo, więc zasięg rośnie z dnia na dzień, bez konieczności dodatkowego wysiłku ręcznego.

Wady testów fuzzowych

Te same cechy, które sprawiają, że fuzzing jest tani, ograniczają również to, co można za jego pomocą udowodnić.

  • Samo testowanie nieścisłe nie jest w stanie dać pełnego obrazu ogólnego zagrożenia bezpieczeństwa lub zbioru błędów.
  • Testowanie niejasne jest mniej skuteczne w przypadku zagrożeń bezpieczeństwa, które nie powodują awarii programów, takich jak niektóre wirusy, robaki i konie trojańskie.
  • Testowanie rozmyte pozwala wykryć jedynie stosunkowo proste błędy lub zagrożenia i nie uwzględnia logiki biznesowej.
  • Aby działać efektywnie, maszyny potrzebują znacznej ilości czasu.
  • Ustawianie warunku wartości brzegowej przy użyciu losowych danych wejściowych jest bardzo problematyczne, chociaż testerzy rozwiązują obecnie wiele z tych problemów za pomocą deterministycznych algorytmów opartych na danych wejściowych użytkownika.

Testowanie rozmyte a testy penetracyjne

Obie te czynności mają na celu wykrycie luk w zabezpieczeniach, jednak odpowiadają na różne pytania i rzadko można je stosować zamiennie.

kryteria Testowanie Fuzza Testowanie penetracyjne
Prowadzony przez Zautomatyzowany silnik generujący nieprawidłowe dane wejściowe Doświadczony tester analizujący system
Szuka Awarie, zawieszanie się i błędy bezpieczeństwa pamięci Słabości, które można wykorzystać, w tym błędy logiczne i konfiguracyjne
Głębokość Bardzo szeroki zakres danych wejściowych, płytkie rozumowanie Wąskie pokrycie, głębokie rozumowanie
Wydajność Odtwarzanie danych wejściowych i stosu traces Raport z wynikami zawierający ścieżki eksploatacji i oceny ryzyka
Najlepszy moment Ciągle, w procesie kompilacji Okresowo, w odniesieniu do wersji Release Candidate

W praktyce te dwie rzeczy wzajemnie się napędzają: testowanie rozmywające usuwa tanie, zautomatyzowane awarie, dzięki czemu ograniczony czas testera może zostać wykorzystany na analizę błędów, które zauważy tylko człowiek.

FAQ

Korpus zalążkowy to początkowy zbiór prawidłowych danych wejściowych, które mutuje fuzzer. Najlepiej sprawdzają się małe, zróżnicowane, rzeczywiste pliki, ponieważ każdy z nich przechodzi już przez parser i pozwala silnikowi przeznaczyć budżet na bardziej szczegółowy kod, zamiast na pierwszą kontrolę poprawności.

Fuzz-Hair to niewielka funkcja, która przekazuje jeden bufor rozmytych bajtów do testowanego kodu. Powinna ona unikać stanu globalnego, zapisów do plików i wywołań sieciowych, aby silnik mógł ją uruchamiać tysiące razy na sekundę.

Godzina lub dwie ujawniają drobne błędy. Poważne kampanie trwają wiele godzin, a nawet dni, ponieważ nowe informacje pojawiają się seriami. Płaskowyż krzywej zasięgu, a nie wskazanie zegara, jest uczciwym sygnałem, że kampania przestała przynosić efekty.

AddressSanitizer wychwytuje przepełnienia bufora i błędy użycia po zwolnieniu, UndefinedBehaviorSanitizer wychwytuje niewłaściwe użycie liczb całkowitych i wskaźników, a MemorySanitizer wychwytuje odczyty niezainicjowanej pamięci. Bez nich wiele błędów jest pomijanych, a fuzzer w ogóle nie zgłasza błędu.

Powtórz to, zminimalizuj dane wejściowe do najmniejszego przypadku awarii, zgrupuj je z awariami współdzielącymi ten sam stos trace, a następnie złóż jeden bilet za pośrednictwem zwykłego proces zarządzania defektami i dodać dane wejściowe do korpusu regresji.

Łączy je przypadkowość, ale nie intencja. Testowanie małp wysyła dowolne akcje użytkownika do działającego interfejsu, podczas gdy analiza rozmyta ma na celu konkretny parser danych wejściowych i mierzy pokrycie kodu, dzięki czemu może skierować się w stronę kodu, do którego wcześniejsze dane wejściowe nie dotarły.

Modele językowe są wykorzystywane do tworzenia uprzęży dla nierozmytych interfejsów API, syntezy danych wejściowych dla nietypowych formatów oraz do grupowania i podsumowywania raportów o awariach. Silnik nadal dostarcza informacji zwrotnych o pokryciu; model w dużej mierze eliminuje ręczną konfigurację.

Drugi pilot GitHub może utworzyć punkt wejścia libFuzzer, plik kompilacji i generator zalążków na podstawie istniejącego podpisu API. Revuważnie obserwuj wynik, ponieważ narzędzie, które po cichu łyka błędy, nie zgłosi żadnych awarii.

Podsumuj ten post następująco: