Sinistra <- Dieci candeline - Indice Generale - Copertina - Emacs, lo GNU parlante -> Destra

PLUTO Ware


Libnet, programmazione C e Packet Shaping

Libnet, programmazione C e Packet Shaping.

di Vincenzo "aspinall" Giacchina

L'articolo

In questo articolo l'autore descrive sinteticamente come forgiare pacchetti IP tramite l'utilizzo della libreria libnet.



Wikipedia, l'enciclopedia libera, recita:

"In una rete di computer, con il termine di IP spoofing si indica una tecnica tramite la quale si crea un pacchetto IP nel quale viene falsificato l'indirizzo IP del mittente. Nell'header di un pacchetto IP si trova uno specifico campo, il Source Address, il cui valore indica l'indirizzo IP del mittente. Semplicemente modificando questo campo si può far credere che un pacchetto IP sia stato trasmesso da una macchina differente".

Preambolo

Nell'articolo Sniffing e programmazione C a basso livello, pubblicato nel numero 43 del Pluto Journal, viene illustrato l'accesso a datalink tramite l'utilizzo di una socket di tipo RAW; in questo numero mostreremo come rendere l'accesso a datalink, per la creazione di pacchetti IP, meno ostico possibile utilizzando la libreria Libnet, che oltre ad essere una libreria leader permette di scrivere codice portabile e performante.

Allo stato attuale é correntemente sviluppata da Mike D. Schiffman (dal 1998): è stata implementata e resa popolare da tantissimi software. L'autore definisce così la libreria:

"Libnet is a high-level API (toolkit) allowing the application programmer to construct and inject network packets".

Viene distribuita sotto la licenza BSD. Il sito ufficiale del progetto è http://www.packetfactory.net/libnet/ .

Per modificare l'header del pacchetto è necessario lavorare a livello datalink (layer IP). Libnet mette a disposizione una serie di funzioni e strutture, che permettono la creazione del contesto Libnet e la gestione dell'header fino all'invio del pacchetto.
Per un veloce confronto, vi propongo un mio codice che effettua un IP spoofing utilizzando il protocollo TCP, scritto senza l'utilizzo della libreria Libnet : spoof_tcp.c.

Funzioni Principali

libnet_t * 
libnet_init(int injection_type, char *device, char *err_buf);

Questa funzione inizializza la libreria e si preoccuperà di gestire tramite il contesto libnet_t l'intera sessione per la creazione del pacchetto.

int injection_type accetta una seria di costanti, che specificano il tipo di socket da utilizzare:

LIBNET_LINK         Link-layer interface 
LIBNET_RAW4 Raw     sockets using Ipv4   
LIBNET_RAW6 Raw     sockets using IPv6   
LIBNET_LINK_ADV     Link-layer interface (advanced) 
LIBNET_RAW4_ADV     Raw sockets using IPv4 (advanced) 
LIBNET_RAW6_ADV     Raw sockets using IPv6 (advanced)

Esempio di utilizzo :

libnet_t *l;
if ((l=libnet_init(LIBNET_RAW4, NULL, err_buf)) == NULL)
{
        fprintf(stderr, "Errore: %s\n", err_buf);
        exit(-1);
}

u_int32_t
libnet_get_ipaddr4(libnet_t *l);

La funzione libnet_get_ipaddr4 ritorna l'indirizzo IP del device attualmente utilizzato.

Esempio :

if ((s_ip=libnet_get_ipaddr4(l)) == -1)
{
        fprintf(stderr, "Errore : %s\n", libnet_geterror(l));
        exit(-1);
}
u_int32_t
libnet_name2addr4(libnet_t *l, char *host_name, u_int8_t use_name);

La funzione libnet_name2addr4 ritorna un IP. Gli argomenti sono :

libnet_t *l : puntatore al contesto
char *host_name : puntatore ad un host di rete.
u_int8_t use_name : accetta due parametri; LIBNET_RESOLVE e LIBNET_DONT_RESOLVE.

Esempio :

if ((d_ip = libnet_name2addr4(l, "www.pluto.it", LIBNET_RESOLVE)) == -1)
                exit(-1);
                                o

if ((d_ip = libnet_name2addr4(l, "151.66.34.67", LIBNET_DONT_RESOLVE)) == -1)
                exit(-1);

void libnet_destroy(libnet_t *l);

La funzione libnet_destroy chiude la sessione precedentemente creata con libnet_init e libera la memoria allocata dalle strutture associate al puntatore "l".

int
libnet_write(libnet_t *l);

La funzione libnet_write permette l'invio dei pacchetti precedentemente forgiati tramite le funzioni libnet_build*, e ritorna un intero contenente il numero di byte inviati.

Esempio :

      if ((byte=libnet_write(l)) == -1)
        {
                fprintf(stderr, "Errore : %s\n", libnet_geterror(l));
                exit(-1);
        }

printf("%d byte trasmessi.\n", byte);

Strutture ed Header

Prima di analizzare le funzioni che permettono di forgiare il pacchetto, è bene spendere due parole prendendo in esempio l'header ICMP. Il protocollo ICMP è utilizzato per trasmettere informazioni come malfunzionamenti, informazioni di controllo e offrendo un campo data permette anche di incapsulare messaggi (man ping).

 
               Header  INTERNET CONTROL MESSAGE PROTOCOL

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     Type      |     Code      |          Checksum             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             DATA                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Type : indica il tipo di icmp utilizzato, che nel nostro caso è 8 (Echo request).
Code : valore strettamente legato a type, nel nostro caso 0.
Checksum : controllo dell'integrità del pacchetto.
Data : dati trasmessi.

Analizziamo il prototipo libpcap_build_icmp_echo

libnet_ptag_t
libnet_build_icmpv4_echo(u_int8_t type, u_int8_t code, u_int16_t sum,
u_int16_t id, u_int16_t seq, u_int8_t *payload, u_int32_t payload_s,
libnet_t *l, libnet_ptag_t ptag);

Secondo quanto detto sopra dovremmo riempire così la funzione :

if(libnet_build_icmpv4_echo
        (
        ICMP_ECHO,      //tipo
        0,              //code
        0,              //checksum
        0x100,          //id
        0x100,          //sequenze number
        NULL,           //puntatore al campo data
        0,              //lunghezza campo data
        l,              //puntatore al contesto libnet
        0               //pacchetto ID
        ) == -1)

                exit(-1);

Ora dobbiamo incapsulare quanto creato in un pacchetto IP.

libnet_ptag_t
libnet_build_ipv4(u_int16_t len, u_int8_t tos, u_int16_t id, u_int16_t frag,
u_int8_t ttl, u_int8_t prot, u_int16_t sum, u_int32_t src, u_int32_t dst,
u_int8_t *payload, u_int32_t payload_s, libnet_t *l, libnet_ptag_t ptag);

Esempio:

src_ip=libnet_get_ipaddr4(l);
dst_ip=libnet_name2addr4(l, "127.0.0.1", LIBNET_RESOLVE);

if (libnet_build_ipv4
        (
        h_lenght,       //lunghezza intera del pacchetto
        0,              //tos
        0x200,          //ID
        0,
        64,             //TTL
        IPPROTO_ICMP,   //protocollo
        0,              //checksum
        src_ip,         //indirizzo sorgente
        dst_ip,         //indirizzo destinazione
        NULL,           //campo data
        0,              //lunghezza campo data
        l,              //puntatore al contesto libnet
        0               //pacchetto ID
        ) == -1)

                exit(-1);

PFping (Pluto Force ping)

PFping è un software sviluppato appositamente per il Pluto Journal che dimostra con quale semplicità si può interagire con il network del proprio OS; permette di capire quante insidie presenta Internet e con quale facilità è possibile modificare i parametri di una connessione, che senza il nostro intervento il kernel avrebbe scelto per noi.

/*
 *
 * Pluto force ping 
 * Compile : gcc -Wall -02 pfping.c -o pfping -lnet
 * License : This source file is under GPL
 *
 *  Disclaimer:
 * Use of this information constitutes acceptance for use in
 * an AS IS condition.There are NO warranties with regard to
 * this information. In no event shall the author be liable for
 * any damages whatsoever arising out of or in connection with
 * the use or spread of this information. Any use of this
 * information is at the user's own risk.
 *
 *
 */


#include <libnet.h> 


void help();
void help()
{
        fprintf(stderr,"Usare : ./pfping -d  [OPZIONI]\n");
        fprintf(stderr, "Opzioni :\n");
        fprintf(stderr,"-s: ip sorgente\n");
        fprintf(stderr,"-p: stringa (max data : 16 byte)\n");
        fprintf(stderr,"-c: counter (max icmp echo  : 10)\n");
        fprintf(stderr,"-h: help\n\n");
        fprintf(stderr,"Pluto force ping <aspinall@oltrelinux.com>\n\n");

        exit(-1);
}


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

libnet_t *l;
char err_buf[LIBNET_ERRBUF_SIZE];

u_long s_ip;
u_long d_ip = 0;
u_short ip_proto;
u_short h_lenght;

char *data = NULL;
int len_data = 0, byte=0, num_pkt = 1;
int p, i;


if (argc < 2)
{
        help();
}


if ((l=libnet_init(LIBNET_RAW4, NULL, err_buf)) == NULL)
{
        fprintf(stderr, "Errore: %s\n", err_buf);
        exit(-1);
}


if ((s_ip=libnet_get_ipaddr4(l)) == -1)
{
        fprintf(stderr, "Errore : %s\n", libnet_geterror(l));
        exit(-1);
}


while ((p = getopt(argc, argv, "s:d:p:c:h?")) != EOF)
{
  switch (p)
  {
        case 's':
                if ((s_ip = libnet_name2addr4(l, optarg, LIBNET_RESOLVE)) == -1)
                exit(-1);
                break;

        case 'd':
                if ((d_ip = libnet_name2addr4(l, optarg, LIBNET_RESOLVE)) == -1)
                exit(-1);
                break;

        case 'p':
                data = malloc(strlen(optarg) + 1);
                strncpy(data, optarg, strlen(optarg) + 1);
                len_data=strlen(data);
                if (len_data > 16)
                {
                        fprintf(stderr, "Errore Max Data!\n");
                        exit(-1);
                }
                break;

        case 'c':
                num_pkt = atoi(optarg);
                if (num_pkt > 10)
                {
                        fprintf(stderr, "Errore Max PKT!\n");
                        exit(-1);
                }
                break;


        case 'h':
        case '?':
        default :
                help();
                break;
  }
}


if(libnet_build_icmpv4_echo
        (
        ICMP_ECHO,
        0,
        0,
        0x100,
        0x100,
        data,
        len_data,
        l,
        0
        ) == -1)

                exit(-1);
h_lenght=LIBNET_ICMPV4_ECHO_H+LIBNET_IPV4_H;
ip_proto=IPPROTO_ICMP;


if (libnet_build_ipv4
        (
        h_lenght,
        0,
        0x200,
        0,
        64,
        ip_proto,
        0,
        s_ip,
        d_ip,
        NULL,
        0,
        l,
        0
        ) == -1)

                exit(-1);


for(i=0;i < num_pkt;i++)

{
        if ((byte=libnet_write(l)) == -1)
        {
                fprintf(stderr, "Errore : %s\n", libnet_geterror(l));
                exit(-1);
        }
}

printf("%d ICMP ECHO -> %d byte trasmessi.\n", num_pkt, byte*num_pkt);


libnet_destroy(l);
return 0;
}

Uso ed abuso

linux:~/pfping # gcc -Wall -O2 pfping.c -o pfping -lnet
linux:~/pfping # ./pfping -h
Usare : ./pfping -d  [OPZIONI]
Opzioni :
-s: ip sorgente
-p: stringa (max data : 16 byte)
-c: counter (max icmp echo  : 10)
-h: help
Pluto force ping <aspinall@oltrelinux.com>
linux:~/pfping # ./pfping -d 10.0.0.2 -s www.pluto.it -c 2
2 ICMP ECHO -> 56 byte trasmessi.

Vediamo con tcpdump cosa succede:

linux:/usr/include/libnet # tcpdump -i eth1 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes
17:45:08.345221 IP eros.pluto.it > 10.0.0.2: icmp 8: echo request seq 256
17:45:08.345703 IP eros.pluto.it > 10.0.0.2: icmp 8: echo request seq 256

Con pochissime righe di codice siamo riusciti a forgiare il nostro pacchetto e a fingerci eros.pluto.it nel contattare 10.0.0.2.

linux:~/pfping # ./pfping -d 10.0.0.2 -s 1.1.1.1 -p ciao
1 ICMP ECHO -> 32 byte trasmessi.
linux:/usr/include/libnet # tcpdump -Xv -i eth1 icmp
tcpdump: listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes
17:54:40.252128 IP (tos 0x0, ttl  64, id 512, offset 0, flags [none], length: 32) 1.1.1.1 > 10.0.0.2: icmp 12: echo request seq 256
        0x0000:  4500 0020 0200 0000 4001 6bd7 c2b1 4053  E.......@.k...@S
        0x0010:  0a00 0002 0800 3127 0100 0100 6369 616f  ......1'....ciao

Ora abbiamo riempito il capo data con la stringa "ciao", e l'abbiamo inviata al nostro destinatario 10.0.0.2 fingendoci 1.1.1.1.

Conclusioni

E' doveroso precisare che al giorno d'oggi l'IP Spoofing è una tecnica che su Internet non è più possibile realizzare.
In LAN, se riuscissimo a sniffare la connessione tra due host, forgiando un pacchetto orientato alla connessione sniffata, potremmo falsificare la nostra identitità instaurando una vera e propria connessione "falsa". In Internet questa tecnica non è utilizzabile, perchè è necessario ricevere ed analizzare la risposta al nostro pacchetto mascherato per poter inizializzare la connessione. Tuttavia non è possibile ovviamente ricevere alcuna risposta a pacchetti per cui il campo mittente e` stato modificato.
In PFping sono stati imposti dei limiti sul numero di pacchetti da inviare e su quanti byte possono essere trasmessi, perchè sarebbe veramente facile realizzare un attacco DOS (Denial Of Service) anonimo, cercando di occupare/saturare più banda possibile.



L'autore

Vincenzo "aspinall" Giacchina attualmente lavora come programmatore/sistemista Unix in un' azienda fornitrice Telecom. Studia, nel tempo che trova, informatica all'università di Palermo. Il suo interesse principale è la network security.


Sinistra <- Dieci candeline - Indice Generale - Copertina - Emacs, lo GNU parlante -> Destra