Wyobraźcie sobie, że chcecie przetestować funkcjonalność lub zreprodukować błąd, do czego potrzebujecie próbkę wysłanego ruchu jednak, takowej nie posiadacie. Załóżmy, że potrzebujecie wygenerować jakiś specyficznie wyglądający pakiet, ponieważ podejrzewacie błąd programowy. Albo, że chcecie przetestować mechanizmy antywirusowe lub antymalware bez ryzyka wpuszczenia rzeczywistego zagrożenia do własnej sieci. Możliwość wpuszczenia do waszej sieci dowolnego pakietu, o ustalonej przez was strukturze i zawartych w nim danych bez oglądania się na wymogi aplikacyjne. Narzędzie spełniające takie funkcje istnieje i nazywa się scapy.

Scapy to w skrócie generator pakietów. Nie należy go mylić z generatorem ruchu, którego używamy w testach obciążeniowych infrastruktury. Generator ruchu służy nam do wygenerowania określonego wolumenu pakietów o ustalonej strukturze analizy poprawności jego przesłania przez naszą infrastrukturę. Scapy służy do wygenerowania pakietu o dowolnej konstrukcji, który następnie wpuścimy do naszej infrastruktury, aby przetestować jej zachowanie w przygotowanym przez nas ściśle określonym scenariuszu. Pozwala on także na modyfikowanie, zarówno nagłówków, jak i zawartości przechwyconego już przez nas wcześniej pakietu.

Jak zainstalować scapy?

Scapy do poprawnego działania wymaga zainstalowanego Pythona. W zależności od wybranej przez nas wersji musi być to Python 2.7 lub co najmniej Python 3.4. Używany przez Was system operacyjny nie powinien mieć większego znaczenia, gdyż scapy działa zarówno na systemach Linux, BDS, MacOS czy Windows. Aby wykorzystać pewne dodatkowe funkcjonalności programu, na przykład te związane z generowaniem raportów lub diagramów, oprócz pakietu samej aplikacji musimy zainstalować kilka dodatkowych bibliotek Pythona. Instalując scapy na urządzeniach z systemem operacyjnym Linux niezbędne jest też zainstalowanie tcpdump. Pakiet ten jest domyślnym składnikiem obecnie większości dystrybucji. Dostępny jest także w systemie MacOS. W systemach Windows zainstalować musimy npcap. Producenci w dokumentacji zaznaczają, że winpcap powinno również działać poprawnie, ja jednak miałem z tym w przeszłości problemy.

Samo scapy możemy zainstalować na kilka sposobów. Pierwszym z nich jest sklonowanie programu bezpośrednio ze strony projektu na GitHub (https://github.com/secdev/scapy) poleceniem:

git clone https://github.com/secdev/scapy.git.  

Uruchomimy w ten sposób najnowszą, developerską wersję programu, która może nie być pozbawiona błędów. Z repozytorium pobrać możemy też jedno z oficjalnych wydań. W chwili obecnej najnowsza wersja oznaczona jest numerem 2.4.5 i pochodzi z kwietnia 2021. Wersję z repozytorium możemy uruchomić bez konieczności jej instalacji za pomocą skryptu, który znajduje się w głównym folderze projektu:

run_scapy (lub run_scapy.bat w przypadku instalacji w systemie Windows)

Jeżeli chcemy w naszym systemie zainstalować tak pobrany program musimy wydać polecenie

python setup.py install.

Komendę należy wydać z konta z uprawnieniami root-a.

Znacznie wygodniejszym sposobem instalacji jest jednak skorzystanie z pakietów dostarczonych wraz z dystrybucją Linuksa lub repozytorium PyPI. Jeżeli korzystasz z systemu MacOS to pamiętaj, że scapy dostępny jest zarówno w MacPorts jak i Homebrew. Niestety na systemach MacOS w wersji 12.x możemy mieć problem z zainstalowaniem części dodatkowych bibliotek Pythona takich, jak choćby matplotlib.

Generowanie pakietu

Zacznijmy od tego, że pojęcie pakietu w scapy jest trochę inne, niż to, które znamy z typowej definicji sieciowej. Pakiet w scapy to każdy wygenerowany przez aplikację obiekt danych. Obiekty danych są zaprogramowanymi w aplikacji strukturami. Przykładowo, obiektem jest dobrze znany nam pakiet IP, ale ramka Ethernet, komunikat protokołu RADIUS, pakiet tunelu GRE i inne. Listę wszystkich pakietów zaimplementowanych w zainstalowanej wersji możemy wyświetlić poleceniem:

 ls().

Polecenie to nie bez powodu kończy się nawiasami, czym tak naprawdę przypomina wywołanie funkcji w języku programowania Python. Używanie scapy nawet z linii poleceń przypomina programowanie i wymaga od nas budowania pakietu, czyli ustalenia jego atrybutów, tak jakbyśmy programowali obiektowo w Pythonie. Na szczęście do tego wystarczy nam minimalna, bardzo podstawowa wiedza związana z tym językiem, a nawet osoby, które nigdy w nim nie programowały powinny spokojnie opanować podstawy.

Każdy obiekt, który stworzymy zawiera szereg atrybutów. Przykładowo atrybutami pakietu IP są pola występujące w nagłówku rzeczywistego pakietu opisanego w RFC. Atrybuty pakietu wyświetlimy za pomocą polecenia:

 ls(IP).

>>> ls(IP)

version    : BitField (4 bits)                   = (4)

ihl        : BitField (4 bits)                   = (None)

tos        : XByteField                          = (0)

len        : ShortField                          = (None)

id         : ShortField                          = (1)

flags      : FlagsField (3 bits)                 = (0)

frag       : BitField (13 bits)                  = (0)

ttl        : ByteField                           = (64)

proto      : ByteEnumField                       = (0)

chksum     : XShortField                         = (None)

src        : SourceIPField (Emph)                = (None)

dst        : DestIPField (Emph)                  = (None)

options    : PacketListField                     = ([])

Postępując tak samo jakbyśmy programowali w Pythonie, aby stworzyć własny pakiet musimy najpierw stworzyć jego reprezentację w postaci zmiennej.

>>> p=IP()

Aktualnie przypisane parametry atrybutów wchodzących w skład obiektu wyświetlimy korzystając z wywołanej dla danego obiektu metody

 show()

>>> p.show()

###[ IP ]###

  version= 4

  ihl= None

  tos= 0x0

  len= None

  id= 1

  flags=

  frag= 0

  ttl= 64

  proto= hopopt

  chksum= None

  src= 127.0.0.1

  dst= 127.0.0.1

  \options\

I jak widać niektóre wartości są już automatycznie wypełnione. Możemy je w prosty sposób modyfikować. Przykładowo, jeżeli chcemy zmienić wartość adresu nadawcy i odbiorcy wystarczy, że wywołamy poniższe polecenia:

>>> p.src=’172.16.8.200′

>>> p.dst=’172.16.8.201′

Wartości zmienionych parametrów w prosty sposób wyświetlimy wywołując dany obiekt:

>>> p

<IP  src=172.16.8.200 dst=172.16.8.201 |>

Aby wysłać pakiet musimy wywołać metodę

send().

>>> send(p)

WARNING: Mac address to reach destination not found. Using broadcast.

.

Sent 1 packets.

W tym miejscu warto zwrócić uwagę na wielką elastyczność, jaką w konstruowaniu pakietów daje nam scapy. W moim przypadku ustawiłem adres nadawcy zupełnie różny od jakiegokolwiek adresu przypisanego do kart sieciowych mojego komputera. Między innymi, właśnie elastyczność jest największą zaletą tego oprogramowania.