Ochrona sieci, bezpieczeństwo systemów komputerowych - securitymag.pl
March 30, 2012, 5:36 pm

Blind Attack

1 Star2 Stars3 Stars4 Stars5 Stars (1 głosów, średnia: 5,00 / 5)
Loading ... Loading ...

Skanowanie komputerów i odnajdywanie działających na nich usług od zawsze było pasjonującym zajęciem, a techniki odkrywania tajemnic zdalnej maszyny ewoluowały od bardzo prostych tj. skanowania pakietami SYN, do bardzo finezyjnych – jaką jest choćby technika blind scanning.

Author: Konrad Malewski

Source: Hakin9 01/2008

O zaletach i celowości skanowania nikogo chyba nie trzeba przekonywać. Obecnie stosowanych technik skanowania jest naprawdę mnóstwo. Większość z nich jest zaimplementowana w programach NMAP i AMAP. Skanery mogą używać technik kamuflażu, które pozwalają na uniknięcie wykrycia. Niektórych z technik wykorzystanych w wymienionych skanerach można użyć do analizy połączeń ustanowionych pomiędzy dwoma komputerami, a nawet doprowadzić do ich rozłączenia.

Cel skanowania

Celem oczywiście jest poznanie usług działających na skanowanym komputerze. Większość technik skanowania opiera się na wysyłaniu pakietów testujących i analizie odpowiedzi. Takie podejście ma jednak wadę – na ogół ujawnia źródło skanowania – co niekoniecznie jest korzystne dla naszego bezpieczeństwa, a dodatkowo nic nie mówi o interesujących nas połączeniach atakowanej maszyny. Sprawa ma się nieco inaczej, jeżeli mamy możliwość podsłuchiwania ruchu związanego ze skanowanym komputerem. Możemy wtedy sfałszować adres źródłowy wysyłanych pakietów i analizować odpowiedzi. Jeżeli nadarzy się taka niezwykle rzadka okazja, jak podsłuchiwanie ruchu maszyny nienależącej do naszej sieci, to należy wiedzieć, że istnieją lepsze techniki skanowania, które dodatkowo nie ujawniają naszej ciekawości. Taką techniką jest pasywny skan/fingerprinting. Używając tej metody nie wysyłamy żadnych danych do skanowanego komputera, a jedynie analizujemy ruch przepływający od i do ofiary, co w większości przypadków zajmuje więcej czasu niż przy aktywnym skanowaniu. Korzystając z tej techniki poznanie wszystkich otwartych portów może być procesem długotrwałym, który zależy wyłącznie od aktywności maszyny w sieci. Dla przykładu – jeśli nikt nie skorzysta z usługi SSH, nigdy nie wykryjemy obecności tejże usługi. Mając do dyspozycji wymienione dwa sposoby, skanowanie aktywne i pasywne, należałoby wybrać pomiędzy prędkością a jakością. Aktywne skany dostarczają szybko dokładnych wyników w przeciwieństwie do pasywnego skanowania, na którego wyniki należy czekać niekiedy tygodniami. Pasywne skanowanie jest bezpieczniejsze. Natomiast aktywne najczęściej ujawnia adres źródłowy skanera.

Klasyczne skanowanie na ślepo

Skanowanie na ślepo (ang. blind scanning) jest inaczej zwane skanowaniem jałowym (ang. idle scanning). Technika ta została zaimplementowana w programie nmap i polega na wykorzystaniu trzeciego komputera do przeprowadzenia skanowania. Komputer, który zostanie użyty jako przykrywka powinien pozostawać w stanie bezczynności albo przynajmniej natężenie ruchu wychodzącego nie powinno fluktuować. Zanim przejdziemy do szczegółów, należy przeanalizować zachowanie stosów IP oraz TCP w niektórych przypadkach. Przypuścimy, że posiadamy skonfigurowaną sieć komputerów składającą się z trzech jednostek znajdujących się w tej samej sieci. Komputery te mają adresy 172.16.83.1, 172.16.83.128 oraz 172.16.83.129. Na pierwszym i ostatnim zainstalowany jest system Linux, a komputer z końcówką 128 działa pod kontrolą systemu Windows Vista. Naszym celem będzie przerwanie połączenia pomiędzy komputerem 172.16.83.129 (A) a komputerem 172.16.83.128 (B) dysponując komputerem 172.16.83.1(C). Nasze możliwości ograniczają się do fałszowania adresu źródłowego wychodzących pakietów. Przestawiamy kartę sieciową komputera C w tryb promisc, aby nie odbierać wyłącznie pakietów z adresem 172.16.83.1. Pomiędzy komputerami A i B nawiązane jest połączenie SSH (serwer działa na komputerze A). Obiektem naszego zainteresowania będzie flaga ID protokołu IP. Najpierw należy zaobserwować, jak systemy operacyjne ustalają wartość pola ID protokołu IP. Znaczenie badanych pól znajdziemy w ramce. Żeby dowiedzieć się, jak poszczególne systemy ustalają wartość pola ID, należy przeprowadzić prosty eksperyment polegający na wywołaniu na komputerze C polecenia przedstawionego na Listingu 2. Przyjrzyjmy się polu ID, które jest inkrementowane o jeden. Natomiast w systemach linuksowych jest tak, jak pokazuje Listing 3. Pole ID jest ustawione na zero.
Możemy spróbować użyć protokołu ICMP. W tym celu dodajemy przełącznik -1 do polecenia hping2. Powinniśmy zauważyć, że pola ID zarówno systemu Windows, jak i Linux są inkrementowane o jeden. Nie są to, tak jak się mogliśmy spodziewać, liczby zupełnie losowe. Należy przeprowadzić dodatkowy eksperyment. Z komputera A wysyłamy pakiety ICMP ECHO REQUEST do komputera B i vice versa, a potem przeprowadzamy ponownie eksperyment wysyłając pakiety z komputera C (patrz Listing 4). Szybki rzut oka na funkcję ip _ select _ ident jądra systemu Linux pozwoli nam odkryć tajemnicę tego zachowania. IP ID w Linuksie jest wybierane w sposób losowy i ma inne wartości dla komunikacji z poszczególnymi hostami i w obrębie poszczególnych połączeń. W momencie wybrania IP ID dla danego połączenia, ID kolejnych pakietów jest zwiększane o jeden. Systemy z rodziny Windows przechowują globalny licznik pakietów, który jest używany jako wartość pola ID w nagłówku IP. Dzięki temu mechanizmowi, można stwierdzić, czy dany host wysłał pakiet, czy nie. Jeśli pomiędzy kolejnymi odpowiedziami pole ID zwiększy się wyłącznie o jeden, oznacza to, że nasz pakiet nie spowodował wysłania odpowiedzi. Metoda skanowania za pomocą tej techniki praktycznie narzuca się sama. Skoro jesteśmy w stanie zbadać, czy dany host wysłał pakiet, czy nie, można to wykorzystać do badania reakcji komputera B na informacje, które wyśle mu komputer A. Można więc zastosować klasyczny SYN-SCAN fałszując adres źródłowy. Zastanawiając się, jakie informacje może odesłać A, dochodzimy do wniosku, że zachodzą dwa przypadki:

• jeżeli skanowany port jest otwarty, komputer odeśle pakiet z ustawionymi flagami SYN i ACK. Pakiet tak sformatowany spowoduje odesłanie przez B odpowiedzi z ustawioną flagą RST, ponieważ B nie zażądał nawiązania połączenia,
• jeżeli skanowany port jest zamknięty, komputer odeśle pakiet z ustawioną flagą RST lub w ogóle nic nie odeśle, jeżeli komputer jest odpowiednio skonfigurowany.

W przypadku A jesteśmy w stanie wykryć odesłanie odpowiedzi, natomiast w przypadku B jesteśmy w stanie wykryć jej brak. Skanowanie będzie więc polegało na wysłaniu pakietu testującego do B, pakietu ze sfałszowanym adresem źródłowym do A oraz ponownie pakietu testującego. Warto tutaj słowem komentarza opatrzyć dwie rzeczy: ograniczenia czasowe oraz implementację tej techniki w programie NMAP. Biorąc pod uwagę ograniczenia czasowe, należy zaważyć, że czas pomiędzy dwoma kolejnymi próbami nie może być krótszy niż RTT pomiędzy A oraz B powiększonym o czas odpowiedzi B (tak dla pewności). Jeżeli nasze próby będą zbyt częste, dojdzie do przekłamań w wynikach, ponieważ odpowiedź od A nie zdąży spowodować odesłania odpowiedzi przez B. Możliwa jest również optymalizacja tej techniki i minimalizacja liczby prób kierowanych z C do hosta zombie (czyli B). Program NMAP wysyła nie jeden, lecz N pakietów z adresem źródłowym równym B i docelowym A. Jeżeli żaden z portów nie jest otwarty, to kolejne testy IP ID powinny różnić się o N. Technika ta zaimplementowana jest w programie NMAP. Aby wykonać skanowanie tego typu, należy na komputerze C wydać polecenie przedstawione na Listingu 5.

Tabela 1. Początkowy rozmiar okna

Tabela 1. Początkowy rozmiar okna

Skanowanie portów wykorzystujące ARP

Skoro już mowa o skanowaniu ukrytym, nie sposób pominąć skanowania wykorzystującego pakiety żądania ARP. Technika ta również polega na zaobserwowaniu, które pakiety wzbudzają odpowiedź po dotarciu do ofiary, a które nie. Jednym z takich przypadków są pakiety z ustawioną flagą FIN. Wysłanie pakietu z ustawioną flagą FIN na otwarty port nie powinno spowodować wygenerowania odpowiedzi. Jeżeli port jest zamknięty, komputer odpowiada pakietem z ustawionymi flagami RST, ACK. W przypadku systemów z Redmond dzieje się inaczej, ponieważ nie ma znaczenia, czy port jest otwarty czy zamknięty – Windows zawsze odpowie na taki pakiet pakietem z ustawioną flagą RST, ACK. Można w celu skanowania wykorzystać również klasyczny SYN-SCAN, jeżeli na docelowym komputerze firewall odrzuca pakiety SYN skierowane do zamkniętych portów. Technika skanowania wymaga, aby skanowany komputer znajdował się w innej sieci niż podsieć skanera. Skanowanie jest ślepe, ponieważ wysyłając pakiety zmieniamy adres źródłowy pakietu na adres komputera nieistniejącego lub aktualnie niepracującego w naszej podsieci. Jeżeli skanowana maszyna odpowie, router łączący naszą sieć będzie próbował zamienić fałszywy adres IP z naszej podsieci na adres fizyczny przy pomocy protokołu ARP. Nie powinno to nastręczać szczególnych problemów, gdyż zapytania ARP wysyłane są do wszystkich komputerów w podsieci. Nasłuchując odpowiedzi ARP, skanujący komputer jest w stanie określić, czy testowany port jest otwarty, czy też nie.

Atak RST na ustanowione połączenie

A co, jeśli chcemy dla zestawionego połączenia przeprowadzić atak RST (np. podobny do tego, który został przedstawiony w hakin9 2/2007 przez Marcina Ulikowskiego)? Pozostaje jedynie technika pasywnego nasłuchiwania interesującego nas ruchu i wstrzyknięcie we właściwym momencie odpowiednio sformatowanego pakietu. Budowa protokołów TCP oraz IP oraz połączenie techniki aktywnego i pasywnego skanowania umożliwia częściowe obejście tego problemu. Kolejnym interesującym faktem – oprócz analizy IP ID – będzie zachowanie się hostów biorących udział w konwersacji na dodatkowe niespodziewane pakiety. Załóżmy, że ustanowiliśmy połączenie SSH z komputera A do komputera B. Po stronie komputera A został użyty port 49160 do połączenia się z komputerem B na port 22. Wysyłając pakiety z komputera C będziemy chcieli empirycznie sprawdzić, czy komputer A używa portu 49160. Czytając RFC nr 793, można się natknąć na interesującą sytuację opisaną na stronie 34. Autorzy RFC opisują w niej zachowanie protokołu TCP w przypadku, gdy jedna ze stron zgubiła połączenie. W RFC znajdziemy również opis zachowania w innych interesujących sytuacjach – kiedy host otrzyma pakiet należący do nieistniejącego połączania oraz kiedy dostanie pakiet SYN-ACK należący do połączenia. Zachowanie w wyżej wymienionych trzech sytuacjach można opisać następującym przykładem: komputer C wysyła pakiet z ustawionym adresem źródłowym komputera B i adresem docelowym komputera A. Port docelowy jest równy portowi usługi SSH, natomiast port źródłowy ustawiony jest na port, który chcemy odkryć (port X). Jedyną ustawiona flagą TCP jest SYN. Wysłanie takiego pakietu do B zgodnie z RFC spowoduje odesłanie w kierunku źródła pakietu z ustawionymi flagami SYN-ACK jeżeli pakiet nie należał do konwersacji oraz ACK przeciwnym przypadku. Po dotarciu pakietu do A możliwe są dwie sytuacje. Jeśli pakiet należy do konwersacji A-B i zawiera jedynie flagę ACK, nic nie jest odsyłane. Pakiet nienależący do żadnego połączenia powoduje podjęcie przez komputer A próby zerwania nieistniejącego połączenia A-B pakietem z ustawianą flagą RST. Przechwytując ruch programem tcpdump, powinniśmy uzyskać coś podobnego do zawartości Listingu 6. Znaczenie poszczególnych pól jest następujące:

• czas IP adres źródłowy.port –> adres docelowy.port: flagi, SEQ _ P:SEQ _ O (liczba bajtów) [ack ACK] rozmiar okna ,

• pole SEQ _ P:SEQ _ O określa, jakie są numery sekwencyjne przesyłanych bajtów. Test polegał na wysłaniu czterech pakietów: dwóch należących do połączenia A-B oraz dwóch, które do niego nie należały. Prosty program wykrywający port użyty przez maszynę A przedstawiony jest na Listingu 7. Przykładowe wywołanie programu ma postać: ./discover _ ports.pl -s 172.16.83.1 -d 172.16.83.129 -sp 22 -vi 172.16.83.128 Program zwróci w miarę dokładną listę portów źródłowych użytych w komunikacji A-B: Found ports: 45474 45730 49160 51885 W celu przeprowadzenia udanego ataku RST należy jeszcze zdobyć dodatkową informację. A mianowicie numery sekwencyjne użyte dla połączenia przez komputer A lub B. Do odkrycia są de facto dwie liczby – liczniki bajtów w połączeniu serwera SSH oraz klienta. W celu sprawdzenia jednego z owych liczników zastosujemy ponownie sztuczkę z IP ID. Najpierw należy jednak zrozumieć, do czego służą flagi ACK I SEQ protokołu TCP. Załóżmy, że host B otrzymuje pakiet. Pole SEQ tego pakietu jest licznikiem bajtów komputera A i powinno się mieścić w tzw. oknie odbiorczym komputera odbierającego wiadomość (w tym przypadku A). Okno odbiorcze jest wartością ograniczającą liczbę pakietów, które – w analizowanym przypadku – może na raz odebrać host A. Pole ACK pakietu pochodzącego od B określa, który pakiet wysłany z A został poprawnie odebrany i przetworzony. Aby to zobrazować, prześledźmy przykładową komunikację C z A. Na komputerze C została wydana komenda telnet A. Część wymiany pakietów pokazuje Listing 8., w którym dla uproszczenia usunięto niektóre dane drukowane przez tcpdump. W celu ustanowienia połączenia wymieniane są pakiety od numeru 1 do 3. Jest to klasyczny przykład trzystopniowego uzgadniania numerów sekwencyjnych. Warto jednak zauważyć, że pakiety z ustawioną flagą SYN (numer sekwencyjny= 3157436274) druga strona potwierdza numerem ACK (pakiet numer 2) o jeden większym (3157436275) – tak, jakby poprzedni pakiet niósł jeden dodatkowy bajt danych. Daje to nam pewność dostarczenia numerów synchronizacyjnych do drugiej strony. Komunikacja C->A zaczyna się od pakietu numer 4 świadczącego o wysłaniu 27 bajtów danych. Bajty te są potwierdzane przez A w pakiecie numer 5, w którym pole ACK jest równe polu SEQ pakietu numer 4 powiększonemu o 27 bajtów danych. Pakiet numer 6 jest odpowiedzią hosta A składającą się z 3 bajtów danych. Odpowiedź hosta A jest potwierdzana w pakiecie numer 7. I tak dalej. Dzięki takiemu mechanizmowi oba komputery posiadają wiedzę o danych, które dotarły do drugiego komputera i danych, które wymagają retransmisji. Naszym celem będzie wysłanie pakietu, który będzie miał licznik tak ustawiony, aby cały pakiet został zinterpretowany przez ofiarę jako poprawny. Na pierwszy rzut oka wydaje się, że należy bardzo dokładnie odgadnąć oba te liczniki. Dla celu przeprowadzania ataku RST na system Vista jest to prawda, jednak odkrycie przedziału numerów akceptowanych przez B nie wymaga już takiej precyzji. Gdyby nie był dopuszczony pewien margines błędu, pakiety, które przyszłyby w złej kolejności, byłyby odrzucane – znacząco degradując prędkość połączenia. Aby umożliwić odbieranie pakietów, które przychodzą w różnej kolejności, protokół TCP wprowadził pojęcie okna. De facto istnieją trzy okna: nadawcze, unikania zatoru (tzw. congestion window) i odbiorcze. Oknem congestion nie będziemy się zajmować. Okno nadawcze związane jest poniekąd z oknem congestion, natomiast okno odbiorcze określa, ile dany host może przyjąć danych na raz (lub patrząc od strony nadawcy – ile może on wysłać danych, które odbiorca jest w stanie przetworzyć). Rozmiar okna odbiorczego jest komunikowany nadawcy podczas ustanawiania połączenia i modyfikowany w zależności od warunków panujących w sieci. Służy do tego celu specjalne, 16-bitowe pole protokołu TCP o nazwie window i znajdujące się 208 bitów od początku nagłówka TCP. Łatwo zauważyć, że 16 bitów ogranicza maksymalną wielkość okna do 65535, co jest niewystarczające przy osiągach obecnych sieci. Z pomocą przychodzi mechanizm skalowania okna, którego dane – jako dodatkowe pole – doklejane są do nagłówka w fazie ustalania połączenia. Pozwala on przeskalować okno o maksymalnie 2^14 bajtów powodując, że dopuszczalny rozmiar okna urasta do 1GB. Duże znaczenie ma również domyślny rozmiar okna, który jest inny dla każdego z systemów. Przykładowe rozmiary okien pokazane są w Tabeli 1. Warto również wiedzieć, że Windows Vista używał w przeprowadzonym teście współczynnika skalowania okna równego 8 – co oznacza, że rozmiar okna jest równy 8192 pomnożone przez 256 (2^8), natomiast współczynnik skalowania okna w systemie Linux wynosił od 2 do 4. Oczywiście nie można zakładać, że jest to regułą. Jak wcześniej zostało wspomniane, numer sekwencyjny komputera A zostanie odkryty poprzez testowanie IP ID komputera z zainstalowanym systemem Windows Vista. Aby uzmysłowić sobie, jak tego dokonać, należy zaobserwować, co się dzieje w przypadku, gdy dany host otrzyma pakiet z numerem SEQ spoza okna odbiorczego (patrz Listing 9). Host B wysyła numer sekwencyjny ostatnio odebranego bajtu. Istotne jest to, że wysyłając zwiększa licznik IP ID, który możemy odczytać poznaną wcześniej metodą. Jeżeli numer SEQ jest w oknie odbiorczym hosta B, to nie odeśle on nam żadnej odpowiedzi. Warto nadmienić, że wykrywanie numerów sekwencyjnych jest procesem szybszym niż wykrywanie portów, ponieważ wysyłając pakiety do B nie musimy się przejmować RTT pomiędzy A oraz B. Nie musimy ponadto przeszukiwać pełnego zakresu (czyli od 0 od 2^32-1), ponieważ host B uzna za poprawny numer sekwencyjny z zakresu, którego się spodziewa. Można więc przyjąć pewien początkowy rozmiar okna równy N=64K i przeszukać cały zakres numerów sekwencyjnych zwiększając SEQ o 64K. Jeżeli nie uda się nam znaleźć okna odbiorczego, to – jak sugeruje jedno ze źródeł – ustalamy SEQ=N/2+I*N dla każdego I. Jeżeli w dalszym ciągu nie znajdziemy okna, ustalamy SEQ=N/4+I*N/2 i postępujemy podobnie. Jeżeli uda nam się wpaść w okno odbiorcze hosta B, z łatwością możemy odkryć dokładny numer sekwencyjny B używając bisekcji, tak jak to jest pokazane na Rysunku 2. Odkrywszy numer SEQ hosta A, możemy przeprowadzić atak RST. RFC 793 dokładnie określa wymagania i zachowanie stosu TCP w przypadku odebrania pakietu z ustawioną flagą RST. A mianowicie, we wszystkich stanach oprócz początkowego (SYN-SENT) odebrane pakiety z flagą RST muszą mieć poprawny numer sekwencyjny. Numer sekwencyjny dla tych pakietów jest poprawny, gdy SEQ znajduje się w przedziale okna odbiorczego. W fazie SYN-SENT RST jest akceptowane, jeśli pole ACK potwierdza wcześniej wysłany pakiet z flagą SYN. Microsoft interpretuje RFC 793 na własny sposób i wymaga, aby pole SEQ pakietu resetującego połączenie było równe wartości oczekiwanej, co poniekąd jest bezpieczniejsze – gdyż teoretycznie host A powinien móc zresetować połączenie i zna dokładną wartość SEQ. Z interpretacją RFC nie ma natomiast problemów system A, więc gdyby z resetowaniem połączenia były problemy, to można spróbować w pełni ślepego ataku RST na maszynę linuksową. W badanym przypadku rozmiar okna został po ustaleniu połączenia ustawiony na 2352 przy współczynniku skalowania równym 2, co dało rozmiar okna 10128 bajtów. Przejrzenie całego zakresu 2^32 numerów sekwencyjnych wymaga w tym przypadku wysłania około 424068 pakietów. Przy założeniu, że typowy pakiet ma 40 bajtów, wymagałoby to wysłania 16962720 bajtów. Zakładając typowe łącze o prędkości 256 Kbit/s, zresetowanie połączenia może zająć w najgorszym przypadku 8,7 minuty.

Rysunek 1. Badanie poprawności portu źródłowego

Rysunek 1. Badanie poprawności portu źródłowego

Atak w pełni ślepy

Uważny czytelnik pewnie dostrzegł, że w pełni ślepy atak jest możliwy do zrealizowania w sprzyjających warunkach. Przy oknie odbiorczym rzędu 1GB wystarczą cztery próby, aby zresetować dane połączenie. Problem może sprawić odkrycie portów źródłowych i docelowych. Jednak i one są w pewnym stopniu przewidywalne (patrz Tabela 2). Mając do dyspozycji zakresy i zakładając 4 próby na rozłączenie połączenia, można w optymistycznym przypadku doprowadzić do awarii połączenia w mniej niż 9 minut. Oczywiście w większości przypadków nie ma się co łudzić – pomyślny atak w tak krótkim czasie nie powiedzie się.

Tabela 2. Porty źródłowe używane przez poszczególne systemy operacyjne

Tabela 2. Porty źródłowe używane przez poszczególne systemy operacyjne

Cała reszta

Należy zdać sobie sprawę, że artykuł opisuje tylko niektóre możliwości, jakie niosą ze sobą ślepe ataki. Skanowanie oraz ataki RST są przysłowiowym wierzchołkiem góry lodowej, pod którą kryje się wiele możliwości. Wierny czytelnik hakin9 od razu powinien zauważyć fakt, że wprowadzanie własnych informacji do ustanowionego połączenia umożliwia zastosowanie większości ataków we wszystkich warstwach, począwszy od warstwy IP. Dzięki wiedzy zdobytej podczas skanowania można również przyśpieszyć atak ICMP opisywany w hakin9 3/2007 przez Marcina Ulikowskiego, nie mówiąc już możliwości wykorzystania pluginu własnej produkcji w Metasploit (patrz: hakin9 4/2007).

Rysunek 2. Dokładne znajdowanie numeru sekwencyjnego

Rysunek 2. Dokładne znajdowanie numeru sekwencyjnego

Podsumowanie

Warto uzmysłowić sobie, dlaczego ślepe ataki są możliwe. Wszędzie tam, gdzie występują przewidywalne liczby, pojawia się niebezpieczeństwo wykorzystania tego faktu. Ponadto same protokoły wykorzystywane w Internecie oraz ich pełna lub częściowa zgodność z RFC stanowi niejakie zagrożenie, ponieważ stan wiedzy o bezpieczeństwie znacznie się zwiększył od czasu sformalizowania standardów internetowych. Istnienie technik opisanych w artykule zawdzięczamy dokładnemu poznaniu zarówno protokołów, jak i ich słabości. Świadomość zagrożenia istnieje i zewsząd pojawiają się inicjatywy, które mają na celu niwelacje zagrożenia, jak np. grsecurity czy inne łatki jądra lub chociażby zmiany stosu TCP/IP w Windows Vista. Z drugiej strony jednak należy spojrzeć na zamysł TCP/IP z dużym uznaniem. Rozwiązania te, niekiedy powstałe 30 lat temu, są codziennie wykorzystywane przez miliony użytkowników. Gdyby technologię TCP/IP porównać do motoryzacji, od razu można sobie wyobrazić bezpieczeństwo na drodze, po której poruszają się trzydziestoletnie rzęchy.

_______________
Konrad Malewski,
absolwent informatyki Politechniki Śląskiej. Obecnie doktorant informatyki na AGH. Administrator amatorskich sieci komputerowych. Zarówno w pracy, jak i prywatnie interesuje się programowaniem oraz bezpieczeństwem aplikacji sieciowych. Kontakt z autorem: kmalewski@gmail.com

Listing 1. Fragmentacja pakietu o wielkości 5000B
IP (id 2067, offset 0, length 1500) 172.16.83.1 > 172.16.83.129: ICMP echo
request, id 49943, seq 1, length 1480
IP (id 2067, offset 1480, length 1500) 172.16.83.1 > 172.16.83.129: icmp
IP (id 2067, offset 2960, length 1500) 172.16.83.1 > 172.16.83.129: icmp
IP (id 2067, offset 4440, length 588) 172.16.83.1 > 172.16.83.129: icmp
Listing 2. Badanie sekwencji IP ID systemu Windows Vista
#hping2 172.16.83.128 -r -c 5
HPING 172.16.83.128 (eth0 172.16.83.128): NO FLAGS are set, 40 headers + 0
data bytes
len=40 ip=172.16.83.128 ttl=128 DF id=348 sport=0 flags=RA seq=0 win=0 rtt=0.2
ms
len=40 ip=172.16.83.128 ttl=128 DF id=+1 sport=0 flags=RA seq=1 win=0 rtt=0.2
ms
len=40 ip=172.16.83.128 ttl=128 DF id=+1 sport=0 flags=RA seq=2 win=0 rtt=0.2
ms
len=40 ip=172.16.83.128 ttl=128 DF id=+1 sport=0 flags=RA seq=3 win=0 rtt=0.3
ms
len=40 ip=172.16.83.128 ttl=128 DF id=+1 sport=0 flags=RA seq=4 win=0 rtt=0.2
ms
Listing 3. Badanie sekwencji IP ID systemu Linux
#hping2 172.16.83.129 -r -c 5
HPING 172.16.83.129 (eth0 172.16.83.129): NO FLAGS are set, 40 headers + 0
data bytes
len=40 ip=172.16.83.129 ttl=64 DF id=0 sport=0 flags=RA seq=0 win=0 rtt=0.5 ms
len=40 ip=172.16.83.129 ttl=64 DF id=+0 sport=0 flags=RA seq=1 win=0 rtt=0.1
ms
len=40 ip=172.16.83.129 ttl=64 DF id=+0 sport=0 flags=RA seq=2 win=0 rtt=0.1
ms
len=40 ip=172.16.83.129 ttl=64 DF id=+0 sport=0 flags=RA seq=3 win=0 rtt=0.1
ms
len=40 ip=172.16.83.129 ttl=64 DF id=+0 sport=0 flags=RA seq=4 win=0 rtt=0.2
ms
Listing 4. Wpływ pakietów wysyłanych przez poszczególne hosty na
używane IP ID
#hping2 172.16.83.128 -1 -r -c 5
HPING 172.16.83.128 (eth0 172.16.83.128): icmp mode set, 28 headers + 0 data
bytes
len=28 ip=172.16.83.128 ttl=128 id=1372 icmp_seq=0 rtt=0.2 ms
len=28 ip=172.16.83.128 ttl=128 id=+3 icmp_seq=1 rtt=0.2 ms
len=28 ip=172.16.83.128 ttl=128 id=+3 icmp_seq=2 rtt=0.3 ms
len=28 ip=172.16.83.128 ttl=128 id=+3 icmp_seq=3 rtt=0.2 ms
len=28 ip=172.16.83.128 ttl=128 id=+3 icmp_seq=4 rtt=0.3 ms
hping2 172.16.83.129 -1 -r -c 5
HPING 172.16.83.129 (eth0 172.16.83.129): icmp mode set, 28 headers + 0 data
bytes
len=28 ip=172.16.83.129 ttl=64 id=6967 icmp_seq=0 rtt=0.2 ms
len=28 ip=172.16.83.129 ttl=64 id=+1 icmp_seq=1 rtt=0.9 ms
len=28 ip=172.16.83.129 ttl=64 id=+1 icmp_seq=2 rtt=0.1 ms
len=28 ip=172.16.83.129 ttl=64 id=+1 icmp_seq=3 rtt=0.1 ms
len=28 ip=172.16.83.129 ttl=64 id=+1 icmp_seq=4 rtt=0.5 ms

Listing 5. Uruchomienie skanera NMAP z opcją Idle Scanning
#nmap -sI 172.16.83.128 172.16.83.129
WARNING: Many people use -P0 w/Idlescan to prevent pings from their true IP.
On the other hand, timing info Nmap gains from pings
can allow for faster, more reliable scans.
Starting Nmap 4.20 ( http://insecure.org ) at 2007-10-01 04:46 CEST
Idlescan using zombie 172.16.83.128 (172.16.83.128:80); Class: Incremental
Interesting ports on 172.16.83.129:
Not shown: 1694 closed|filtered ports
PORT STATE SERVICE
22/tcp open ssh
23/tcp open telnet
113/tcp open auth
MAC Address: 00:30:4F:A1:65:0A
Nmap finished: 1 IP address (1 host up) scanned in 16.124 seconds
Listing 6. Wpływ poprawnych i niepoprawnych pól portów źródłowych
na wysyłane dane
02:02:57.810075 IP 172.16.83.128.49160 > 172.16.83.129.ssh: S 1218938372:
1218938372(0) win 512
02:02:57.810253 IP 172.16.83.129.ssh > 172.16.83.128.49160: . ack 1768219816
win 4512
02:02:59.392821 IP 172.16.83.128.49160 > 172.16.83.129.ssh: S 605303214:
605303214(0) win 512
02:02:59.392933 IP 172.16.83.129.ssh > 172.16.83.128.49160: . ack 1 win 451202:03:03.406027 IP 172.16.83.128.49161 > 172.16.83.129.ssh: S 1540719811:
1540719811(0) win 512
02:03:03.406145 IP 172.16.83.129.ssh > 172.16.83.128.49161: S 4169466566:
4169466566(0) ack 1540719812 win 5840
02:03:03.406466 IP 172.16.83.128.49161 > 172.16.83.129.ssh: R 1540719812:
1540719812(0) win 0
02:03:04.314936 IP 172.16.83.128.49161 > 172.16.83.129.ssh: S 1240411569:
1240411569(0) win 512
02:03:04.315065 IP 172.16.83.129.ssh > 172.16.83.128.49161: S 4170366716:
4170366716(0) ack 1240411570 win 5840
02:03:04.315275 IP 172.16.83.128.49161 > 172.16.83.129.ssh: R 1240411570:
1240411570(0) win 0
Listing 7a. Program, który odkryje porty używane w komunikacji A-B
#!/usr/bin/perl
use Net::Pcap;
use Net::RawIP;
use NetPacket::Ethernet qw(:strip);
use NetPacket::ICMP;
use Getopt::Long qw(GetOptions);
use NetPacket::IP;
use FileHandle;
use threads;
use threads::shared;
use Time::HiRes;
STDOUT->autoflush(1);
my $my_ip,$server_ip,$server_port,$victim_ip;
my $opt=1;
my $promisc = 1;
my $snaplen = 68;
my($err,$net,$mask,$dev,$filter_t);
my @probes_array;
share ( @probes_array );
GetOptions(
"s=s"=> \$my_ip, "d=s" => \$server_ip, "sp=s"=>\$server_port, "vi=s"=>\
$victim_ip,
) or uzycie();
uzycie() if not $server_ip;
my $dev = rdev($server_ip);
my $filter = "src host $victim_ip";
my $my_id_cnt=0;
my $my_id_prb_cnt=-1;
my @ports_to_scan = ( [0,65535] );
my @found_ports;
share(@found_ports);
my $thread = threads->create('start_probing', undef);
die if (not defined $thread);
if ( (Net::Pcap::lookupnet($dev, \$net, \$mask, \$err) ) == -1 ) {
die "Net::Pcap::lookupnet failed. Error was $err";
}
my $pcap_t = Net::Pcap::open_live($dev, $snaplen, $promisc, $to_ms, \$err);
$pcap_t || die "Can't create packet descriptor. Error was $err";
if ( Net::Pcap::compile($pcap_t, \$filter_t, $filter, $opt, $net) == -1 ) {
die "Unable to compile filter string '$filter'\n";
}
Net::Pcap::setfilter($pcap_t, $filter_t);
Net::Pcap::loop($pcap_t, -1, \&callback, \@x);
sub start_probing {
my $rtt_tuned = 0.20;
my $outcond=0;
print "RTT autotune. Starting from: $rtt_tuned \n";
while ($outcond==0)
{
local $i=0;
for ($client_port=1;$client_port<100;$client_port++){ send_probe(); send_testing_packet(); Time::HiRes::sleep($rtt_tuned); } { lock (@found_ports); if (scalar(@found_ports)>=5)
{
$rtt_tuned = $rtt_tuned * 2;
$outcond = 1;
last;
}
undef @found_ports;

Listing 7b. Program, który odkryje porty używane w komunikacji A-B
}
$rtt_tuned = $rtt_tuned / 2;
print "RTT: coud do beter: trying $rtt_tuned \n";
if ($rtt_tuned <= 0.005 )
{
$outcond = 1;
last;
}
}
undef @found_ports;
print "RTT established at: $rtt_tuned\n";
sleep(1);
while (scalar(@ports_to_scan)!=0)
{
my ($port_begin,$port_end) = @{pop(@ports_to_scan)};
print "Scanning from/to: $port_begin,$port_end \n";
for ($client_port=$port_begin;$client_port<=$port_end;$client_port++) { send_probe(); send_testing_packet(); Time::HiRes::sleep ($rtt_tuned) if ($rtt_tuned>0.0005);
}
}
print "Found ports: @found_ports\n";
}
sub send_probe {
my $rawip = Net::RawIP->new({icmp =>{}});
$rawip->set({
ip => { saddr => $my_ip,
daddr => $victim_ip,
},
icmp => { type => 8,
id => NetPacket::htons($my_id_cnt),
sequence => NetPacket::htons($my_id_cnt),
data => timem()
}
});
$rawip->send();
{
lock(@probes_array);
push (@probes_array,$my_id_cnt);
}
$my_id_cnt=0 if (++$my_id_cnt>=65536);
}
sub send_testing_packet
{
my $rawip = Net::RawIP->new({
ip => {saddr => $victim_ip,
daddr => $server_ip
},
tcp=> {dest => $server_port,
source => $client_port,
psh => 1,
syn => 1}});
$rawip->send();
{
lock(@probes_array);
push(@probes_array, $client_port);
}
if ($client_port % 100 == 0 )
{
print "Probed client port: $client_port\n";
}

Listing 7c. Program, który odkryje porty używane w komunikacji A-B
}
sub received_probe_pacet
{
my ($raw_data) = @_;
my $id,$dest_port;
my $id,$port_scanned,$last_id;
$ip = NetPacket::IP->decode(eth_strip($raw_data));
my ($icmp_type,$icmp_code,$icmp_chsum,$icmp_id,$icmp_seqnum,$icmp_data) =
unpack("CCnnna*",$ip->{data});
{
lock(@probes_array);
if (scalar(@probes_array)>=3)
{
$id = shift (@probes _ array);
$dest_port = shift (@probes_array);
$last_id = $probes_array[0];
if ($last_id!=$icmp_id)
{
$outofsync=1;
while ((scalar(@probes_array)>=3) && ($outofsync==1))
{
$id = shift (@probes_array);
$dest_port = shift (@probes_array);
$last_id = $probes_array[0];
if ($last_id==$icmp_id)
{
$outofsync=0;
}
}
}
}
}
if ($my_id_prb_cnt == $ip->{id})
{
push @found_ports,$dest_port;
}
$expected_ip_id = $icmp_id + 1;
$my_id_prb_cnt=$ip->{id};
$my_id_prb_cnt++;
}
sub analyze_packet
{
my ($raw_data) = @_;
$ip = NetPacket::IP->decode(eth_strip($raw_data));
if ($ip->{proto} == NetPacket::IP::IP_PROTO_ICMP)
{
$icmp = NetPacket::ICMP->decode(NetPacket::IP::strip($raw_data));
if ($icmp->{code}==ICMP_ECHOREPLY)
{
received_probe_pacet($raw_data);
}
}
}
sub callback {
my ($args,$header,$packet) = @_;
analyze_packet($packet);
}
sub uzycie {
print "$0 -s my-ip -d server-ip -sp server_port -vi victim-ip\n";
exit -1;

Listing 8. Szczegóły przykładowej komunikacji C<->A
IP (id 56121, length 60) 172.16.83.1.45462 > 172.16.83.129.23: S, 3157436274:
3157436274(0)
IP (id 0, length 60) 172.16.83.129.23 > 172.16.83.1.45462: S, 1617613530:
1617613530(0) ack 3157436275
IP (id 56122, length 52) 172.16.83.1.45462 > 172.16.83.129.23: ., ack
1617613531
IP (id 56123, length 79) 172.16.83.1.45462 > 172.16.83.129.23: P, 3157436275:
3157436302(27) ack 1617613531
IP (id 7199, length 52) 172.16.83.129.23 > 172.16.83.1.45462: ., ack
3157436302
IP (id 7200, length 55) 172.16.83.129.23 > 172.16.83.1.45462: P, 1617613531:
1617613534(3) ack 3157436302
IP (id 56124, length 52) 172.16.83.1.45462 > 172.16.83.129.23: ., ack
1617613534
IP (id 56125, length 55) 172.16.83.1.45462 > 172.16.83.129.23: P, 3157436302:
3157436305(3) ack 1617613534

Listing 9. Reakcja systemu Windows Vista na pakiet z nieprawidłowym
numerem sekwencyjnym
Wywołanie polecenia z C
./hping2 172.16.83.128 -a 172.16.83.129 -s 22 -p 49165 -M 0 -c 5
spowoduje odesłanie odpowiedzi przez B:
IP (ttl 128, id 393, offset 0, flags [DF], proto TCP (6), length 40)
172.16.83.128.49165 > 172.16.83.129.22: ., ack
200270774 win 253
IP (ttl 128, id 394, offset 0, flags [DF], proto TCP (6), length 40)
172.16.83.128.49165 > 172.16.83.129.22: ., ack
200270774 win 253
IP (ttl 128, id 395, offset 0, flags [DF], proto TCP (6), length 40)
172.16.83.128.49165 > 172.16.83.129.22: ., ack
200270774 win 253
IP (ttl 128, id 396, offset 0, flags [DF], proto TCP (6), length 40)
172.16.83.128.49165 > 172.16.83.129.22: ., ack
200270774 win 253
IP (ttl 128, id 397, offset 0, flags [DF], proto TCP (6), length 40)
172.16.83.128.49165 > 172.16.83.129.22: ., ack
200270774 win 253

Komentarze zablokowane.

EN
PL
FR
DE


Software Press Sp. z o.o. Sp. Komandytowa 02-682 Warszawa, ul. Bokserska 1, NIP 9512279582, REGON 141804060, KRS: 0000327578