Scapy jest tak naprawdę biblioteką w języku Python, do której twórcy stworzyli dla nas wygodny interfejs, przypominający wiersz poleceń. Przypominający, ponieważ nawet w tym interfejsie mniej lub bardziej świadomie programujemy. Dlaczego zatem nie wykorzystać możliwości języka Python i nie pisać po prostu aplikacji korzystających z biblioteki Scapy?

Jak i gdzie może to być przydatne?

Istnieje wiele obszarów, w których programowalne podejście do generowania dowolnych pakietów nam się przyda. Scapy jest powszechnie wykorzystywane we wszelkiego rodzaju testach funkcjonalnych, czy to aplikacji, czy to platform sieciowych. Twórcy testów funkcjonalnych przygotowują odpowiednio spreparowane pakiety, aby przetestować działanie aplikacji, w tym odporność na błędy czy ataki, które mogą być na nie kierowane. Sieć, o ile pakiet jest poprawnie skonstruowany, prześle dowolne informacje, natomiast Scapy pozwala nam na umieszczenie w nagłówkach pakietów, jak i w polu danych dowolnych informacji. Oznacza to, że testerzy mogą wygenerować payload o dowolnej strukturze i przesłać go do aplikacji docelowej, aby przetestować obsługę błędów czy zdarzeń wyjątkowych. Audytorzy i pentesterzy wykorzystują Scapy do przeprowadzania rekonesansu infrastruktury. Oprogramowanie może również zostać wykorzystane do zbierania danych (sniffing) czy ataku na infrastrukturę lub aplikacje. Oczywiście w systemie zarówno Linux jak i Windows znajdziemy wiele aplikacji, które wykonują takie same czynności, na które są zaprogramowane w Scapy. W tym przypadku, największą zaletą Scapy jest to, że mamy bardzo wiele funkcjonalności skompensowanych wewnątrz pojedynczej biblioteki, zatem wykorzystanie ich staje się proste, często bardziej intuicyjne. Zebrane dane zapisujemy zmiennych, których zawartość następnie możemy dowolnie analizować i modyfikować korzystając z funkcji które są wbudowane w bibliotekę Scapy, bez konieczności dokonywania translacji na inne struktury danych wykorzystywane przez inne biblioteki. Dzięki temu dostajemy do rąk bardzo potężne narzędzie.

Scapy i Python

Gdy zainstalujemy już bibliotekę Scapy to w naszych skrytkach języku Python wykorzystujemy ją w taki sam sposób, jak każdą inną bibliotekę. Rozpocząć musimy od jej zaimportowania.

import scapy.all as scapy

Scapy jest biblioteką obiektową. Oznacza to, że korzystając dni musimy programować obiektowo. Utworzenie zmiennej konkretnego typu, czyli wskazanego typu obiektu, może za sobą pociągać wywołanie konkretnych czynności, które są zdefiniowane w konstruktorze danego obiektu. Zatem nie jest tak, że utworzenie obiektu nie wywołuje od razu wykonania pewnych czynności. Spójrzmy na poniższy przykład:

req = scapy.ARP()

print(req.summary())

Gdy tworzymy zmienną req typu ARP to wywołujemy konstruktor obiektu ARP. Świadczą o tym między innymi nawiasy które dodajemy na końcu tego wywołania. Konstruktor obiektu zaprogramowany jest w ten sposób, aby przy jego tworzeniu wysłać żądanie ARP na urządzeniu, na którym skrypt zostanie uruchomiony. Samo utworzenie obiektu spowoduje już wygenerowanie i wysłanie odpowiedniego zapytania do naszej sieci. Dlaczego o tym piszę? Wyobraź sobie, że potrzebujesz napisać skrypt, który będzie testował konfigurację twojego routera albo firewalla. Już na etapie programowania i testowania działanie tego skryptu nieświadomie może zacząć wysyłać pakiety, które wyzwolą alarmy bezpieczeństwa w Twojej sieci lub w jakikolwiek inny sposób negatywnie wpłynął na jej działanie. Dlatego programowanie z wykorzystaniem biblioteki scapy wymaga dokładnego poznania mechanizmów, które są wbudowane w konkretne obiekty, a najlepiej programowania i testowania skryptów w zamkniętym, odizolowanym środowisku laboratoryjnym.

Wróćmy do naszego przykładu. Konstruktor obiektu ARP działa w ten sposób, że wysyłane jest zapytanie ARP (czyli who-has) o wskazany adres. Ponieważ w naszym wywołaniu nie wskazaliśmy adresu, o który pytamy konstruktor domyślnie podstawi wartość „0.0.0.0”. Otrzymany wynik możemy wyświetlić oraz przetwarzać dalej na wiele sposobów. Wywołanie metody summary() powoduje wyświetlenie na ekranie odpowiedzi ARP, która podobna jest do tej, którą często widzimy w zrzutach choćby z tcpdump.

admin@host:~ $ python3 skrypt.py

ARP who has 0.0.0.0 says 172.16.8.81

Na szczęście, biblioteka oferuje nam więcej przyjaznych funkcji, które możemy wykorzystać do wyświetlania danych na ekranie. Jedną z najczęściej używanych i wyświetlających najbardziej przyjazny do odczytania użytkownikowi wynik jest metoda „show()”.

req = scapy.ARP()

print(req.show())

Wyświetlane na ekranie informacje są od razu odpowiednio sformatowane, aby ułatwić zwykłemu użytkownikowi ich odczytanie.

admin@host:~ $ python3 skrypt.py

###[ ARP ]###

  hwtype    = 0x1

  ptype     = 0x800

  hwlen     = 6

  plen      = 4

  op        = who-has

  hwsrc     = b8:27:eb:aa:bb:cc

  psrc      = 172.16.88.81

  hwdst     = 00:00:00:00:00:00

  pdst      = 0.0.0.0

None

Generator pakietów w jednej linii

Wykorzystując bibliotekę Scapy możemy łączyć zalety elastyczności języka Python z szerokimi możliwościami samej biblioteki. Załóżmy, że elementem działania naszej aplikacji ma być wygenerowanie pakietu ICMP o określonym numerze sekwencyjnym lub dowolnie inaczej spreparowanym polu w nagłówku. Patrząc na to zadanie od strony programisty wiemy, że musimy przygotować pakiet ICMP z odpowiednimi parametrami, umieścić go wewnątrz pakietu IP, w którego nagłówku zawrzemy adres odbiorcy, a następnie wysłać taki pakiet. Te trzy czynności zapiszemy odpowiednio w poniższym skrypcie:

#!/usr/bin/python3

import scapy.all as scapy

pakiet_IP = scapy.IP(dst=”172.16.8.83″)

wiadomosc_ICMP = scapy.ICMP(seq=1234)

pakiet_do_wyslania = pakiet_IP / wiadomosc_ICMP

scapy.send(pakiet_do_wyslania)

Pamiętajmy jednak, że jedną z największych zalet języka Python jest jego elastyczność. Zatem całą tę operację możemy zawrzeć w jednej linii:

#!/usr/bin/python3

import scapy.all as scapy

scapy.send(scapy.IP(dst=”172.16.8.83″) / scapy.ICMP(seq=1234))

Pamiętajmy także, że większość operacji, które chcemy wykonywać za pomocą biblioteki scapy wymagają uprawnień root-a, zatem musimy uruchamiać się bezpośrednio z konta z uprawnieniami administratora lub za pomocą sudo.