gowitness — masowe zrzuty ekranu stron podczas rekonesansu
gowitness robi zrzuty ekranu tysięcy stron przez headless Chrome, czyta wyniki Nmapa i Nessusa, a wizualne duplikaty grupuje haszowaniem percepcyjnym. Piszemy też o tym, czego wymaga legalne użycie i co znajduje się w tej bazie.
Wyobraź sobie sytuację, którą zna każdy, kto robił audyt większej infrastruktury. Skan portów zwrócił cztery tysiące adresów z otwartym portem 80 albo 443. Co dalej? Otwieranie ich po kolei w przeglądarce to tygodnie pracy. A odpowiedź na pytanie „które z tych czterech tysięcy są interesujące" trzeba mieć na drugi dzień.
gowitness rozwiązuje to najprostszym możliwym sposobem: robi zrzut ekranu każdego z nich i pokazuje wyniki w galerii. Człowiek przewija galerię i w kilka minut wyłapuje panel administracyjny bez hasła, zapomniane środowisko testowe i drukarkę z interfejsem webowym. Oko robi to lepiej niż jakikolwiek automat.
Najpierw sprawa upoważnienia
To narzędzie odpytuje cudze serwery, więc zaczynamy od zakresu, w jakim wolno je uruchamiać. Wymieniam to na początku, a nie w przypisie, bo to nie formalność.
gowitness jest przeznaczony do trzech sytuacji: badania własnej infrastruktury, wykonywania zleconego testu bezpieczeństwa w zakresie ustalonym na piśmie oraz udziału w programie zgłaszania podatności, w granicach zakresu opublikowanego przez jego organizatora. Poza nimi się go nie uruchamia.
Skanowanie cudzych systemów bez zgody nie jest w polskim porządku prawnym neutralną czynnością techniczną i może wypełniać znamiona czynu zabronionego, niezależnie od tego, czy cokolwiek zostało uszkodzone. Do tego dochodzą warunki świadczenia usług dostawców i regulaminy usług chmurowych. Praktyczny standard branżowy jest prosty i warto się go trzymać: pisemne upoważnienie z określonym zakresem adresów i okresem, przed pierwszym uruchomieniem narzędzia. Przy zleceniu od klienta to element umowy, a nie miła praktyka.
Warto też pamiętać o skali. Narzędzie potrafi przejść całą podsieć, więc pomyłka w zakresie nie oznacza jednego niepotrzebnego żądania, a kilka tysięcy — pod adresami, których nie miałeś prawa dotykać.
Stan projektu
Repozytorium: sensepost/gowitness. 4502 gwiazdki, 453 forki, napisane w Go, licencja GPL-3.0. Start w październiku 2017, więc dziewięć lat historii. Otwartych zgłoszeń jest 43. Za projektem stoi SensePost, czyli zespół badawczy znany w branży bezpieczeństwa, a prowadzi go jedna osoba z tego zespołu.
Tempo trzeba opisać uczciwie, bo jest nierówne. Ostatnie commity na gałęzi master pochodzą z 22 kwietnia 2026 — czyli sprzed czterech i pół miesiąca. Ostatnie wydanie, 3.1.1, ukazało się 24 listopada 2025, a poprzedzające je 3.1.0 tydzień wcześniej; wcześniejsza linia 3.0.x to jesień 2024.
Zajrzałem jednak, czego dotyczyły te kwietniowe commity, i to zmienia ocenę: podmiany obrazu Dockera na wariant z headless Chrome oraz poprawy wykrywania Chrome. Innymi słowy opiekun robi dokładnie tę pracę, która przy takim narzędziu jest niezbędna — nadąża za przeglądarką, na której całe narzędzie stoi. Chrome zmienia się co kilka tygodni i to on jest tu ruchomym elementem, nie kod. Projekt jest więc wolny w wydawaniu, ale utrzymywany w miejscu, które ma znaczenie.
Pięć rodzajów wejścia
Tu jest największa zaleta praktyczna, bo narzędzie wpasowuje się w istniejący warsztat zamiast wymuszać przepisywanie danych. Katalog poleceń pokazuje pięć trybów skanowania:
scan single— jeden adres, do sprawdzenia czegoś na szybko.scan file— lista adresów z pliku.scan cidr— cała podsieć podana notacją CIDR.scan nmap— czyta wyniki Nmapa wprost z pliku XML.scan nessus— czyta wyniki skanera Nessus.
Dwa ostatnie tryby są tym, co odróżnia narzędzie dojrzałe od skryptu. Typowy przebieg audytu to najpierw Nmap albo Nessus, a potem pytanie „co właściwie stoi pod tymi portami". Skoro gowitness czyta te wyniki bezpośrednio, nie trzeba pisać pośrednika wyciągającego adresy i porty — a to jest właśnie ten kawałek pracy, który każdy pisał sobie sam i za każdym razem inaczej.
Pierwsze uruchomienie wygląda tak:
go install github.com/sensepost/gowitness@latest
gowitness scan single --url "https://twoja-domena.pl" --write-dbFlaga --write-db jest tu istotna, bo bez niej dostajesz same obrazy, a z nią całą resztę danych i możliwość skorzystania z przeglądarki wyników.
Co zbiera poza obrazem
Zrzut ekranu jest głównym produktem, ale narzędzie zapisuje przy okazji rzeczy, które przy analizie bywają cenniejsze: dziennik żądań, dzienniki konsoli przeglądarki, nagłówki odpowiedzi oraz ciasteczka.
Warto rozumieć, dlaczego to ma znaczenie. Nagłówki mówią, co stoi pod adresem i w jakiej wersji. Dziennik żądań pokazuje, gdzie strona sięga po zasoby — czyli ujawnia usługi trzecie i czasem wewnętrzne adresy. Dziennik konsoli wyłapuje błędy JavaScriptu, które nierzadko zawierają ścieżki, nazwy zmiennych albo adresy API. To wszystko dane, które przy ręcznym przeglądaniu galerii i tak byś obejrzał, tylko po jednej stronie na raz.
Haszowanie percepcyjne, czyli dlaczego to działa przy tysiącach adresów
To najciekawszy element techniczny i najłatwiejszy do przeoczenia, bo widać go tylko w liście zależności — jest tam biblioteka do haszowania percepcyjnego obrazów.
Problem, który to rozwiązuje, ujawnia się dokładnie wtedy, gdy narzędzie jest najbardziej potrzebne. Przeskanowanie podsieci klasy B daje tysiące zrzutów, z których ogromna większość jest identyczna: domyślna strona nginksa, ten sam ekran logowania na dwustu urządzeniach tego samego producenta, ta sama strona błędu. Zwykłe porównanie plików nic nie da, bo obrazy różnią się o kilka pikseli — inny znacznik czasu, inna nazwa hosta w nagłówku.
Hasz percepcyjny liczy natomiast odcisk tego, jak obraz wygląda, a nie z jakich bajtów się składa. Dwa zrzuty tej samej domyślnej strony dają odciski niemal identyczne i dają się zgrupować. Praktyczny skutek: galeria zamiast czterech tysięcy kafelków pokazuje kilkadziesiąt różnych rzeczy, a przegląd skraca się z dnia do kwadransa. To jest cecha, która decyduje o użyteczności narzędzia przy prawdziwej skali.
Druga zależność warta wymienienia to biblioteka odcisku technologii — ta sama klasa rozwiązania co znane rozszerzenia rozpoznające, na czym zbudowana jest strona. Dzięki niej wyniki zawierają nie tylko obraz, ale i informację o wykrytym stosie, co pozwala odfiltrować na przykład wszystko, co działa na konkretnej wersji konkretnego oprogramowania.
Pokrętła, które trzeba znać
Wartości domyślne mówią o tym narzędziu więcej niż README, bo pokazują, że autor pisał je z myślą o pracy na cudzej infrastrukturze, a nie o wyciśnięciu maksymalnej przepustowości.
--threads, domyślnie 6 — liczba równoległych wątków. Zaskakująco mało, i to jest właściwa decyzja: sześć równoległych instancji przeglądarki nie przewróci cudzego serwera.--delay, domyślnie 3 sekundy — opóźnienie między wejściem na stronę a zrobieniem zrzutu. Daje stronie czas na dokończenie renderowania, bo zrzut pustego jeszcze ekranu jest bezwartościowy.--timeout, domyślnie 60 sekund na stronę. Przy dużych zakresach warto obniżyć, bo martwe adresy zajmą minutę każdy.--driver, domyślniechromedp, alternatywniegorod— oba sterowniki przeglądarki są wybieralne w czasie działania. To wyjaśnia, dlaczego w zależnościach są dwa, i daje drogę wyjścia, gdy jeden przestanie współpracować z bieżącym Chrome.--http-code-filter— domyślnie zrzucane są wszystkie kody odpowiedzi, ale można zawęzić do wybranych. Przy skanie podsieci ograniczenie do kodu 200 odsiewa większość szumu.--screenshot-format, domyślniejpegz jakością 60, oraz--screenshot-fullpage, domyślnie wyłączone — standardowo dostajesz sam widoczny obszar, nie całą stronę. Przy tysiącach adresów to świadomy kompromis na rzecz rozmiaru.
Jest też flaga z ostrzeżeniem wpisanym wprost w tekst pomocy: --save-content zapisuje treść pobraną w żądaniach sieciowych, a opis mówi, że ma potencjał do wysadzenia twojego magazynu danych pod względem rozmiaru. Przeciwwagą są --skip-html i --skip-network-logs, którymi te dane można odchudzić.
Osobno warto wymienić --javascript (albo wariant czytający z pliku), pozwalające wykonać własną funkcję JavaScriptu na każdej stronie przed zrzutem. Jest przydatne — na przykład do zamknięcia banera zgody zasłaniającego całą stronę — ale trzeba mieć świadomość, czym jest: wykonywaniem własnego kodu w kontekście cudzej strony. Przy zleceniu w ustalonym zakresie to normalne narzędzie pracy, warto tylko, by mieściło się w tym, na co klient się zgodził.
Do potoków powłoki służy --write-stdout, a jeśli ktoś chce mierzyć wydajność, jest globalna flaga profilowania procesora i pamięci, zapisująca wyniki do katalogu ze znacznikiem czasu.
Baza i formaty wyjścia
Dane można zapisać do bazy SQLite, do formatu JSON Lines albo do CSV. Warto jednak zajrzeć w zależności, bo wynika z nich coś, czego README nie mówi wprost: warstwa dostępu do bazy ma sterowniki do SQLite, MySQL i PostgreSQL.
Przy pracy pojedynczej osoby SQLite wystarcza i jest najprostszy. Ale przy zespole pracującym nad jednym zleceniem możliwość wskazania Postgresa oznacza wspólną bazę wyników zamiast wymiany plików — i to jest różnica organizacyjna, nie tylko techniczna.
Do zarządzania wynikami jest osobna grupa poleceń: report list wypisuje zawartość, report generate tworzy raport, report convert przekłada między formatami, report migrate aktualizuje schemat bazy, a report merge — i to jest pozycja warta uwagi, bo jest najobszerniejszym plikiem w tym katalogu — scala wiele baz w jedną. Przy zleceniu, w którym trzy osoby skanowały trzy zakresy adresów, to właśnie polecenie, którego się szuka na końcu.
Chrome: skąd się bierze i jak go odsunąć
Skoro całe narzędzie stoi na przeglądarce, warto wiedzieć, skąd ona się bierze — a odpowiedź jest w opisie flagi --chrome-path: wskazuje ona ścieżkę do binarki Chrome, a domyślnie pobierana jest binarka odpowiednia dla platformy.
To istotne z dwóch powodów. Operacyjnie: pierwsze uruchomienie ściąga przeglądarkę, więc w środowisku bez dostępu do internetu albo z kontrolowanym łańcuchem dostaw trzeba wskazać własną instalację. Z perspektywy bezpieczeństwa: warto wiedzieć, że narzędzie samo dociąga plik wykonywalny, zamiast założyć, że korzysta z systemowego.
Jest natomiast flaga rozwiązująca problem znacznie poważniejszy. --chrome-wss-url pozwala podłączyć się do zdalnej, już działającej instancji Chrome przez protokół DevTools. Znaczenie tego jest następujące: przeglądarka odwiedzająca cudze, potencjalnie złośliwe strony nie musi działać na twojej maszynie ani w twojej sieci. Możesz ją postawić na osobnej, jednorazowej maszynie i tylko sterować nią zdalnie.
Przy narzędziu rekonesansu jest to właściwa architektura. Przeglądarka wchodząca na tysiące nieznanych adresów jest tą częścią zestawu, która najprawdopodobniej zostanie zaatakowana — a trzymanie jej poza swoją siecią jest tanim zabezpieczeniem. Uzupełnia to --chrome-proxy, przyjmująca proxy HTTP albo SOCKS5, czyli droga do wypuszczenia ruchu przez wskazane wyjście.
Dwie mniejsze rzeczy z tej samej grupy. Rozmiar okna to domyślnie 1280 na 720 pikseli, co określa, ile strony trafi na zrzut w trybie bez pełnej strony. A domyślny ciąg identyfikujący przeglądarkę jest wpisany na stałe i podaje Chrome w wersji 128 na macOS — czyli wersję z 2024 roku. Praktycznie jest to sygnatura rozpoznawalna dla każdego, kto patrzy w dzienniki, i warto o niej wiedzieć niezależnie od tego, czy zamierzasz ją zmienić flagą --chrome-user-agent. Dodatkowe nagłówki dokłada się przez --chrome-header, którą można podać wielokrotnie.
Przeglądarka wyników i jak jej nie wystawić
Polecenie report server uruchamia interfejs webowy z galerią, widokiem szczegółów i — jak podkreśla README — w pełni funkcjonalnym API. To jest właściwe miejsce pracy z wynikami.
I tu dochodzimy do rzeczy, którą autor rozwiązał wzorowo, a którą łatwo zignorować. Dołączony plik docker-compose.yml nie uruchamia po prostu serwera z wynikami. Stawia go za odwrotnym proxy Traefik, z terminowaniem TLS i uwierzytelnianiem podstawowym HTTP, przy ustawieniu, w którym usługi nie są domyślnie wystawiane. W komentarzu jest przy tym wyjaśnienie, jak wygenerować własny skrót hasła narzędziem htpasswd, przypomnienie o konieczności podwojenia znaku dolara w pliku oraz odnośnik do osobnego narzędzia, jeśli ktoś chce zarządzać dostępem porządniej.
Powód takiej konfiguracji jest oczywisty, gdy się o nim pomyśli: ta baza jest zbiorem najwrażliwszych danych w całym zleceniu. Zawiera zrzuty ekranów paneli administracyjnych klienta, jego nagłówki, ciasteczka i mapę tego, co jest wystawione i podatne. Serwer z wynikami wystawiony bez uwierzytelniania jest gotowym raportem dla kogoś, kto go znajdzie. Autor przewidział to i dowiózł bezpieczną konfigurację jako domyślną — co przy narzędziach tej klasy nie jest regułą.
Na zrzutach są dane osobowe
Sekcja, której w dokumentacji narzędzi rekonesansu praktycznie się nie spotyka, a która w polskiej firmie świadczącej usługi audytu jest realnym obowiązkiem.
Zrzut ekranu strony logowania zawiera zwykle niewiele. Ale skan większej infrastruktury złapie też panele, w których widać nazwiska pracowników, wewnętrzne systemy z listami spraw, interfejsy z danymi klientów, a czasem stronę, na której ktoś zostawił otwartą sesję. Do tego dochodzą ciasteczka i nagłówki, które narzędzie zapisuje razem z obrazem.
Wniosek jest praktyczny i sprowadza się do czterech rzeczy. Baza wyników jest zbiorem danych i ma leżeć w miejscu z kontrolą dostępu, nie w katalogu domowym na laptopie bez szyfrowania dysku. Retencja musi być określona — dane rekonesansu po zakończeniu zlecenia i przekazaniu raportu nie mają powodu istnieć, a umowa z klientem powinna to mówić. Przekazywanie wyników — także zrzutów wewnątrz raportu — to udostępnianie danych, więc kanał ma być odpowiedni. I wreszcie, jeśli działasz jako podmiot przetwarzający na zlecenie klienta, ta czynność powinna być objęta umową powierzenia.
Żadna z tych rzeczy nie utrudnia pracy. Wszystkie natomiast bywają pomijane, bo narzędzia rekonesansu bywają traktowane jako techniczne, a nie jako coś, co produkuje zbiór danych.
Pułapki w jednym miejscu
- Bez pisemnego upoważnienia tego nie uruchamiasz. Zakres adresów i okres, przed pierwszym skanem.
- Pomyłka w zakresie CIDR to tysiące żądań, nie jedno. Sprawdź zakres dwa razy.
- Bez flagi
--write-dbdostajesz same obrazy, bez nagłówków, ciasteczek i możliwości użycia przeglądarki wyników. - Serwera wyników nie wystawiaj bez uwierzytelniania. Dołączona konfiguracja Dockera robi to dobrze — użyj jej, zmieniając hasło.
- Baza wyników zawiera dane osobowe. Kontrola dostępu, określona retencja, właściwy kanał przekazania.
- Ostatnie wydanie z listopada 2025, przy commitach z kwietnia 2026 — do najnowszych poprawek trzeba budować ze źródeł.
- Wsparcie dla Windowsa jest opisane jako „w większości działające", w odróżnieniu od Linuksa i macOS.
- Narzędzie stoi na headless Chrome, więc wymaga jego obecności i jest wrażliwe na jego wersję — kwietniowe commity dotyczyły właśnie wykrywania przeglądarki.
- Domyślnie narzędzie pobiera binarkę Chrome — w środowisku bez internetu wskaż własną przez
--chrome-path. - Domyślny identyfikator przeglądarki podaje Chrome 128 na macOS, czyli wersję z 2024. To rozpoznawalna sygnatura w dziennikach.
- Flaga
--save-contentpotrafi wysadzić magazyn danych — ostrzeżenie jest w samym tekście pomocy. - Rozważ zdalną przeglądarkę przez
--chrome-wss-url— Chrome odwiedzający nieznane adresy nie powinien działać w twojej sieci. - Licencja GPL-3.0. Osadzenie tego kodu w zamkniętym produkcie wymaga rozmowy z SensePost.
Podsumowanie
gowitness jest narzędziem wąskim i właśnie dlatego dobrym. Robi jedną rzecz — zrzuty ekranu stron na dużą skalę — i robi ją z całą obudową, której się przy tej skali potrzebuje: czyta wyniki innych skanerów, grupuje wizualne duplikaty, zapisuje dane towarzyszące i daje przeglądarkę z API.
- Pięć rodzajów wejścia, w tym czytanie wyników Nmapa i Nessusa wprost, bez pisania pośrednika.
- Grupowanie wizualnych duplikatów haszowaniem percepcyjnym — cecha decydująca o użyteczności przy tysiącach adresów.
- Odcisk technologii i dane towarzyszące — nagłówki, ciasteczka, dzienniki żądań i konsoli obok obrazu.
- Trzy silniki bazy — SQLite dla jednej osoby, Postgres albo MySQL dla zespołu pracującego na wspólnych wynikach.
- Polecenie scalania baz, czyli odpowiedź na zlecenie dzielone między kilka osób.
- Bezpieczna domyślna konfiguracja wdrożenia — przeglądarka wyników za proxy z TLS i uwierzytelnianiem, z instrukcją zmiany hasła.
Projekt jest wydany na licencji GPL-3.0, a sekcja licencyjna README dodaje zdanie warte uwagi: uprawnienia wykraczające poza zakres tej licencji mogą być dostępne, z odnośnikiem do kontaktu z SensePost. Czyli standardowy układ podwójnego licencjonowania — do własnego użytku i do pracy audytorskiej GPL nie stanowi żadnej przeszkody, bo używasz narzędzia, a nie rozpowszechniasz go. Gdybyś natomiast chciał wbudować ten kod w zamknięty produkt komercyjny — na przykład we własną platformę do zarządzania podatnościami — to jest właśnie ta rozmowa, którą trzeba odbyć przed napisaniem pierwszej linii integracji, a nie po.