============================================================================== --------------------[ BFi12-dev - file 02 - 15/02/2003 ]---------------------- ============================================================================== -[ DiSCLAiMER ]--------------------------------------------------------------- Tutto il materiale contenuto in BFi ha fini esclusivamente informativi ed educativi. Gli autori di BFi non si riterranno in alcun modo responsabili per danni perpetrati a cose o persone causati dall'uso di codice, programmi, informazioni, tecniche contenuti all'interno della rivista. BFi e' libero e autonomo mezzo di espressione; come noi autori siamo liberi di scrivere BFi, tu sei libero di continuare a leggere oppure di fermarti qui. Pertanto, se ti ritieni offeso dai temi trattati e/o dal modo in cui lo sono, * interrompi immediatamente la lettura e cancella questi file dal tuo computer * . Proseguendo tu, lettore, ti assumi ogni genere di responsabilita per l'uso che farai delle informazioni contenute in BFi. Si vieta il posting di BFi in newsgroup e la diffusione di *parti* della rivista: distribuite BFi nella sua forma integrale ed originale. ------------------------------------------------------------------------------ -[ HACKiNG ]------------------------------------------------------------------ ---[ ANGEL: THE P0WER T0 PR0TECT - PART II -----[ The Sponge - http://www.sikurezza.org/angel AngeL - The Power to protect "Preludi al chiaro di luna" part 2 The Sponge 0x0 Intro 0x1 AngeL network engine 0x2 Hook function handler 0x2.1 Sp00fing 0x2.2 Sanity check sull'header TCP 0x2.3 Denial of service contro X 0x2.4 Protocol Related Sanity Check 0x2.5 Syn Flood 0x2.6 Land 0x2.7 Smurf 0x2.8 Ping of Death 0x3 Considerazioni e sviluppi futuri 0x4 Bibliografia 0x0 Intro Avete letto vero l'"Ouverture in B minor"? L'articolo pubblicato su BFi dove descrivevo AngeL e dove parlavamo delle contromisure per evitare attacchi host based sulla nostra linux box? Bene, ora tiriamo in ballo il nostro fiammante stack TCP/IP e parliamo della parte net based di AngeL. Come abbiamo fatto nella scorsa puntata facciamo riferimento alla versione di sviluppo di AngeL. Il codice net based e' pressoche' immutato dalla versione "stable" a quella di sviluppo, anzi presto dovro' rimboccarmi le maniche e operare un po' di pulizia del codice e magari rendere piu' semplice l'aggiunta di nuove regole. Siamo quindi nella directory AngeL-0.9.x/src/modules/net , allacciate le cinture, prendete una pinta della vostra birra preferita e cominciamo il nostro viaggio. Un'ultima nota riguarda il kernel. Andremo a toccare, marginalmente, netfilter parlando dell'hook al sistema di firewalling che andremo a piazzare. Anche se AngeL si comporta perfettamente col sistema di firewall del kernel 2.2.xx, parleremo solo di netfilter ignorando deliberatamente il buon vecchio ipchains. Per i pezzi di codice presi in prestito dal kernel, stiamo parlando della versione 2.4.19 del kernel vanilla. 0x1 AngeL network engine Il file engine.c e' il cuore del sottosistema di rete. Il compito del codice contenuto in questo file e' semplice: installare un hook nella catena di output nel sistema di firewalling di netfilter e dare una spulciata ai pacchetti in uscita. Per i paranoici della privacy, AngeL fa un pattern matching nel payload solo per cercare shellcode e presto useremo anche qui la stessa funzione usata in angel_execve() . La prima cosa da fare, per installare un hook su netfilter e' dire al kernel dove piazzare l'hook e soprattuto la funzione handler che sara' chiamata dallo stack TCP/IP quando i pacchetti incontreranno l'hook. Dato che i programmatori di netfilter (che ci hanno regalato un gran pezzo di codice), hanno previsto che piu' funzioni possono essere installate in uno degli hook di netfilter. E` stato quindi introdotto un concetto di priorita' che stabilisce quale funzione verra' chiamata per prima ad ogni pacchetto che attraversa lo stack TCP/IP. Questa e' la struttura che dovremo andare a riempire: struct nf_hook_ops { struct list_head list; /* User fills in from here down. */ nf_hookfn *hook; int pf; int hooknum; /* Hooks are ordered in ascending priority. */ int priority; }; Come si legge dal commento, il campo "list" *NON* dovra' essere riempito. Noi, poveri utenti mortali ( :P ), dovremo toccare solo dal campo "hook" in giu'. Nel dettaglio hook e' il puntatore alla funzione che sara' chiamata dal kernel nel momento opportuno. Ogni volta che un pacchetto, nella catena di netfilter specificata, incontra il punto in cui netfilter si aspetta di trovare le funzioni utente, tali funzioni verranno chiamate secondo l'ordine stabilito dal livello di priorita' utilizzato per registrare le stesse. Queste catene di cui sto parlando individuano sostanzialmente la direzione del flusso di pacchetti che scorrono nella catena stessa. La catena di INPUT e' attraversata dai pacchetti in ingresso dall'interfaccia di rete verso la nostra macchina. La catena di OUTPUT, viceversa, e' attraversata dai pacchetti che sono in uscita. Il pacchetto, nella catena di OUTPUT, e' appena stato costruito e sta per essere inviato al data link layer affinche' ci appiccichi i dati del livello 2 e lo passi alla scheda di rete. Esistono altre catene, ma l'unica che useremo e' quella di OUTPUT. Noi vogliamo controllare il traffico generato dal nostro kernel e che sta per abbandonare la nostra box. Il campo "hooknum" contiene appunto la costante che individua la catena di nostro interesse. Il campo "pf" e' l'acronimo di "protocol family" e indica il protocollo di nostro interesse. Noi vogliamo catturare il traffico del protocollo IPv4, imposteremo questa variabile di conseguenza. Infine l'ultimo campo e' il famoso livello di priorita' che sara' utilizzato per registrare l'hook. Noi vogliamo registrare l'hook alla massima priorita' possibile per avere la certezza di essere i primi a osservare il traffico in uscita. So cosa stai pensando lettore accorto. Tu crei il tuo modulino superbad.o che si registra dopo AngeL e fa le peggio cose. Peccato che incontrando il mio hook per primo, posso decidere, approvando il pacchetto, di far uscire il pacchetto stesso verso il data link layer e quindi il tuo modulino non si vede arrivare nulla. static struct nf_hook_ops angel_ops = { { NULL, NULL }, angel_output_hook, PF_INET, // Work on IP version 4 NF_IP_LOCAL_OUT, /* * Highest priority is set by NF_IP_PRI_FIRST constant. I suppose this * is the highest priority we can use outside netfilter core system. * */ NF_IP_PRI_FILTER - 1 }; Il resto del codice di engine.c e' costituito dalla funzione angel_net_init() che inizializza il sottosistema di rete installando l'hook stesso. Parallelamente e' contenuta una funzione angel_net_shutdown() che rimuove l'hook dal sistema di netfilter. Andiamo un po' nel dettaglio della funzione angel_net_init(). Inizializziamo alcune strutture dati per calcolare delle statistiche per alcuni attacchi di tipo flooding, che inondano di pacchetti l'host vittima. for (i=0;i Allochiamo delle zone di memoria per contenere le contromisure di default contro alcuni attacchi e inizializziamo i comportamenti standard che AngeL utilizzera' per reagire ad un attacco. Tali azioni potranno essere modificate in seguito attraverso il /proc filesystem. actions_buffer.action_message = kmalloc(ANGEL_ACTION_LENGTH, GFP_KERNEL); actions_buffer.angel_synflood_action = kmalloc(ANGEL_ACTION_LENGTH, GFP_KERNEL); actions_buffer.angel_land_action = kmalloc(ANGEL_ACTION_LENGTH, GFP_KERNEL); actions_buffer.angel_spoofing_action = kmalloc(ANGEL_ACTION_LENGTH, GFP_KERNEL); actions_buffer.angel_smurf_action = kmalloc(ANGEL_ACTION_LENGTH, GFP_KERNEL); if (setup_default_angel_behaviour() < 0) return -1; E ora registro l'hook fisicamente. register_result = nf_register_hook (&angel_ops); if (register_result == 0) printk(KERN_INFO "[angel] network subsystem is up and running...\n"); Se qualcosa va storto e "register_result" contiene un valore diverso da 0, angel_net_init() fallisce ed il sottosistema di rete non viene inizializzato. La funzione angel_net_shutdown() non contiene codice di particolare difficolta', l'unico compito e' la de-registrazione dell'hook stesso e la liberazione delle zone di memoria allocate. Adesso che siamo giunti a meta' della prima pinta di birra, andiamo a spulciare del codice veramente importante. Andiamo a vedere cosa c'e' in angel_output_hook() . 0x2 Hook function handler Tutto ha inizio alla riga 50 del file angel_hook.c . unsigned int angel_output_hook ( unsigned int hook, struct sk_buff **pskb, const struct net_device *indev, const struct net_device *outdev, int (*okfn)(struct sk_buff *)) { Il prototipo della funzione hook che netfilter si aspetta segue fedelmente quanto recitato da: typedef unsigned int nf_hookfn(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)); Il codice di angel_output_hook comincia a recuperare puntatori utili alla struttura skbuff che corrisponde al pacchetto in questione, alla struttura dell'header TCP e dell'header IP e comincia subito il rock & roll. Niente pacchetti con roba nel campo "options" dell'header IP per favore. Visto che il traffico legale non ha bisogno di opzioni a livello rete, per evitare che pacchetti con opzioni possano causare problemi ad altri host buttiamo a mare questi pacchetti "strani". if (ip->ihl != 5) { printk(KERN_INFO "[angel]: Ip header comes with options. Killed.\n"); angel_net_stats.NetStats[IP_INSANITY]++; angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; } Le costanti ANGEL_KILL e ANGEL_SAFE_PACKET hanno lo stesso valore di costanti definite da netfilter e indicano la sorte destinata ad un pacchetto. ANGEL_KILL significhera' che il pacchetto verra' scartato e il kernel si occupera' di liberare le risorse allocate per il pacchetto in questione (tuttavia non verra' chiusa la socket legata a questa comunicazione). ANGEL_SAFE_PACKET d'altro canto indica che il pacchetto puo' lasciare il sistema di firewall e deve essere passato al livello data link. 0x2.1 Sp00fing AngeL fa in modo che il nostro host sia onesto? Bene, allora non spooferemo il nostro indirizzo IP. :) if (IS_SPOOFING(skb)) { switch (actions_code.angel_spoofing_code) { case ANGEL_KILL : printk(KERN_INFO "[angel]: Spoofing attempt no. ( %ld ). Killed.\n", ++angel_net_stats.NetStats[SPOOFING]); angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; break; case ANGEL_WARNING : printk(KERN_INFO "[angel]: Spoofing attempt no. ( %ld ). Warning.\n", ++angel_net_stats.NetStats[SPOOFING]); break; case ANGEL_SAFE_PACKET : return ANGEL_SAFE_PACKET; break; default: printk(KERN_CRIT "[angel]: Unexpected action for spoofing... just drop\n"); angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; break; } } // End of spoofing handling La macro IS_SPOOFING controlla il contenuto della struttura sk_buff e poi vengono compiute le azioni previste per l'attacco in questione. D'ora in poi evitero' di riportare la gestione delle azioni previste per gli attacchi perche' tanto non cambiano mai. Ecco la macro IS_SPOOFING in tutto il suo splendore. #define IS_SPOOFING(skb) (angel_ip_spoofing(skb) == ANGEL_IP_SPOOF) Boom, sorpresa: chiamo la funzione angel_ip_spoofing che fa tutti i controlli del caso. Ok, siamo seri per un po', andiamo a vedere cosa fa: int angel_ip_spoofing (const struct sk_buff *skb) { ... for (dev = dev_base; dev; dev = dev->next) { if (!(in_dev = dev->ip_ptr)) continue; for (ifap = in_dev->ifa_list; ifap;ifap = ifap->ifa_next) if (skb->nh.iph->saddr == ifap->ifa_local) { #ifdef IS_KERNEL_4 spin_unlock(&dev->queue_lock); #endif #ifdef IS_KERNEL_2 dev_unlock_list(); #endif return ANGEL_IP_REGULAR; } } #ifdef IS_KERNEL_2 dev_unlock_list(); #endif return ANGEL_IP_SPOOF; } Sostanzialmente scorriamo la lista dei dispositivi di rete andando a controllare che almeno uno dei dispositivi di rete corrisponda all'indirizzo IP del pacchetto in uscita. Se troviamo il dispositivo allora il pacchetto e' valido, altrimenti no. 0x2.2 Sanity check sull'header TCP Passiamo a controllare ora che l'intestazione del protocollo TCP sia regolare e non abbia combinazioni strane dei flag. if (skb->nh.iph->protocol == IPPROTO_TCP) { tcp = ((struct tcphdr *) (((char *) skb->nh.iph) + skb->nh.iph->ihl * 4)); if (angel_tcp_sanity_check(tcp) == ANGEL_TCP_INSANE) { printk(KERN_INFO "[angel]: Tcp sanity check failure no. ( %ld ). Killed.\n", ++angel_net_stats.NetStats[IP_INSANITY]); angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; } ... E la funzione angel_tcp_sanity_check() fa questi controlli. int angel_tcp_sanity_check (const struct tcphdr *tcp) { // Drop Xmas !! Ymax if (tcp->res1 != 0) return ANGEL_TCP_INSANE; // Drops SYN without ACK but with other flags set if ((tcp->syn && !tcp->ack) && (tcp->fin || tcp->rst || tcp->psh || tcp->urg)) return ANGEL_TCP_INSANE; // Drops SYN/ACK with RST and/or FIN set if ((tcp->syn && tcp->ack) && (tcp->fin || tcp->rst)) return ANGEL_TCP_INSANE; // Drops TCP packet with no-sense flags ( or without flags at all ) if (!tcp->fin && !tcp->syn && !tcp->rst && !tcp->ack) return ANGEL_TCP_INSANE; return ANGEL_TCP_SANE; } Ok, dalla nostra box escono solo pacchetti con l'header TCP valido, un passo avanti, un gran bel passo avanti. 0x2.3 Denial of service contro X Anche se questo e' un attacco locale (fortunatamente le connessioni remote a Xserver devono essere autorizzate esplicitamente), utilizza il protocollo TCP/IP e quindi casca qui dentro. if (angel_X_dos(skb)) { printk(KERN_INFO "[angel]: Angel X dos attempt ( %ld ). UID: %d.\n", ++angel_net_stats.NetStats[X_DOS], current->uid); angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; } Quello che viene controllato e' che il traffico sia diretto effettivamente verso la porta 6000 (quella dell'Xserver) e che il payload, se presente, non contenga un valore negativo per una certa direttiva che puo' essere specificata a X. Questo valore negativo, non controllato correttamente dalle versioni di X precedenti alla versione 3.3.6, causa un DoS in quanto il codice di X entra in un loop cercando di decrementare il valore negativo ricevuto per raggiungere 0, cosa che ovviamente non accadra' mai. int angel_X_dos(struct sk_buff *skb) { ... if (tcp->dest != htons(6000)) return 0; // controllo che il pacchetto inviato contenga dati utili if(!CONTAIN_DATA(skb)) return 0; data=SKB_TCP_DATA(skb); if (!strcmp(data,"")) return 0; len_buf=strlen(buf); len_data=TCP_LENGTH_DATA(skb); // qualcuno sta cercando di riempirmi il buf if (len_data+strlen(buf)>sizeof(buf)) memset(buf,0,sizeof(buf)); if (len_data>sizeof(buf)) { printk(KERN_INFO "[Angel]Possible buffer overflow in X loop (angel_X_dos"); return 1; } strncat(buf, data,len_data); if ((p=strstr(buf, "XC-QUERY-SECURITY-1")) != NULL) { if (strlen(buf)!=len_data+len_buf) strncat(buf,(char *)(strchr(data,0)+1),len_data+len_buf-strlen(buf)-1); if ((strlen(p)>20)&&(p[20]<0)) { memset(buf,0,sizeof(buf)); (* skb->sk->prot->disconnect)(skb->sk,0); (* orig_sys_kill)(current->pid,9); return 1; } if ((strlen(p)>20)&&(p[20]>=0)) { memset(buf,0,sizeof(buf)); return 0; } } return 0; } 0x2.4 Protocol Related Sanity Check Continuando l'analisi della funzione angel_output_hook() (contenuta nel file angel_hook.c ), ci imbattiamo, dopo aver verificato il traffico verso il server X, in una serie di controlli sul traffico dei protocolli solitamente utilizzati in una Linux box connessa in Internet. ... // HTTP Traffic if ((http=ctrl_http_traffic(skb))!=0) { printk(KERN_INFO "[angel]: HTTP arbitrary command execution attempt. Recognized string N. %d ( %ld ). UID: %d.\n", http, ++angel_net_stats.NetStats[HTTP], current->euid ); angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; } // FTP Traffic if (ctrl_ftp_traffic(skb)) { printk(KERN_INFO "[angel]: FTP remote exploit attempt ( %ld ). UID: %d.\n", ++angel_net_stats.NetStats[FTP], current->uid); angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; } ... Come si puo' osservare i controlli in angel_hook() seguono una struttura molto chiara: se la funzione ctrl_[protocol]_traffic(struct sk_buff *sk) restituisce un valore diverso da 0 allora e' in corso un attacco informatico attraverso il protocollo in esame. La semantica del valore restituito dipende dalla funzione specifica, ma il piu' delle volte sara' l'indice del pattern dell'attacco riconosciuto dalla funzione. Anche le funzioni ctrl_[protcol]_traffic() hanno una struttura abbastanza simile l'una con l'altra. Prendiamo ctrl_http_traffic come caso di studio. La funzione si apre con la dichiarazione del vettore contente i pattern riconosciuti in letteratura come attacchi utilizzati attraverso il protocollo http. int ctrl_http_traffic(struct sk_buff *skb) { char *str; static char *strget; // string that will contain the payload static int strgetsize=0; // length of strget static char *http_string[PHFKNOWN+ IISKNOWN+INFOKNOWN+ALIBABAKNOWN+ALIENKNOWN+AMLITEKNOWN+BIZKNOWN+1] = { PHF1, // phf arbitrary command attempt PHF2, PHF3, IIS1, IIS2, // internet information server IIS3, IIS4, INFOSEARCH1, // infosearch arbitrary command attempt ALIBABA1, ALIENFORM1, AMLITE1, BIZDB1, NULL }; ... Successivamente si controlla che questo traffico sia destinato verso la porta 80 di un server e che contenga un payload. Questi controlli sono eseguiti tramite due macro: IS_TO_PORTN() e CONTAIN_DATA() entrambe definite in angel_http.h ma accessibili da tutte le funzioni ctrl_xxx_traffic grazie a giri di "include". La strategia utilizzata e' quella di salvare il payload in una stringa sufficientemente grande e verificare se contiene uno dei pattern riconosciuti. if ( (IS_TO_PORTN(skb,80)) && (CONTAIN_DATA(skb)) ) { /* * La stringa strget e' statica quindi ogni volta controllo che * non sia troppo piccola per ospitare il nuovo payload e provvedo * a riallocarne una piu' grossa. */ if (strgetsize=strlen(http_string[i])) if(strstr(strget,http_string[i])) { angel_tcp_close(skb->sk, 0); (*orig_sys_kill)(current->pid,9); return (i+1); } } Se all'interno del ciclo viene trovata una stringa riconosciuta come pericolosa, la socket viene chiusa attraverso la funzione angel_tcp_close() , ed il processo corrente ucciso. La funzione angel_tcp_close() e' una novita' introdotta sviluppando la versione 0.9.1 (e immediatamente propagata nella versione 0.8.10 che credo sia gia' stata rilasciata mentre leggete questo articolo). Il kernel fornisce una comoda tcp_close() per chiudere le connessioni tcp rilasciando in maniera opportuna tutte le strutture interne (fa una cosa analoga anche una funzione per rilasciare strutture allocate per "connessioni" udp; si ricordi che parlare di connessione udp non ha senso). Il problema (analogo alla gestione della funzione handle_sys_request vista nella prima parte dell'articolo) e' che il kernel esporta questo simbolo in due casi: *) e' configurato il supporto a IPv6; *) e' configurato il supporto per il web server khttp. Quindi in fase di compilazione del modulo, uno script ( src/modules/buildutils/netHelperFind.sh ) prende in prestito l'indirizzo di questa funzione che viene assegnato all'handler angel_tcp_close . Ovviamente se lo script non trova il simbolo verra' utilizzato il vecchio metodo per chiudere la socket, ovvero utilizzare una funzione il cui indirizzo e' contenuto in sk_buff , ma l'approccio attraverso la tcp_close() e' senza dubbio il migliore. Il lettore smaliziato avra' notato un flow di questo ragionamento. Lo script si basa prima su /proc/ksyms e poi su /boot/System.map per recuperare l'indirizzo, ma il file /boot/System.map potrebbe non essere quello del kernel attualmente in esecuzione. Questo avrebbe conseguenze imprevedibili sul comportamento di angel_tcp_close() . Ovviamente si stanno cercando soluzioni al problema: a riguardo vi sono accese discussioni sulla nostra mailing list (a proposito venite a trovarci su angel@sikurezza.org , ringraziamo ancora il grande Koba per l'ospitalita'). Si noti che, prima o poi, AngeL sara' una patch al kernel e non sara' piu' compilabile come modulo. Questo anche alla luce della rimozione del vettore delle system call dai nuovi kernel 2.5.xx; AngeL dovra' essere qualcosa di integrante nel kernel e disattivabile al limite con il noto meccanismo di password, ma mai rimuovibile del tutto (come invece accade ora). 0x2.5 Syn Flood Ultimati i controlli sul payload dei pacchetti, controlli che erano come ricordo application related, AngeL analizza se il pacchetto in uscita costituisce un tentativo di syn flood. Per chi fosse a digiuno di quanto sto raccontando, rimando a [1] per la trattazione completa ed esaustiva di come si stabilisce una connessione utilizzando il protocollo tcp (il three-way handshake). Basti sapere al lettore, per capire cosa sia il syn flood, che se io mando un numero di richieste di connessione (pacchetti TCP/IP aventi il flag SYN settato) molto grande in un breve lasso di tempo, l'host che li riceve alloca strutture interne al proprio kernel per ospitare le possibili connessioni e comincia ad inviare pacchetti SYN/ACK in risposta. Se io, l'attacker, non invio pacchetti ACK che concluderebbero il three-way handshake, l'host attaccato resta in attesa fino a quando le socket che ha predisposto non vanno in timeout. Se l'attacker invia un numero MOLTO grande di SYN ad un rate MOLTO grande, la vittima (o meglio il servizio che stiamo attaccando ad esempio il demone SMTP) esaurira' le proprie risorse e non sara' piu' in grado di accettare connessioni. Abbiamo causato quindi un DoS poco simpatico per chi se lo vede arrivare. La soluzione applicata da AngeL e' quella di, se il pacchetto ha il flag SYN asserito, incrementare un contatore in un vettore il cui indice e' ottenuto calcolando una funzione hash sull'indirizzo di destinazione. Quello che si cerca di fare e' cercare di capire il numero di SYN che si stanno inviando ad un determinato host (collisioni della funzione hash a parte). if (IS_SYN_FLOOD(skb)) { hash_value = hash(skb->nh.iph->daddr); hashtable.syn_packets[hash_value]++; ... Se il valore di questo contatore supera una certa soglia (definita a compile time e hard coded nel programma), AngeL reagira' interpretando il pacchetto come facente parte di un attacco e prendera' le dovute contromisure. if (hashtable.syn_packets[hash_value] >= SYN_HARD_LIMIT) { switch (actions_code.angel_synflood_code) { case ANGEL_KILL : printk(KERN_INFO "[angel]: Synflood attempt no. ( %ld ). Killed.\n", ++angel_net_stats.NetStats[SYNFLOOD]); #ifdef VERBOSE printk(KERN_INFO "[angel]: Synflood to %d.%d.%d.%d\n", NIPQUAD(ip->daddr)); #endif angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; case ANGEL_WARNING : printk(KERN_INFO "[angel]: Synflood attempt no. ( %ld ). Warning.\n", ++angel_net_stats.NetStats[SYNFLOOD]); #ifdef VERBOSE printk(KERN_INFO "[angel]: Synflood to %d.%d.%d.%d\n", NIPQUAD(ip->daddr)); #endif return ANGEL_SAFE_PACKET; break; case ANGEL_SAFE_PACKET: return ANGEL_SAFE_PACKET; default: printk(KERN_CRIT "[angel]: Unexpected action for spoofing... just drop\n"); angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; break; } } } // End of syn flood handler L'approccio euristico di questo tipo non e' sempre ben visto, specie dai sysadmin piu' esigenti. Effettivamente potrebbe anche prestarsi a falsi positivi se un'applicazione legittima dovesse aver bisogno di inviare SYN ad un rate elevato. Il campo tuttavia ha premiato l'approccio di AngeL che si e' dimostrato sufficientemente efficace contro questo tipo di DoS. 0x2.6 Land Su questo vecchio tipo di DoS c'e' poco da dire ed il codice di AngeL predisposto ad impedire che un attacco del genere parta e' abbastanza esplicativo. Il tutto si basa sull'impedire che si inviino dei pacchetti aventi indirizzo IP sorgente e destinazione e il numero di porta sorgente e destinazione uguali a i valori dell'host e del servizio che si vuole attaccare. Si pensi al loop che si innescherebbe se cercassimo di connetterci ad un servizio su una macchina facendoci riconoscere come la macchina stessa e fornendo la porta del servizio come porta a cui rispondere. if (IS_LAND(skb)) { if (ANGEL_LAND_ACTION == ANGEL_KILL) { printk(KERN_INFO "[angel]: Land attempt no. ( %ld ). Killed.\n", ++angel_net_stats.NetStats[LAND]); #ifdef VERBOSE printk(KERN_INFO "[angel]: Land to %d.%d.%d.%d\n", NIPQUAD(ip->daddr)); #endif angel_net_stats.NetStats[COUNTER_DROP]++; //return NF_DROP; return ANGEL_KILL; } else { printk(KERN_INFO "[angel]: Land attempt no. ( %ld ). Warning.\n", ++angel_net_stats.NetStats[LAND]); #ifdef VERBOSE printk(KERN_INFO "[angel]: Land to %d.%d.%d.%d\n", NIPQUAD(ip->daddr)); #endif return ANGEL_SAFE_PACKET; } } // End of land handler 0x2.7 Smurf Per gli ultimi due tipi di attacco abbiamo lasciato il protocollo TCP e ci siamo avventurati nell'ICMP. All'interno di angel_hook questo e' equivalso a spostarsi da un if all'altro. L'attacco chiamato smurf viene eseguito mandando un pacchetto di echo request ad un indirizzo di broadcast di una rete (interna o esterna) che fungera' da amplificatore per il nostro attacco. Ovviamente l'attaccante mascherera' il proprio indirizzo IP con quello della vittima che si vedra' provenire un elevato numero di echo reply che sottrarranno banda preziosa all'host attaccato. Una nota per il lettore: le definizioni degli attacchi come IS_SYN_FLOOD , IS_LAND , IS_SMURF possono essere trovate in include/attack_rulez.h . #define IS_SMURF(skb) ((angel_ip_spoofing(skb)==ANGEL_IP_SPOOF) &&\ (skb->sk->broadcast) && \ ((((struct icmphdr *) (((char *)skb->nh.iph) +\ skb->nh.iph->ihl * 4)))->type==ICMP_ECHO)) Il codice per la gestione dell'attacco e' infine banale: if (IS_SMURF(skb)) { switch (actions_code.angel_smurf_code) { case ANGEL_KILL : printk(KERN_INFO "[angel]: Smurf attempt no. ( %ld ). Killed.\n", ++angel_net_stats.NetStats[SMURF]); #ifdef VERBOSE printk(KERN_INFO "[angel]: Attempt to flood %d.%d.%d.%d\n", NIPQUAD(ip->saddr)); #endif angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; case ANGEL_WARNING : printk(KERN_INFO "[angel]: Smurf attempt no. ( %ld ). Warning.\n", ++angel_net_stats.NetStats[SMURF]); #ifdef VERBOSE printk(KERN_INFO "[angel]: Attempt to flood %d.%d.%d.%d\n", NIPQUAD(ip->saddr)); #endif return ANGEL_SAFE_PACKET; break; case ANGEL_SAFE_PACKET: return ANGEL_SAFE_PACKET; default: printk(KERN_CRIT "[angel]: Unexpected action for smurf... just drop\n"); angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; break; } 0x2.8 Ping of Death Il Ping of death fu un attacco che ebbe un serio impatto sugli host collegati in Internet attorno al 1995-1996. Correva l'anno di Windoze95 e del kernel 1.3.xx e ci si accorse che quando un host (non ricordo sinceramente la lista dei sistemi operativi affetti dall'attacco) riceveva un echo request contenuto in un pacchetto IP costruito in maniera opportuna, tale host andava in crash. L'attacco sfruttava la possibilita', attraverso la frammentazione, di creare una echo request contenuta in un pacchetto IP la cui dimensione superava i 65535 byte; il kernel ospite, al momento del riassemblaggio del pacchetto vedeva andare in overflow le proprie strutture interne causando il blocco del kernel. L'approccio operato da AngeL consiste nel controllare che una echo request sia contenuta in un pacchetto IP che non necessita di frammentazione, anche perche' osservando il protocollo ICMP un "ping" non richiede piu' di un pacchetto IP. #define IS_PINGOFDEATH(skb) ((skb->nh.iph->frag_off == htons(0x2000)) &&\ (((struct icmphdr *) (((char *)skb->nh.iph) + \ skb->nh.iph->ihl * 4)))->type == ICMP_ECHO) Nel codice di angel_hook , il controllo per questo tipo di attacco e' banale: if (IS_PINGOFDEATH(skb)) { printk(KERN_INFO "[angel]: Ping of death attempt no. ( %ld ). Killed.\n", ++angel_net_stats.NetStats[PINGOFDEATH]); angel_net_stats.NetStats[COUNTER_DROP]++; return ANGEL_KILL; } 0x3 Considerazioni e sviluppi futuri Con queste righe si conclude il nostro tour in AngeL. Questo pezzo di codice e' in giro da 2 anni e necessiterebbe di maggior cura, sono il primo a cospargermi il capo di cenere. Tuttavia sono convinto che in futuro riusciremo a fare ancora qualcosa di buono e a tirare fuori novita' interessanti per impedire che i nostri host facciano i discoli all'interno del web. Questa convinzione deriva anche dalla validita' delle persone iscritte alla mailing list angel@sikurezza.org (cito buffer, alor ed il grande gg sullivan solo come esempio) e dal loro impegno nel portare avanti questo progetto. Cosa dire ora, il network layer andrebbe riscritto da zero, ad occhi attenti ad esempio non sara' passato inosservato il fatto che, nel caso di buffer overflow remoti, ogni controllo viene vanificato se l'attacco e' portato frammentando i pacchetti. Deve essere seguito un approccio migliore in fase di progettazione dell'hook, in maniera tale da ottenere un codice che sia piu' efficace e meno soggetto a falsi positivi o a problemi di varie nature. Tale codice andra' reso piu' flessibile adottando una struttura a plugin che mi consenta di aggiungere regole e contromisure contro attacchi nuovi senza dover aspettare una nuova release del modulo. Quello che ho in mente e' un sistema "snort like" dove ognuno possa istruire AngeL attraverso una sorta di linguaggio ed inserire nuovi pattern di attacchi. Parole vanno poi spese per la struttura di AngeL in quanto modulo. Durante lo sviluppo tra problemi di simboli non esportati e di dirottamento di system call al limite del lecito, sinceramente sono un po' stufo di nascondermi dietro ad un modulo. In futuro, anche perche' il kernel 2.6 avra' LSM come framework di security di default, non e' detto che AngeL non si interfacci ed usi tale framework oppure che il nostro progetto non miri a diventare una patch statica al kernel (sulla scia delle patch di openwall per intenderci). Spero di avervi un po' incuriosito nell'esplorazione della kernel land ed in particolare di cosa si puo' fare mettendo un hook nelle catene di netfilter. Spero che siate invogliati ad iscrivervi alla nostra mailing list anche solo per sommergerci di critiche (costruttive), ma spero con forza che siate motivati a partecipare nello sviluppo del codice e nel migliorarlo aggiungendo pattern per nuovi attacchi, segnalandoci nuovi attacchi, facendo bug report e magari pagandoci una pinta di birra, scura ovviamente. Dicendo questo, ringrazio voi tutti gentili lettori per avermi fatto compagnia fino a questo punto. Vi do' appuntamento, spero, ad altri articoli ed invito tutti all'hacking piu' sfrenato, quello che stimola la nostra voglia di apprendere e sperimentare. TheSponge 0x4 Bibliografia [0] /usr/src/linux, Kernel sources, Autori Vari [1] W. Richard Stevens "TCP/IP Illustrated, Volume 1 The Protocols", Addison-Wesley [2] www.google.com [3] www.sikurezza.org/angel -[ WEB ]---------------------------------------------------------------------- http://www.bfi.cx http://bfi.freaknet.org http://www.s0ftpj.org/bfi/ -[ E-MAiL ]------------------------------------------------------------------- bfi@s0ftpj.org -[ PGP ]---------------------------------------------------------------------- -----BEGIN PGP PUBLIC KEY BLOCK----- Version: 2.6.3i mQENAzZsSu8AAAEIAM5FrActPz32W1AbxJ/LDG7bB371rhB1aG7/AzDEkXH67nni DrMRyP+0u4tCTGizOGof0s/YDm2hH4jh+aGO9djJBzIEU8p1dvY677uw6oVCM374 nkjbyDjvBeuJVooKo+J6yGZuUq7jVgBKsR0uklfe5/0TUXsVva9b1pBfxqynK5OO lQGJuq7g79jTSTqsa0mbFFxAlFq5GZmL+fnZdjWGI0c2pZrz+Tdj2+Ic3dl9dWax iuy9Bp4Bq+H0mpCmnvwTMVdS2c+99s9unfnbzGvO6KqiwZzIWU9pQeK+v7W6vPa3 TbGHwwH4iaAWQH0mm7v+KdpMzqUPucgvfugfx+kABRO0FUJmSTk4IDxiZmk5OEB1 c2EubmV0PokBFQMFEDZsSu+5yC9+6B/H6QEBb6EIAMRP40T7m4Y1arNkj5enWC/b a6M4oog42xr9UHOd8X2cOBBNB8qTe+dhBIhPX0fDJnnCr0WuEQ+eiw0YHJKyk5ql GB/UkRH/hR4IpA0alUUjEYjTqL5HZmW9phMA9xiTAqoNhmXaIh7MVaYmcxhXwoOo WYOaYoklxxA5qZxOwIXRxlmaN48SKsQuPrSrHwTdKxd+qB7QDU83h8nQ7dB4MAse gDvMUdspekxAX8XBikXLvVuT0ai4xd8o8owWNR5fQAsNkbrdjOUWrOs0dbFx2K9J l3XqeKl3XEgLvVG8JyhloKl65h9rUyw6Ek5hvb5ROuyS/lAGGWvxv2YJrN8ABLo= =o7CG -----END PGP PUBLIC KEY BLOCK----- ============================================================================== -----------------------------------[ EOF ]------------------------------------ ==============================================================================