<- Mozilla Firefox - Indice Generale - Copertina - DaDaBIK -> |
PLUTO Ware
di Vincenzo "aspinall" Giacchina
L'articoloIn questo articolo l'autore descrive sinteticamente cosa sia uno sniffer, illustrando con un esempio pratico come se ne possa scrivere uno. |
Con il termine sniffing si intende quella tecnica che permette
ad un utente di poter leggere nel dettaglio tutto il traffico che transita
nella rete in modalità passiva, con scopi legittimi (ad esempio
verificare se vi sono state eventuali intrusioni nel proprio sistema) o
illegittimi (ad esempio tentare di intercettare informazioni sensibili
provenienti da terze parti).
Quando uno sniffer viene eseguito su una macchina, solitamente si mette in
background ad intercettare tutti i pacchetti che transitano per
un'interfaccia di rete, sia in ingresso che in uscita, salvando il risultato
su file o rendendolo semplicemente a video.
Avendo usato il termine "intercettare", è doveroso spiegare come questo
avvenga.
Quando più pc si trovano nello stesso segmento di rete capita, ad
esempio a causa dell'utilizzo di un hub, che i dati che viaggiano sulla rete
vengano replicati.
D ^ | | | B<------ HUB ------>C: riceve il traffico che A e D si scambiano, ma il suo kernel ^ lo rifiuta, perché non è indirizzato alla sua interfaccia di rete. | | | A: vuole comunicare con D, ma l'hub invia il traffico anche a tutte le altre macchine. |
In una rete dove le comunicazioni non vengono crittografate, uno sniffer è davvero uno strumento potenzialmente pericoloso. Impostando la scheda di rete di una macchina in modalità promiscua, si dice al kernel di accettare tutti i pacchetti, anche quelli non destinati a quella determinata macchina.
Ogni applicazione che usa il protocollo TCP per trasmettere dei dati crea
un "canale di comunicazione", definito socket. La socket si colloca
ad un livello più alto rispetto ad una socket raw utilizzata da uno
sniffer, semplicemente perché con uno sniffer dobbiamo andare a
leggere tutti i dati trasmessi da altri programmi, quindi altre socket.
Una socket di tipo raw non solo permette di leggere tutti i pacchetti ma
anche di forgiarne altri a nostro piacere.
Con una socket di tipo raw riusciamo a leggere l'header di ogni pacchetto
ricevuto, inteso come intestazione di ogni pacchetto TCP, e in
diversi casi (vedi hub) anche i pacchetti scambiati da altri pc.
Una volta chiaro cosa sia e cosa faccia uno sniffer, vediamo di scriverne
uno. Munitevi di un compilatore e di buona pazienza.
Eccovi in pasto un po' di codice :
sock_ioctl = socket(PF_INET, SOCK_DGRAM, 0); |
Con questa chiamata creiamo la socket per la comunicazione con la ioctl().
/* codice dell' header sockios.h #define SIOCGIFFLAGS 0x8913 /* get flags */ #define SIOCSIFFLAGS 0x8914 /* set flags */ */ ioctl(sock, SIOCGIFFLAGS, &ifr); |
In questo modo leggiamo i flag del device e modifichiamo i campi che ci interessano.
ifr.ifr_flags |= IFF_PROMISC; ioctl(sock, SIOCSIFFLAGS, &ifr); /* codice tratto da if.h # define ifr_flags ifr_ifru.ifru_flags /* flags */ IFF_PROMISC = 0x100, /* Receive all packets. */ # define IFF_PROMISC IFF_PROMISC */ |
Modifichiamo il campo ifr.ifr_flags con IIF_PROMISC e con la funzione
ioctl andiamo a scrivere tramite il flag SIOCSIFFLAGS nella struttura
ifreq.
Fatto questo abbiamo modificato il nostro device impostandolo in
modalità promiscua.
Dopo aver costruito il nostro pacchetto,
ethernet = (struct ethhdr *)buffer; ip = (struct iphdr *) (buffer + sizeof(struct ethhdr)); tcp = (struct tcphdr *) (buffer + sizeof(struct ethhdr) + sizeof(struct iphdr)); |
inserendo ad ogni livello l'header precedente, dobbiamo semplicemente creare un ciclo infinito e tenerci in ascolto sulla socket.
while(1) { recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&from, &addr_len); printf(flag tcp vari ed eventuali....) |
Strano ma vero, abbiamo già scritto un codice che analizza nel dettaglio l'header di ogni pacchetto che arriva alla recvfrom(), posta in ascolto sul nostro device.
/* Author : aspinall@oltrelinux.com * License : This source file is under GPL * Only for Linux kernel * * * 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 <net/if.h> #include <string.h> #include <signal.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <netdb.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #include <linux/icmp.h> #include <netinet/if_ether.h> #include <netinet/in.h> int sock_ioctl; int fd; int addr_len; char buffer[1500]; //1500 da RFC è il massimo MTU per Ethernet (più grosso non serve) struct ifreq ifr; struct ethhdr *ethernet = (struct ethhdr *)&buffer[0]; struct iphdr *ip = (struct iphdr *)&buffer[sizeof(struct ethhdr)];; struct tcphdr *tcp; struct udphdr *udp; struct icmphdr *icmp; struct sockaddr from; struct sockaddr_in sin_in; void end() { ifr.ifr_flags &=~ IFF_PROMISC; //toglie il flag IFF_PROMISC ioctl(sock_ioctl, SIOCSIFFLAGS, &ifr); close(sock_ioctl); exit(0); } int main(int agc,char *agv[]) { (void) signal(SIGINT, end); //Intercetta CTRL+C e chiama la funzione end if (geteuid ()) { fprintf (stderr, "Devi essere root\n"); exit(1); } if (agc < 2) { printf("usare: %s <device>\n", agv[0]); exit(1); } if ((sock_ioctl = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { perror("Errore socket()"); exit(1); } addr_len = sizeof(struct sockaddr); memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, agv[1], sizeof(ifr.ifr_name)); printf("\nSniffing sul device : "); printf("%s\n", ifr.ifr_name); if ((ioctl(sock_ioctl, SIOCGIFFLAGS, &ifr)) < 0) { fprintf(stderr, "%s", "Errore ioctl()"); exit(1); } ifr.ifr_flags |= IFF_PROMISC; ioctl(sock_ioctl, SIOCSIFFLAGS, &ifr); strncpy(from.sa_data, agv[1], sizeof(from.sa_data)); if ((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) { perror("Errore socket()"); exit(1); } while(1) { recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&from,&addr_len); switch (ip->protocol) { case IPPROTO_TCP: printf("\n----IP----\n"); sin_in.sin_addr.s_addr = ip->daddr; printf("ip destinazione : %s\n", inet_ntoa(sin_in.sin_addr)); sin_in.sin_addr.s_addr = ip->saddr; printf("ip sorgente : %s\n", inet_ntoa(sin_in.sin_addr)); printf("----TCP----\n"); tcp = (struct tcphdr *)&buffer[sizeof(struct ethhdr) + sizeof(struct iphdr)]; printf("porta sorgente : %u\n", ntohs(tcp->source)); printf("porta destinazione : %u\n", ntohs(tcp->dest)); printf("sequenze number : %u\n", ntohl(tcp->seq)); printf("ACK-SEQ : %u\n", ntohl(tcp->ack_seq)); printf("flag syn : %u\n", tcp->syn); printf("flag ack : %u\n", tcp->ack); printf("flag fin : %u\n", tcp->fin); printf("ttl : %u\n", ip->ttl); break; case IPPROTO_UDP: printf("\n----IP----\n"); sin_in.sin_addr.s_addr = ip->daddr; printf("ip destinazione : %s\n", inet_ntoa(sin_in.sin_addr)); sin_in.sin_addr.s_addr = ip->saddr; printf("ip sorgente : %s\n", inet_ntoa(sin_in.sin_addr)); printf("----UDP----\n"); udp = (struct udphdr *)&buffer[sizeof(struct ethhdr) + sizeof(struct iphdr)]; printf("porta sorgente : %u\n", ntohs(udp->source)); printf("porta destinazione : %u\n", ntohs(udp->dest)); break; case IPPROTO_ICMP: printf("\n----IP----\n"); sin_in.sin_addr.s_addr = ip->daddr; printf("ip destinazione : %s\n", inet_ntoa(sin_in.sin_addr)); sin_in.sin_addr.s_addr = ip->saddr; printf("----ICMP----\n"); icmp = (struct icmphdr *)&buffer[sizeof(struct ethhdr) + sizeof(struct iphdr)]; printf("ip sorgente : %s\n", inet_ntoa(sin_in.sin_addr)); printf("type : %u\n", icmp->type); printf("code : %u\n", icmp->code); break; } } } |
Lo sniffer, come abbiamo visto, impone alla scheda di rete la modalità promiscua. Partendo da questo presupposto, per poter vedere se qualche potenziale sniffer o comunque qualche altro strumento affine è avviato nella nostra macchina, dovremo andare a controllare il campo ifr.ifr_flags e vedere se è impostato il flag IFF_PROMISC.
if((ifr.ifr_flags & IFF_PROMISC) != 0) fprintf(stdout, "%s promiscuos mode: Attenzione possibile sniffing.\n", ifr.ifr_name); |
Così facendo, andiamo a controllare il flag IFF_PROMISC mostrando
via codice se questa è attivo o no.
Chiaro che questo non è un artificio che mette del tutto in ginocchio
chi sta tentando di sniffare il traffico, ma è sempre un buon punto
di partenza.
/* Author : aspinall@oltrelinux.com * License : This source file is under GPL * Only for Linux kernel * * * 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 <string.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <stdio.h> #include <net/if.h> int sock_ioctl; struct ifreq ifr; int main() { if ((sock_ioctl = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { perror("Errore socket()"); exit(1); } strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name)); if ((ioctl(sock_ioctl, SIOCGIFFLAGS, &ifr)) < 0) { fprintf(stderr, "%s", "errore ioctl()"); exit(1); } if ((ifr.ifr_flags & IFF_PROMISC) != 0) fprintf(stdout, "%s promiscuos mode: possibile sniffing.\n", ifr.ifr_name); else fprintf(stdout, "%s non è in promuscuous mode.\n", ifr.ifr_name); exit(1); return 0; } |
Di carne al fuoco ne abbiamo messa parecchia: un consiglio che voglio
darvi è quello di analizzare le strutture principali utilizzate in
questi sorgenti per cercare di prendere più dimestichezza
possibile con esse.
L'autoreVincenzo "aspinall" Giacchina attualmente lavora come sistemista Unix in un' azienda fornitrice Telecom. Studia, nel tempo che trova, informatica all'università di Palermo. Il suo interesse principale è la network security. Collabora con alcune riviste del settore e spesso di notte coltiva il suo hobby: la programmazione. |
<- Mozilla Firefox - Indice Generale - Copertina - DaDaBIK -> |