Fuzzing: od awarii do zgłoszenia, które da się odtworzyć

Przygotuj zgłoszenie awarii z fuzzingu: dokładna wersja, dane wejściowe, polecenie, dowody, osoba odpowiedzialna i weryfikacja poprawki.

Ernest Bursa

Ernest Bursa

Founder · · 10 min czytania
Man inspecting a board at a workbench beside a closed laptop

Przydatne zgłoszenie awarii wykrytej podczas fuzzingu łączy dane wejściowe wywołujące błąd z dokładną wersją badanego programu, poleceniem, środowiskiem i zaobserwowanym skutkiem. Zapisz, jak regularnie udaje się odtworzyć problem, kto prowadzi analizę i czego jeszcze nie wiadomo o wpływie na bezpieczeństwo. Gdy poprawka będzie gotowa, zachowaj wyniki ponownego uruchomienia tego przypadku na poprawionej wersji.

Warto zadbać o przekazanie tych informacji, zanim poświęcisz kolejne popołudnie na generowanie danych wejściowych. Sam katalog plików wywołujących awarie zostawia kolejnej osobie podstawowe pytania: z jakiego pliku wykonywalnego pochodzą? Czy błąd występuje poza procesem fuzzingu? Czy badany program skompilowano z narzędziem wykrywającym błędy pamięci? Jaki wynik pozwoli uznać problem za naprawiony?

Porady Chrisa Wellonsa dotyczące testów z AFL++ wróciły do dyskusji na Hacker News, którą odnotowaliśmy 2 listopada 2026 r. Artykuł pochodzi z 5 lutego 2025 r., więc chodzi o ponowne zainteresowanie starszym poradnikiem technicznym. Jego praktyczne podejście jest dobrym punktem wyjścia do osobnego pytania: jak zamienić znalezisko w pracę, którą może podjąć ktoś inny?

Poniższa procedura to propozycja przekazywania takich spraw w niewielkim zespole inżynierskim, oparta na dokumentacji AFL++ i ClusterFuzz. Nie opisuje kampanii fuzzingu przeprowadzonej przez nas. Przykład parsera jest fikcyjny i nie dotyczy odkrytej podatności.

Zachowaj eksperyment, zanim zaczniesz go upraszczać

Zapisz dość informacji, by dało się odtworzyć uruchomienie zakończone błędem. Znaczenie mają nie tylko dane wejściowe, ale też plik wykonywalny, kod sterujący testem, konfiguracja i obserwacje, które pozwalają zinterpretować wynik.

Harness to kod, który podaje wygenerowane dane wejściowe do testowanej funkcji lub programu. Zapisz jego rewizję razem z rewizją badanego programu. Zmiana sposobu inicjalizacji parsera lub przekazywania bufora może sprawić, że te same dane będą już częścią innego eksperymentu.

Oryginalne dane wejściowe przechowuj w miejscu z ograniczonym dostępem, a w zgłoszeniu umieść trwałe odwołanie. Zachowaj istotne wyniki przed wymianą plików binarnych lub przebudową środowiska. Wymień platformę, kompilator, opcje kompilacji, konfigurację narzędzi wykrywających błędy pamięci i opcje uruchomienia, których użycie jest potwierdzone.

Brakujące szczegóły oznacz jako brakujące, zamiast odtwarzać je z pamięci. Załóżmy, że fikcyjny harness parsera dokumentów przestaje działać po otrzymaniu zapisanego pliku. Zdanie „Parser ulega awarii na tym załączniku” pozostawia kilka możliwości: plik może ujawniać błąd badanego programu, harness może niepoprawnie wywoływać parser, a do zaobserwowania awarii może być potrzebne konkretne ustawienie środowiska. Pierwsza notatka powinna zachować opis zdarzenia bez przedwczesnego wyboru jednej z tych przyczyn.

Wellons omawia dwie istotne pułapki dotyczące harnessu w analizowanym przez niego środowisku: C/C++ z dostępnym kodem źródłowym, Linux i glibc. Chodzi o stan przenoszony między iteracjami trybu trwałego oraz zbyt duże bufory wejściowe, które ukrywają przed AddressSanitizer część odczytów poza granicami pamięci. Te przykłady pokazują, dlaczego warto przyjrzeć się samemu eksperymentowi. Nie uzasadniają kopiowania wszystkich ustawień kompilatora do zupełnie innego środowiska technicznego.

Nie przechowuj jedynej kopii wrażliwych danych wejściowych w załączniku do publicznego zgłoszenia. Wygenerowany przypadek może być nieszkodliwy, ale analiza może też obejmować prawdziwe dokumenty, wewnętrzne ścieżki lub dane diagnostyczne. Ustal, kto potrzebuje dostępu, i zachowaj pliki tam, skąd te osoby mogą je pobrać. Link, którego kolejny inżynier nie może otworzyć, nadal oznacza niepełne przekazanie sprawy.

Przygotuj polecenie odtworzenia działające także poza twoją maszyną

Instrukcja powinna pozwolić kolejnej osobie uruchomić właściwy przypadek bez zgadywania lokalnych konwencji. Podaj polecenie wraz z katalogiem roboczym, wymaganymi danymi wejściowymi i istotnymi ustawieniami środowiska.

Procedura naprawiania błędów w ClusterFuzz udostępnia przypadek testowy wywołujący błąd i odpowiednią kompilację programu. Zapisuje też wiersz polecenia oraz ważne zmienne środowiskowe. Dokumentacja zaznacza, że zmienne te mogą być niezbędne do odtworzenia awarii. To przydatny wzorzec nawet wtedy, gdy zespół korzysta z innego narzędzia.

W przekazywanym zgłoszeniu zidentyfikuj badany plik binarny za pomocą trwałego identyfikatora kompilacji i rewizji źródeł. Wyjaśnij, czy program odczytuje plik, standardowe wejście czy korzysta z innego interfejsu. Zapisz, jak zdobyć lub skompilować plik wykonywalny.

Ścieżki właściwe dla konkretnej maszyny zastępuj udokumentowanymi lokalizacjami dopiero po sprawdzeniu, że zmienione instrukcje nadal opisują to samo uruchomienie. Oddziel przygotowanie od wykonania. Jeśli odbiorca potrzebuje konkretnego obrazu kontenera, katalogu danych testowych lub konfiguracji kompilacji, umieść te wymagania przed instrukcją odtworzenia. Niezbędny krok przygotowania nie powinien być ukryty w starej wiadomości na czacie.

Następnie precyzyjnie opisz obserwację. „Nie działa” może oznaczać komunikat narzędzia wykrywającego błędy pamięci, sygnał procesu, przekroczenie limitu czasu albo nieoczekiwany wynik. Zachowaj istotny komunikat diagnostyczny i napisz, jakiego zachowania oczekiwano. Zrzut ekranu może ułatwić rozpoznanie wyniku, ale nie powinien być jedynym zapisem polecenia lub błędu, który trzeba skopiować i przeanalizować.

Zapisuj próby jako obserwacje. Jeśli problem odtwarza się nieregularnie, powiedz to wprost i opisz sprawdzone warunki. Jedna nieudana próba odtworzenia nie dowodzi, że pierwotne znalezisko było błędne. Zaplanuj eksperyment, który może wyjaśnić różnicę, na przykład porównanie oryginalnej kompilacji lub sprawdzenie stanu harnessu.

Oddziel mniejszy przypadek testowy od oceny bezpieczeństwa

Zmniejszenie danych wejściowych może ułatwić analizę. Samo w sobie nie dowodzi jednak, że awarię można wykorzystać do ataku ani że podatny fragment kodu jest osiągalny w twoim produkcie.

AFL++ opisuje użycie afl-tmin do minimalizacji przypadków testowych. Zachowaj oryginał obok wersji zredukowanej i zapisz sposób jej uzyskania. Zanim zastąpisz nią oryginał, ponownie sprawdź istotne zachowanie. Mniejszy plik ma pomagać wyjaśnić analizowany błąd, a nie tylko powodować jakąkolwiek awarię.

Ta sama dokumentacja ostrzega, że ocena możliwości wykorzystania błędu do ataku może wymagać rozległego debugowania i analizy kodu. Zachowaj to rozróżnienie w zgłoszeniu. „Odtworzono błąd wykryty przez narzędzie kontroli pamięci” to przydatny wynik. „Potwierdzono podatność możliwą do wykorzystania zdalnie” wymaga znacznie więcej dowodów niż samo zatrzymanie procesu.

W przypadku fikcyjnego parsera kolejne pytania mogą dotyczyć obecności danego kodu w dostarczanej wersji, możliwości dotarcia do niego przez niezaufane dokumenty i uprawnień procesu parsującego. To pytania do zbadania, a nie ustalone fakty dotyczące przykładu. Zapisuj znaną ekspozycję na zagrożenie oddzielnie od założeń.

Podobnie traktuj grupowanie przypadków: pomaga organizować analizę. Kilka zapisanych plików lub podobnych śladów stosu nie przesądza o liczbie odrębnych przyczyn ani podatności możliwych do wykorzystania. Zachowuj powiązania między obserwacjami, pozostawiając możliwość zmiany grup w miarę postępów analizy.

Jeśli sprawa wymaga procesu publicznego ujawniania, skorzystaj z programu ujawniania podatności (VDP). Dowody odtworzenia wspierają ten proces, ale nie wyznaczają terminu ujawnienia ani nie upoważniają do publikacji wrażliwych plików.

Przekaż jedno zgłoszenie z konkretnym kolejnym eksperymentem

Zgłoszenie powinno szybko odpowiadać na dwa pytania: co może odtworzyć kolejna osoba i jaką niewiadomą powinna rozstrzygnąć w następnej kolejności?

Możesz dostosować poniższy szablon do swojego narzędzia do śledzenia zgłoszeń. To propozycja zapisu, nie wymaganie konkretnego narzędzia. Zamiast wklejać wszystko w opis, umieszczaj linki do materiałów z ograniczonym dostępem.

Badany program i rewizja źródeł:
Rewizja harnessu i punkt wejścia:
Platforma, kompilator, opcje kompilacji i ustawienia wykrywania błędów pamięci:
Oryginalne dane wejściowe i lokalizacja pliku:
Zredukowane dane wejściowe i metoda redukcji, jeśli dostępne:
Polecenie odtworzenia i katalog roboczy:
Zaobserwowany błąd i istotne wyniki:
Oczekiwane zachowanie:
Próby odtworzenia i ich wyniki:
Znana ekspozycja na zagrożenie i wpływ wymagający oceny:
Osoba prowadząca analizę i kolejny eksperyment:
Rewizja poprawki, dowody ponownego uruchomienia i test regresji:

Nie musisz znać wszystkich odpowiedzi przed utworzeniem zgłoszenia. Musisz jednak odróżnić puste pole od zweryfikowanego wniosku. „Nieznana rewizja badanego programu; poproszono autora zgłoszenia o identyfikator kompilacji” mówi więcej niż stanowczy status nadany na podstawie niepełnych dowodów.

Dokumentacja triażu w ClusterFuzz wymienia niezawodność odtwarzania, typ awarii, platformę i zakresy rewizji, w których wprowadzono regresję, jako informacje przydatne w analizie. Takie zakresy są dostępne dla awarii, które można niezawodnie odtworzyć. W swoim zgłoszeniu możesz zachować te same rozróżnienia bez deklarowania, że udało się wskazać commit wprowadzający błąd.

Krótka tabela decyzyjna pokaże, co zrobić dalej:

Dostępne dowody Przydatne działanie Nierozstrzygnięta kwestia
Dane wejściowe bez odpowiadającej im kompilacji lub polecenia Poproś o brakujący kontekst odtworzenia Czy inny inżynier może powtórzyć awarię
Powtarzalna awaria w zapisanej konfiguracji Przydziel analizę przyczyny i ekspozycji na zagrożenie Wpływ na bezpieczeństwo i zakres produktu objęty problemem
Nieregularna awaria Zachowaj wyniki prób i zbadaj różnice warunków Dlaczego niezawodność odtwarzania się zmienia
Kilka pozornie powiązanych przypadków Porównaj dowody i utrzymuj powiązania Liczba odrębnych przyczyn
Poprawka bez dowodów ponownego uruchomienia Sprawdź zapisany przypadek na poprawionej kompilacji Czy zaobserwowany błąd został naprawiony

Wskaż osobę odpowiedzialną za kolejny eksperyment. W przykładzie parsera może to być inżynier, który porówna zachowanie harnessu z produkcyjnym punktem wejścia. Zgłoszenie powinno określać, co ta osoba sprawdzi i gdzie zapisze wynik.

Jeśli potwierdzone znalezisko utknie później na decyzji dotyczącej zgodności lub wydania, będzie to już inny problem. Nasz poradnik eskalacji decyzji o poprawkach bezpieczeństwa opisuje, jak wskazać osobę odpowiedzialną za tę decyzję i termin jej ponownego rozpatrzenia.

Zweryfikuj poprawkę na zapisanym przypadku awarii

Odwołanie do poprawki pokazuje, co się zmieniło. Dowody ponownego uruchomienia pokazują, co się wydarzyło po przetestowaniu istotnego przypadku z tą zmianą.

Przechowuj razem rewizję z błędem, poprawioną rewizję, konfigurację, odwołanie do przypadku testowego i wyniki. Tam, gdzie to praktyczne, dodaj przypadek do testów regresji, aby przyszłe zmiany sprawdzały naprawione zachowanie. Opisz oczekiwany wynik na tyle jasno, by kolejny inżynier rozumiał, po co istnieje ten test.

Procedura naprawiania błędów w ClusterFuzz opisuje ponowne testowanie przypadków na późniejszych kompilacjach. Ta automatyzacja jest funkcją ClusterFuzz. Jeśli zespół uruchamia przypadek ręcznie lub w innym systemie, zapisz rzeczywistą metodę i wynik, zamiast przejmować etykietę weryfikacji z innego narzędzia.

Sprawdź, czy przypadkiem nie zmienił się sam eksperyment. Brak błędu po wyłączeniu odpowiedniego narzędzia kontroli pamięci lub zmianie harnessu może dotyczyć już innego problemu. Wyjaśnij celowe zmiany konfiguracji oraz ich wpływ na możliwe wnioski.

Przy nieregularnej awarii opisz pozostałe niewiadome oraz przyjęty okres obserwacji lub dodatkowe testy. Nie istnieje uniwersalna liczba prób dowodząca, że każdy taki błąd zniknął. Podobnie pomyślne ponowne uruchomienie jednego przypadku uzasadnia wniosek dotyczący tego przypadku i tej konfiguracji. Nie stanowi certyfikatu bezpieczeństwa całego parsera.

Trzymaj dowody razem z historią obsługi zgłoszenia w Kit

Fuzzer, system kompilacji, debugger i inżynierowie dostarczają dowody techniczne. Narzędzie do śledzenia zgłoszeń pomaga powiązać je z analizą i towarzyszącą jej komunikacją.

Zgłoszenia podatności w Kit obsługują notatki o odtworzeniu, oceny oraz statusy oznaczające zgłoszenia wymagające wyjaśnienia, zweryfikowane, rozwiązane lub ze zweryfikowaną poprawką. Podsumowanie odtworzenia i odwołania można zachować przy zgłoszeniu przez cały czas pracy. Kit szyfruje przechowywane pola tekstowe zgłoszenia, w tym kroki odtworzenia. To stwierdzenie nie obejmuje wszystkich materiałów, które możesz przechowywać gdzie indziej.

Status powinien podsumowywać zapisaną pracę. Zanim oznaczysz znalezisko jako zweryfikowane, dodaj link do rzeczywistego wyniku ponownego uruchomienia i pokaż pozostałe ograniczenia zakresu. Kit nie uruchamia AFL++, nie minimalizuje korpusu awarii, nie ustala możliwości wykorzystania błędu do ataku ani nie wykonuje testów regresji.

Zacznij od kolejnego znaleziska, które już czeka na analizę. Sprawdź, czy inny inżynier może uzyskać dane wejściowe, zidentyfikować kompilację, uruchomić polecenie i zrozumieć zaobserwowany błąd. Uzupełnij braki, określ kolejny eksperyment i zachowaj jego wynik przy zgłoszeniu. Dzięki temu zespół może kontynuować analizę na podstawie konkretnych ustaleń.

Powiązane artykuły

Wypróbuj Kit przez 30 dni.

Rekrutacja, zgłoszenia podatności i szkolenia w jednym koncie, dla zespołów, w których nikt nie robi tego na pełny etat. Kartę podajesz na starcie; zrezygnuj przed końcem okresu próbnego, a nic nie zapłacisz.

Zacznij za darmo