www.hakin9.org
hakin9 Nr 11/2007
30
Atak
O
becnie eksperci zajmujący się bez-
pieczeństwem komputerowym mo-
gą korzystać z całej gamy aplikacji
służących do analizy ruchu w sieci, pisania
exploitów, skanowania portów czy wykrywa-
nia włamań. Niektóre z tych programów ma-
ją już ugruntowaną pozycję i korzystanie z
nich stało się pewnym standardem. Bywa
jednak, że niektóre ze znanych narzędzi nie
spełniają wszystkich naszych wymagań lub
z pewnych przyczyn nie możemy z nich sko-
rzystać. Wtedy z pomocą przychodzi język
Python i nasza kreatywność – w kilka chwil
możemy napisać skrypt, który będzie dosto-
sowany do konkretnego problemu i, co naj-
ważniejsze, jego budowa będzie nam do-
brze znana.
Dlaczego Python?
Jest kilka powodów, dla których warto rozpo-
cząć naukę tego języka. Po pierwsze, jest to
język, który stale się rozwija i jest ciągle udo-
skonalany. Wydany jest na licencji Open So-
urce, zapewniającej stałe wsparcie rzeszy
oddanych programistów. Co więcej, Python
jest językiem łatwym w nauce, a jego kod jest
bardzo przejrzysty. Pozwala to na łatwą mo-
dyfikację źródła aplikacji, nawet przez oso-
by niebędące znawcami tego języka. Jednak
największym plusem jest ogromna ilość mo-
dułów i frameworków rozszerzających możli-
wości języka. Dotyczą one zarówno progra-
mowania sieciowego, interfejsów graficznych
jak i np. obróbki grafiki.
Idealnym przykładem jest moduł Inline-
Egg, który został już opisany na łamach ha-
kin9. Kolejnymi, doskonałymi przykładami,
mogą być rozszerzenia przybliżane w tym
Spoofing i Sniffing
w Pythonie
Piotr Łaskawiec
stopień trudności
Każdy specjalista ds. bezpieczeństwa komputerowego powinien
być w stanie pisać programy pomagające w audycie lub analizie
ewentualnych zagrożeń. Programy te powinny być proste,
przejrzyste i co najważniejsze, ich budowa powinna zajmować
mało czasu. Programy pisane w Pythonie spełniają wszystkie
powyższe warunki.
Z artykułu dowiesz się
• jak pisać aplikacje sieciowe w języku Python,
• jak szybko przechwytywać i odczytywać pakie-
ty w Sieci,
• czym jest spoofing i jak można napisać pro-
gram modyfikujący pakiety w Pythonie.
Co powinieneś wiedzieć
• powinieneś znać podstawy działania sieci
TCP/IP,
• powinieneś znać podstawy Pythona.
Aplikacje sieciowe w Pythonie
hakin9 Nr 11/2007
www.hakin9.org
31
artykule, czyli Pcapy i Impacket.
Wszystkie programy napisane na
potrzeby tej publikacji były testo-
wane na systemie operacyjnym Li-
nux, z zainstalowanym Pythonem
w wersji 2.5 (którego można po-
brać ze strony (1)).
Moduł Socket
Python posiada duży zbiór stan-
dardowych modułów służących do
programowania sieciowego. Ideą
Pythona jest udostępnienie wszyst-
kich najpotrzebniejszych narzę-
dzi wraz z pakietem instalacyjnym.
Moduł socket daje nam swobodny
dostęp do gniazd (sockets), czyli
abstrakcyjnych obiektów pozwala-
jących nam na nawiązywanie połą-
czeń, transmisję danych itd.
Funkcja
socket([rodzina _ adresu[,
typ _ gniazda[, numer _ protokolu]]])
odpowiedzialna jest za tworzenie
nowego gniazda. W Ramce przed-
stawione są możliwe typy gniazd
i rodziny adresów wraz ze sto-
sownym opisem. Do gniazda przy-
porządkowane są także niezbęd-
ne funkcje, odpowiadające opera-
cjom które może wykonywać soc-
ket:
•
listen()
– oczekiwanie na połą-
czenia dla danego gniazda,
•
accept()
– akceptacja oczekują-
cego połączenia,
•
bind()
– przypisanie adresu do
gniazda,
•
connect()
– nawiązanie połącze-
nia,
•
close()
– zamknięcie połączenia
i anulowanie wszystkich operacji.
Listingi 1 i 2 obrazują łatwość pisa-
nia programów w Pythonie. W kilka
minut można napisać prostą aplika-
cję typu klient-serwer. Kod pokaza-
ny na Listingach jest bardzo pro-
sty i tym samym jest dobrym wpro-
wadzeniem do dalszej części arty-
kułu.
Pcapy i Impacket
Są to dwa moduły usprawniające
programowanie sieciowe w Pytho-
nie. Pcapy pozwala na wyłapywa-
nie pakietów z sieci, natomiast Im-
packet służy do ich szczegółowej
analizy i modyfikacji. Obsługuje on
zarówno protokoły niskopoziomowe
(IP, TCP, UDP, ICMP), jak i proto-
koły wysokiego poziomu. Oba mo-
duły idealnie ze sobą współpracu-
ją. Można je pobrać ze strony (2).
W celu instalacji obu modułów,
przechodzimy do katalogu ze źró-
dłem i wydajemy polecenie:
python setup.py install
Należy także pamiętać, że do po-
prawnego działania Pcapy potrzeb-
Rodziny adresów
•
AF_UNIX
– Gniazdo uniksowe, zapewniające wydajną komunikację międzyproceso-
wą na tej samej maszynie. W Pythonie adresy tych gniazd reprezentowane są w po-
staci łańcuchów.
•
AF _ INET
– Gniazdo zapewniające komunikację na dwóch różnych maszynach
przy wykorzystaniu adresowania IPv4. Reprezentowane jest przez układ (
host
,
port
), gdzie
host
jest łańcuchem znaków a
port
liczbą całkowitą.
•
AF _ INET6
– Gniazdo różni się od
AF _ INET
tylko zmianą adresowania z IPv4 na
IPv6. Obecnie jest ono bardzo rzadko używane.
Typy gniazd
•
SOCK_STREAM
– odpowiada za komunikację połączeniową,
•
SOCK _ DGRAM
– odpowiada za komunikację bezpołączeniową,
•
SOCK _ RAW
– zapewnia dostęp do warstw położonych poniżej warstwy aplikacyj-
nej. Pozwala na manipulację surowymi pakietami na poziomie warstwy sieciowej,
•
SOCK _ RDM
– gniazdo komunikatów niezawodnie doręczanych,
•
SOCK _ SEQPACKET
– gniazdo pakietów uporządkowanych.
Rysunek 1.
Działanie sniffera
W Sieci
• http://www.python.org – strona domowa Pythona (1),
• http://www.oss.coresecurity.com/ – strona domowa modułów pcapy i impacket (2),
• http://www.tcpdump.org – strona projektu libpcap (3),
• http://www.twistedmatrix.com – strona projektu Twisted (4).
hakin9 Nr 11/2007
www.hakin9.org
Atak
32
ny jest Libcap w wersji 0.9.3 lub now-
szej. Można go pobrać ze strony (3).
Inne rozwiązania
Szybki wzrost popularności Py-
thona powoduje ciągłe powstawa-
nie nowych rozwiązań programi-
stycznych. Jednym z nich jest Twi-
sted – alternatywa dla omawianych
w tym artykule modułów Pcapy
i Impacket. Twisted jest framewor-
kiem, który dostarcza nam wielu
mechanizmów pomagających w pi-
saniu aplikacji sieciowych. Zapew-
nia on wsparcie takich protokołów
jak TCP, UDP, SSL/TLS, HTTP,
SSH itd. Z Twisted korzysta wie-
le znanych programów, m. in. Bit-
Torrent oraz Zope3. Twisted moż-
na pobrać ze strony (4). Instalacja
przebiega analogicznie, jak w przy-
padku Pcapy i Impacket. Wchodzi-
my do katalogu, w którym umiesz-
czone są źródła i wydajemy polece-
nie python setup.py install. Gorąco
polecam zaznajomienie się z tym
projektem w wolnej chwili.
Piszemy sniffer
Pierwszym przydatnym programem
który napiszemy, będzie sniffer. Za-
danie stawiane przed naszą apli-
kacją jest oczywiste – ma on prze-
chwytywać pakiety i wyświetlać ich
zawartość. Program ten stanowi
świetne narzędzie diagnostyczne
i jest nieoceniony w pewnych sytu-
acjach, dlatego uważam, że war-
to poznać jego budowę. Przeana-
lizujmy kod zamieszczony w Listin-
gu 3. Źródło składa się z kilkunastu
linijek i stanowi w pełni działający
sniffer. Program wykorzystuje tzw.
surowe gniazda (raw sockets) oraz
dwa moduły: moduł socket, któ-
ry jest standardowym składnikiem
Pythona, a także moduł Impacket.
W kodzie na Listingu 3. należy zwró-
cić uwagę na sposób tworzenia
obiektu socket. Do funkcji
socket()
przekazywane są 3 argumenty:
AF _ INET, SOCK _ RAW
i numer proto-
kołu. Numer protokołu jest elemen-
tem wymaganym podczas manipu-
lowania surowymi gniazdami. Nu-
mer ten możemy pozyskać za po-
mocą funkcji
getprotobyname()
, któ-
Listing 1.
Prosty serwer
import
socket
#tworzymy gniazdo
mysocket
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_STREAM
)
#laczymy gniazdo z odpowienim portem
mysocket
.
bind
((
''
,
1234
))
mysocket
.
listen
(
5
)
while
True
:
#Akceptacja polaczenia
client
,
address
=
mysocket
.
accept
()
'Nowe polaczenie: '
,
address
# Wysylamy banner do klienta
client
.
send
(
'Witamy na testowym serwerze'
)
#Zamykamy polaczenie
client
.
close
()
Listing 2.
Prosty klient
import
socket
#tworzymy gniazdo
mysocket
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_STREAM
)
#nawiazujemy polaczenie
mysocket
.
connect
((
'localhost'
,
1234
))
#odbior i drukowanie danych
temp
=
mysocket
.
recv
(
1024
)
temp
#zamykamy polaczenie
mysocket
.
close
()
Listing 3.
Sniffer-Impacket
import
socket
from
impacket
import
ImpactDecoder
#wykorzystywany protokol
protocol
=
"tcp"
#zamiana nazwy protokolu na liczbe
int_protocol
=
socket
.
getprotobyname
(
protocol
)
#tworzymy surowe gniazdo
mysocket
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_RAW
,
int_protocol
)
mysocket
.
setsockopt
(
socket
.
IPPROTO_IP
,
socket
.
IP_HDRINCL
,
1
)
"Nasluchuje..."
#tworzymy dekoder
ipdecoder
=
ImpactDecoder
.
IPDecoder
()
while
True
:
#odbieranie danych
packet
=
mysocket
.
recvfrom
(
4096
)[
0
]
if
0
==
len
(
packet
):
mysocket
.
close
()
break
;
else
:
#dekodowanie danych
packet
=
ipdecoder
.
decode
(
packet
)
packet
Aplikacje sieciowe w Pythonie
hakin9 Nr 11/2007
www.hakin9.org
33
ra zwraca nam odpowiednią liczbę
na podstawie nazwy protokołu. Ko-
lejną ważną i często używaną funk-
cją jest
setsockopt()
, która służy do
ustawiania opcji gniazda. W na-
szym przypadku ustawiamy para-
metr
IP _ HDRINCL
na wartość
True
.
Jest on odpowiedzialny za dostar-
czenie aplikacji nagłówków pakie-
tu. Opcja ta może występować tyl-
ko wtedy, gdy korzystamy z suro-
wych gniazd.
W kodzie przedstawionym na
Listingu 3. kluczową rolę odgrywa
obiekt
ipdecoder
. Jest on tworzony
poprzez
ipdecoder = ImpactDecoder
.IPDecoder()
i służy do konwersji su-
rowych pakietów na jednostki zrozu-
miałe dla modułu Impacket. Umożli-
wia to swobodną manipulację pakie-
tami i wyświetlenie pakietu w spo-
sób przejrzysty. Sam proces deko-
dowania odbywa się poprzez funkcję
decode()
, do której przekazujemy po-
brane dane.
Wykorzystanie modułu socket
nie jest jedyną drogą prowadzą-
cą do napisania sniffera. Możemy
również wykorzystać moduł Pcapy.
Kod programu, który opiera się na
Pcapy i Impacket, przedstawiony
jest na Listingu 4. Niewątpliwą za-
letą takiego rozwiązania jest moż-
liwość wykorzystania kilku przy-
datnych funkcji będących częścią
Pcapy.
Pierwszą rzeczą, na którą warto
zwrócić uwagę, jest funkcja
findall-
devs()
. Zwraca ona listę wszystkich
aktywnych interfejsów sieciowych.
Umożliwia to łatwy wybór źródła
pakietów i nie ogranicza nas do do-
myślnego interfejsu.
Kolejne ułatwienie przychodzi
wraz z funkcją
setfilter()
. Umożli-
wia ona dostosowywania naszego
sniffera do konkretnego protokołu.
Oprócz TCP, który został wykorzy-
stany w naszym przykładzie, może-
my także skorzystać z innych zna-
nych protokołów (np. ICMP).
Sercem programu jest funkcja
open _ live()
. Służy ona do otwie-
rania konkretnego urządzenia w ce-
lu wyłapania pakietów. Przekazuje-
my do niej nazwę urządzenia, ilość
pobieranych bajtów, flagę trybu pro-
miscuous i opóźnienie. Działający
sniffer przedstawia Rysunek 1.
Piszemy spoofer
Czas na napisanie innego, równie
przydatnego programu. Na Listingu
5. został zamieszczony kod proste-
go spoofera napisanego w Pythonie
przy użyciu modułów socket i Impac-
ket. W czasie pisania programu zo-
stały wykorzystane sposoby progra-
mowania opisane przy okazji wcze-
śniejszych przykładów. Sam pro-
gram spełnia kilka ważnych warun-
ków:
• kod jest krótki, przejrzysty i opty-
malny,
• program potrafi modyfikować ad-
res nadawcy i port,
• program potrafi zmieniać usta-
wienia flag SYN i ACK,
• spoofer potrafi zmieniać wartość
TTL (time-to-live).
Dzięki aplikacji z Listingu 5. jesteśmy
w stanie wysłać własnoręcznie zmo-
dyfikowany pakiet na dowolny adres
w sieci. Jest to przydatne przy pew-
nych rodzajach ataków, o których na-
piszę później. Nasz spoofer urucha-
miamy poprzez wywołanie z konso-
li komendy:
python sniff.py<adres_odbiorcy>
<port_odbiorcy><adres_nadawcy>
<port_nadawcy><wart_SYN>
<wart_ACK><wart_TTL>
Terminologia
• Sniffing – jest to monitorowanie komunikacji między urządzeniami w sieci. Snif-
fery pasywnie przechwytują pakiety komunikacji sieciowej serwera, routera czy
bramy. Podstawą działania sniffera jest tworzenie kopii informacji wysyłanych
i odbieranych przez urządzenie sieciowe. Wśród tych informacji można zna-
leźć wiele przydatnych danych, które mogą być pomocne w analizie architektu-
ry sieci lub podczas opracowywania odpowiedniego wektora ataku. Należy jed-
nak pamiętać, że sniffing jest możliwy tylko wtedy, gdy pakiety docierają do na-
szych interfejsów sieciowych.
• Spoofing – jest to podrabianie adresów IP i nazw DNS w celu uzyskania autoryza-
cji i możliwości komunikacji z chronioną stacją docelową. Spoofing polega głównie
na przechwyceniu i modyfikacji pakietów przesyłanych między dwoma urządzenia-
mi lub przekierowaniu pakietów ze stacji docelowej do atakującego.
Tryb promiscuous
Jest to tryb pracy karty sieciowej (lub innego, dowolnego interfejsu sieciowego), w któ-
rym przechwytuje ona wszystkie pakiety krążące w sieci, a nie tylko te adresowane
bezpośrednio do niej (poprzez adres MAC). Pozwala to na podsłuchiwanie ruchu w ra-
mach swojego segmentu Sieci.
Raw Sockets
Raw sockets są rodzajem gniazd sieciowych, które zapewniają dostęp do nagłów-
ków pakietów wysyłanych i pobieranych z sieci. W przypadku korzystania z raw soc-
kets, programista otrzymuje pełną kontrolę nad procesem tworzenia pakietu i co za
tym idzie, odpowiada za jego poprawne oznakowanie i zaadresowanie. Surowe gniaz-
da pozwalają także na dostęp do warstw leżących poniżej warstwy aplikacji w mode-
lu OSI, a więc do warstwy transportowej i warstwy sieciowej. Zwiększa to nasze moż-
liwości i oferuje praktycznie nieograniczone sposoby na modyfikację wysyłanych i od-
bieranych pakietów.
hakin9 Nr 11/2007
www.hakin9.org
Atak
34
Przeanalizujmy teraz kod progra-
mu. Po zaimportowaniu odpowied-
nich modułów następuje przypisa-
nie zmiennym wartości podanych
przez użytkownika oraz deklara-
cja protokołu (w naszym przypadku
jest to TCP). Korzystamy z surowych
gniazd, więc konieczne będzie obli-
czenie wartości liczbowej odpowia-
dającej protokołowi TCP. Następ-
nie tworzymy obiekt IP poprzez mo-
duł impacket. Instancja klasy IP jest
implementacją protokołu IP działają-
cego w warstwie sieciowej modelu
OSI. Zgodnie z tym uzupełniamy da-
ne potrzebne do prawidłowego zbu-
dowania nagłówka – adres nadawcy
i odbiorcy, a także wartość TTL. Do-
konujemy tego za pomocą funkcji:
set_ip_src(), set_ip_dst()
oraz
set_ip_ttl().
Analogicznie postępujemy z obiek-
tem reprezentującym protokół TCP.
Aby uzupełnić braki w nagłówku
TCP (port źródłowy, port docelowy,
wartości SYN i ACK), posługujemy
się funkcjami:
set_th_sport(), set_th_dport(),
set_th_seq()
i
set_th_ack().
Ostatnią rzeczą o jakiej musimy pa-
miętać, jest wygenerowanie sumy
kontrolnej. Suma kontrola (check-
sum) jest częścią nagłówka pakietu
TCP i jest wymagana do ustanowie-
nia połączenia:
tcp.calculate_checksum()
tcp.auto_checksum = 1
Ustawienie atrybutu
auto _ checksum
na wartość 1 (równoznaczne z
True
)
informuje program, że suma kontro-
lna będzie wygenerowana automa-
tycznie dla każdego kolejnego pa-
kietu. Zmiana tego atrybutu na 0
wymusi na nas ręczne podawanie
Listing 4.
Sniffer-Pcapy, Impacket
import
pcapy
from
impacket
import
ImpactDecoder
def
packet_recive
(
handler
,
data
):
#dekodowanie danych
decoded
=
ImpactDecoder
.
EthDecoder
()
.
decode
(
data
)
decoded
#listujemy interfejsy sieciowe
eths
=
pcapy
.
findalldevs
()
"Interfejsy sieciowe: "
i
=
0
for
eth
in
eths
:
"%d: %s"
%
(
i
,
eths
[
i
])
i
+=
1
choose
=
int
(
raw_input
(
"Wybierz interfejs: "
))
eth
=
eths
[
choose
]
#
source
=
pcapy
.
open_live
(
eth
,
1500
,
1
,
100
)
source
.
setfilter
(
'tcp'
)
"Siec:%s Maska:%s
\n
"
%
(
source
.
getnet
()
,
source
.
getmask
())
#reakcja na przychodzące pakiety
source
.
loop
(-
1
,
packet_recive
)
Listing 5.
Spoofer
import
socket
import
sys
from
impacket
import
ImpactPacket
protocol
=
"tcp"
int_protocol
=
socket
.
getprotobyname
(
protocol
)
dst_host
=
sys
.
argv
[
1
]
dst_port
=
int
(
sys
.
argv
[
2
])
src_host
=
sys
.
argv
[
3
]
src_port
=
int
(
sys
.
argv
[
4
])
SYN
=
int
(
sys
.
argv
[
5
])
ACK
=
int
(
sys
.
argv
[
6
])
TTL
=
int
(
sys
.
argv
[
7
])
#tworzymy obiekt IP
ip
=
ImpactPacket
.
IP
()
#deklarujemy adresata, odbiorce i wartosc TTL
ip
.
set_ip_src
(
src_host
)
ip
.
set_ip_dst
(
dst_host
)
ip
.
set_ip_ttl
(
TTL
)
#tworzymy obiekt TCP
tcp
=
ImpactPacket
.
TCP
()
tcp
.
set_th_sport
(
src_port
)
#deklarujemy port zrodlowy, port docelowy, wartosci SYN i ACK
tcp
.
set_th_dport
(
dst_port
)
tcp
.
set_th_seq
(
SYN
)
tcp
.
set_SYN
()
tcp
.
set_th_ack
(
ACK
)
tcp
.
set_ACK
()
ip
.
contains
(
tcp
)
#obliczamy sume kontrolna
tcp
.
calculate_checksum
()
tcp
.
auto_checksum
=
1
#nawiazujemy polaczenie i wysylamy pakiet
mysocket
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_RAW
,
int_protocol
)
mysocket
.
setblocking
(
0
)
mysocket
.
setsockopt
(
socket
.
IPPROTO_IP
,
socket
.
IP_HDRINCL
,
1
)
mysocket
.
sendto
(
ip
.
get_packet
()
,
(
dst_host
,
dst_port
))
sumy kontrolnej (co w niektórych przypadkach jest sytu-
acją pożądaną).
Ciekawą i na pewno godną zapamiętania funkcją
jest
setblocking()
. Gdy przekażemy do funkcji 0, gniaz-
do nie będzie blokowane. Oznacza to, że każde nie-
udane wysłanie pakietu danych lub brak możliwości
odebrania takiego pakietu z sieci spowodują wywoła-
nie wyjątku.
Należy teraz zastanowić się nad praktycznym za-
stosowaniem takiego programu. Istnieje wiele ataków,
u podstaw których leży modyfikacja wysyłanych pakie-
tów. Jednym z takich ataków jest atak typu SYN. Do ata-
kowanego komputera wysyłane jest żądanie połącze-
nia TCP (SYN). Wysyłane pakiety mają zmieniony ad-
res nadawcy (na adres innego komputera w sieci). Wy-
słanie odpowiednio dużej ilości takich pakietów powo-
duje zaangażowanie bardzo dużych zasobów na atako-
wanej maszynie.
W wyniku tego możemy doprowadzić do zawiesze-
nia atakowanej maszyny. Kolejnym atakiem wykorzy-
stującym spoofing jest DNS Amplification. Polega on
na wysłaniu zapytań do serwerów DNS ze zmodyfi-
kowanym adresem zwrotnym (adres zwrotny ustawia-
ny jest na adres atakowanej maszyny). Przy wykorzy-
staniu większej ilości komputerów wysyłających zapy-
tanie, komputer ofiary nie jest w stanie obsłużyć połą-
czeń kierowanych z serwerów DNS i najczęściej zawie-
sza się. Jak widać, spoofing ma swoje zastosowanie
w praktyce.
Podsumowanie
Umiejętność pisania aplikacji sieciowych w Pythonie jest
cenna. Pozwala nam szybko i przyjemnie pisać aplikacje
dostosowane do konkretnego przypadku. Łatwość pisa-
nia gwarantuje nam duży komfort pracy, a duże możliwo-
ści samego języka i jego modułów pozwalają na stworze-
nie praktycznie dowolnego programu.
W tym artykule zostały przedstawione podstawy pro-
gramowania sieciowego. Jest to pewna fundamentalna
wiedza, która przy odrobinie chęci i samozaparcia po-
zwala pisać bardziej złożone aplikacje. W dzisiejszych
czasach umiejętność pisania programów mających prak-
tyczne zastosowanie w procesie audytu jest na wagę zło-
ta. Równie ważny jest czas poświęcony na kodowanie.
Pamiętajmy więc, że Python jest językiem, który ideal-
nie łączy szybkość pracy z instynktownym programowa-
niem. l
O autorze
Piotr Łaskawiec od wielu lat związany z tematyką bezpie-
czeństwa komputerowego. Pasjonat języka Python. Założy-
ciel i przewodniczący Koła Naukowego PK IT Security Gro-
up (www.pkitsec.pl). W wolnych chwilach programuje i zajmu-
je się publicystyką.
Kontakt z autorem: hellsource@gmail.com