Choć piszę głównie o testach i bezpieczeństwie aplikacji internetowych, to zdarzają się również testy penetracyjne infrastruktury sieciowej, często zresztą połączone z testami aplikacji internetowej korzystającej z danej infrastruktury. Jednym z pierwszych kroków w takich testach jest skanowanie portów. Czas skanowania (a właściwie jego minimalizacja) jest dość istotna w przypadku testów (lub nawet tylko skanowania z wykorzystaniem narzędzi automatycznych) dużej ilości adresów.
Jak o(d)głupić nmap
Jeśli skanowanie, to oczywiście nmap, takie jest przynajmniej pierwsze skojarzenie wielu osób. Wiem, że są inne narzędzia, ale żadne z nich nie zaoferowało mi czegoś, co skłoniłoby mnie do zmiany podstawowego narzędzia wykorzystywanego w trakcie skanowania portów i jak na razie pozostaje nim nmap właśnie. Wraz z pojawieniem się Zenmap osoby uczulone na CLI uzyskały dość przyjazne GUI, sam zresztą z niego korzystam do analizy rezultatów skanowania. Jak wygląda wydajność nmap i o co chodzi z tym ogłupianiem oraz odgłupianiem?
Najpierw jedno wyjaśnienie - wiadomo, że na wydajność skanowania znaczny wpływ ma sieć, jej przepustowość oraz RTT między skanującym i skanowanym. To, co chcę pokazać jednak nie jest związane z ogólnie pojętymi warunkami sieciowymi.
W przypadku skanowania w trybie SYN nmap wysyła pakiet SYN na testowane porty i sprawdza reakcję systemu, a możliwe reakcje mogą być trzy (mowa o podstawowych, w temat komunikatów ICMP się tu nie zagłębiam):
- przysłanie pakietu z ustawionymi flagami SYN i ACK w przypadku, gdy port jest otwarty,
- przysłanie pakietu z ustawioną flagą RST gdy port jest zamknięty,
- brak reakcji,
Teoretycznie najszybsze skanowanie ma miejsce wówczas, gdy host zwraca pakiet RST dla zamkniętych portów. Znów teoretycznie najwolniejszy jest przypadek gdy porty są filtrowane. Tradycyjnie teoria z praktyką nieco się rozmijają. Dla przykładu czas skanowania tego samego komputera (te same warunki sieciowe), przy czym zmienia się konfiguracja firewalla (pf). W każdym przypadku są cztery otwarte porty, reakcja na porty chronione przez firewall się zmienia. Skanowane są wszystkie porty, nie używam dodatkowych opcji modyfikujących timing.
- filtrowane porty (brak odpowiedzi) - czas skanowania: 112 sekund,
- blokowane porty (zwracana odpowiedź RST) - czas skanowania: 26 sekund,
Różnica między przypadkami jest przytłaczająca, ale wcale filtrowane porty nie są czymś najgorszym, co się może zdarzyć. Najgorsza jest przeplatanka, porty filtrowane oraz porty zwracające RST. W przypadku, gdy 99% portów zwraca RST i 1% jest filtrowanych, czas skanowania wynosi już 61 sekund, gdy filtrowanych jest 5% portów czas skanowania wynosi już 176 sekund(!).
Z czego to wynika? Nmap sam stara się dopasować do zmieniających warunków i czasami jest w tym zbyt zachowawczy. W szczególności istotne mogą być dwa parametry (i żaden z nich nie dotyczy RTT):
- max_successful_tryno,
- send delay,
Parametr max_successful_tryno określa ile razy sprawdzany jest dany port, a dokładniej ile razy wysyłany jest pakiet SYN na dany port, jeśli nie ma żadnej reakcji celu. Parametr send delay z kolei określa jak często wysyłane są pakiety (jakie jest opóźnienie między nimi). Wzrost wartości tych parametrów skutkuje bezpośrednio wydłużonym czasem skanowania, ponieważ po pierwsze wykonanych musi być więcej prób dla każdego filtrowanego portu, a po drugie ilość wysyłanych pakietów w jednostce czasu spada, spada więc również ilość wykonywanych w niej prób.
Teraz proste pytanie - co stanie się w przypadku, gdy skanowany host zostanie wyłączony w trakcie skanowania? Tak, nmap zgłupieje, zwiększy wartości wspomnianych parametrów, w rezultacie skanowanie zajmie potwornie dużo czasu. Jeśli taka sytuacja zaistnieje przy skanowaniu całej klasy (niech będzie C) adresów IP, czas wykonania tego zadania może wzrosnąć do wartości absolutnie nieakceptowalnej.
Co zrobić z tym problemem? Po pierwsze warto się zastanowić na ile istotne jest rozróżnienie między stanem closed i filtered, w szczególności, jeśli mowa o skanowaniu masowym. Teoretycznie wchodzą tu jeszcze przypadki specjalne, choćby wspomniany przeze mnie w Trochę schowane porty, ale przy "normalnym" skanowaniu w opisany kiedyś przeze mnie sposób też można jakieś usługi ukryć. Jeśli uznamy, że informacja, czy port jest zamknięty czy filtrowany nie jest informacją kluczową, można skorzystać z opcji --defeat-rst-ratelimit, której nazwa wzięła się stąd, że niektóre systemy ograniczają ilość pakietów RST wysłanych w ciągu sekundy, podobnie jak ma to czasem miejsce z komunikatami ICMP. Użycie tej opcji powoduje dużą różnicę, w przypadku z 5% filtrowanych portów czas skanowania wynosi 46 sekund, co w porównaniu z 176 sekundami jest zdecydowanie lepszym wynikiem. Poza tym warto ustalić ile maksymalnie warto czekać na skończenie się skanowania jakiegoś hosta i ustawić odpowiedni limit czasu korzystając z opcji --host-timeout.
Można też wykorzystać bardziej agresywne sposoby optymalizacji czasu testów, ale jak zwykle z optymalizacjami bywa, trzeba uważać, by nie przedobrzyć. W tym przypadku, by poprawa wydajności nie została osiągnięta kosztem zmniejszenia dokładności testu (dokładność w tym wypadku oznacza poprawną identyfikację portów otwartych).
Oddzielnym przypadkiem jest sytuacja, gdy trzeba skanować wszystkie porty wszystkich hostów w danej klasie, aktywne hosty mogą nie odpowiadać na ping i w dodatku (prawie) wszystkie porty są filtrowane. W takich przypadkach nmap może mieć spore problemy z ustaleniem odpowiedniej wartości RTT i czas skanowania może być nieakceptowalny. Ale to inny przypadek i nieco inne optymalizacje trzeba w nim zastosować.
To, co opisałem nie jest bynajmniej wiedzą tajemną, informacje te dostępne są tu: Timing and Performance. Tylko by dostępne opcje umiejętnie stosować, trzeba wiedzieć z czego wynika określone, być może niepożądane, zachowanie i jaka będzie najlepsza droga jego usunięcia.