Articoli Manifesto Tools Links Canali Libri Contatti ?
OpenBSD / Networking

PF e la normalizzazione del traffico di rete

Abstract
Questo documento tratta della normalizzazione del traffico di rete.
La prima parte affronta l'argomento dal punto di vista teorico. La seconda parte presenta alcuni stralci di codice, ampiamente commentati, dell'implementazione della direttiva "scrub" all'interno del Packet Filter di OpenBSD. La terza parte illustra brevemente il sanity check eseguito dall'estensione unclean di IPTables, il firewall dei sistemi GNU/Linux.
Data di stesura: 29/12/2003
Data di pubblicazione: 05/04/2004
Ultima modifica: 04/04/2006
di Gianluigi Spagnuolo Discuti sul forum   Stampa

Introduzione

Un normalizzatore (normalizer) altro non è che un dispositivo che si pone nei punti di ingresso della rete con l'unico scopo di rimuovere le eventuali ambiguità presenti nel flusso di pacchetti che lo attraversa.

PARTE I

Cosa deve fare

Lo scopo del normalizzatore è quello di convertire i pacchetti che presentano delle "ambiguità", e che potrebbero essere interpretati in modo differente dai vari destinatari finali, in un flusso "normalizzato" e omogeneo, univocamente interpretato da tutti gli end-point.

A prima vista un meccanismo del genere potrebbe sembrare quantomeno superfluo, invece risulta essere di grande utilità soprattutto se affiancato ad un Network Intrusion Detection System o ad un firewall.

I NIDS basano la loro efficacia sul concetto che osservando i pacchetti in transito su una interfaccia sia possibile predirne il comportamento, e bloccarli se risultano dannosi per il destinatario finale.
Tutto questo non ha più senso quando il NIDS incontra pacchetti creati ad-hoc da un eventuale attaccante e viene aggirato (attacchi di inserzione ed evasione dei pacchetti [1]).
In realtà, differenze nell'interpretazione del flusso di dati, si verificano anche quando un NIDS ha a che fare con traffico "regolare", questo accade perché, in genere, un network IDS si trova su una macchina diversa da quella dell'end-point e osserva un punto completamente diverso della rete; queste discrepanze sono il frutto di diverse implementazioni dei driver di rete e differenze fisiche tra le macchine.
In Tabella 1 c'è un esempio di quanto appena detto, è rappresentato il comportamento dei vari sistemi operativi in presenza di overlap dei frammenti IP.

+-------------------+--------------------------------------------+
| Sistema Operativo |                              Comportamento |
+-------------------+--------------------------------------------+
| Windows NT        | Prende sempre in considerazione i dati che |
|                   | arrivano per primi                         |
+-------------------+--------------------------------------------+
| 4.4BSD            | Prende sempre in considerazione i nuovi    |
|                   | frammenti in caso di forward overlap       |
+-------------------+--------------------------------------------+
| Linux             | Prende sempre in considerazione i nuovi    |
|                   | frammenti in caso di forward overlap       |
+-------------------+--------------------------------------------+
| Solaris           | Prende sempre in considerazione i dati che |
|                   | arrivano per primi                         |
+-------------------+--------------------------------------------+
| HP-UX             | Prende sempre in considerazione i nuovi    |
|                   | frammenti in caso di forward overlap       |
+-------------------+--------------------------------------------+
| Irix              | Prende sempre in considerazione i nuovi    |
|                   | frammenti in caso di forward overlap       |
+-------------------+--------------------------------------------+
Ebbene il normalizer rimuove le ambiguità (volute o casuali) presenti nel flusso in ingresso, passando al NIDS un flusso normalizzato in modo da metterlo in condizioni di prevedere correttamente il comportamento del destinatario finale.
A grandi linee lo stesso ragionamento vale per i firewall.

Sebbene lo scopo principale del normalizer sia quello di normalizzare i pacchetti, spesso il traffico non può essere corretto in accordo con le specifiche del protocollo. In questi casi l'azione da intraprendere dipende dalla configurazione del normalizzatore, non c'è bisogno di aggiungere che una politica di sicurezza seria prevede in questi casi il rifiuto di tutti i pacchetti illegali e non "normalizzabili".

Oltre a correggere le incongruenze, il normalizer interviene anche su alcuni pacchetti che, benché siano perfettamente legali dal punto di vista delle specifiche del protocollo, potrebbero creare problemi agli end-point.
Ad esempio interviene sul campo TTL (Time-To-Live) dei pacchetti IP, in modo da evitare che i pacchetti con un valore basso di TTL, visti dal NIDS, non arrivino al destinatario finale.

Oltre ad intervenire sul traffico in ingresso, un normalizzatore opera anche sui pacchetti in uscita.
Ad esempio va a sovrascrivere il valore del campo "identification" dei pacchetti IP con un valore casuale, evitando in questo modo i port scan di tipo stealth e rendendo più complicato il lavoro dei vari tool per il fingerprinting.

Cosa non deve fare

Un normalizzatore deve solo normalizzare il traffico e nient'altro, non si deve occupare né di filtrare i pacchetti né di individuare possibili attacchi, a questo ci pensano rispettivamente i firewall e i network intrusion detection system.
Perché quando si implementa un dispositivo di normalizzazione bisogna prestare particolare attenzione alle performance, il rischio, non tanto remoto, è quello di congestionare la rete.
Un normalizzatore deve garantire un buon attraversamento dei pacchetti irregolari, che devono essere normalizzati e quindi riscritti, e inoltre non deve avere un impatto negativo sul traffico regolare.
L'incidenza sulle prestazioni generali della rete non è facilmente quantificabile, troppe sono le parti in gioco; bisogna tener conto del protocollo che si va a normalizzare, del tipo di rete, del numero di pacchetti, della complessità delle regole di scrubbing, dell'efficienza della scheda di rete e di tutto l'hardware in generale, etc.

Come funziona

In genere un normalizzatore opera prevalentemente, ma non solo, sugli header dei pacchetti da esaminare.
Ogni campo dell'intestazione viene confrontato con un range di valori ammessi, e ne viene controllato il contenuto alla ricerca di ambiguità e di valori che potrebbero avere degli effetti indesiderati sull' end-point.

Ad esempio, nel caso di normalizzazione IP, in genere, si controlla se la lunghezza dell'header è troppo lunga o troppo corta, se la lunghezza totale è corretta, controlla se gli indirizzi di partenza e di destinazione sono validi, esamina il campo TTL e le opzioni, verifica la correttezza del checksum e così via.
Nel caso di normalizzazione TCP, oltre alle verifiche riguardanti la lunghezza di header e la validità del checksum, vengono eseguiti controlli sulla correttezza dei flag e sul numero di sequenza. Per gli altri protocolli, a grandi linee, valgono le stesse considerazioni.

Di seguito vedremo in dettaglio un esempio di implementazione di un normalizzatore, e più precisamente poseremo l'attenzione sulla direttiva scrub del Packet Filter di OpenBSD, senza dimenticare Linux e IPTables.

PARTE II

Real World: la direttiva Scrub del Packet Filter di OpenBSD

La direttiva scrub di PF si occupa della normalizzazione e della deframmentazione del traffico TCP/IP.
In questo documento ci occuperemo solo del primo aspetto.

La sintassi della direttiva scrub è molto simile alla sintassi per il filtraggio, questo rende facile usare scrub selettivamente solo su alcuni pacchetti. Un semplice esempio dell'uso di scrub all'interno delle regole di pf è il seguente:

scrub in all
questa riga applicherà la direttiva scrub a tutti i pacchetti in ingresso su tutte le interfacce.

Oltre all'uso barbaro visto poc'anzi, è possibile usare nelle regole di scrubbing le seguenti opzioni riguardanti la normalizzazione:

no-df azzera il bit "don't fragment" nell'intestazione dei pacchetti IP, vedremo in seguito l'utilizzo di questa opzione;
random-id sostituisce il campo "identification" dei pacchetti IP in uscita con un valore casuale per compensare i valori predicibili usati da alcuni sistemi operativi;
min-ttl num impone un valore minimo (num) del "Time To Live" (TTL) nell'intestazione dei pacchetti IP;
max-mss num impone un valore massimo (num) del "Maximum Segment Size" (MSS) nell'intestazione dei pacchetti TCP.
Le opzioni che gestiscono i frammenti sono invece "fragment reassemble", "fragment crop" e "fragment drop-ovl" che si occupano della gestione dei frammenti e del trattamento dei frammenti duplicati o che si vanno a sovrapporre a quelli già ricevuti.

Un esempio un po' più rappresentativo di una regola di scrubbing è il seguente:

scrub in on rl0 all fragment reassemble min-ttl 20 max-mss 1400
Per maggiori dettagli sulla sintassi della direttiva scrub rimando alle FAQ ufficiali di OpenBSD [3].

Senza entrare nei dettagli della deframmentazione vediamo di seguito come è implementata la normalizzazione all'interno di PF.

Nota: il codice utilizzato nella stesura di questo articolo fa riferimento alla versione 3.3 dei sorgenti di OpenBSD [2].

Normalizzazione IP

La funzione che si occupa di normalizzare il traffico IP [7] all'interno di PF è pf_normalize_ip in /usr/src/sys/net/pf_norm.c [2], ecco di seguito il prototipo:
int pf_normalize_ip(struct mbuf **, int, struct ifnet *, u_short *);
Le variabili che incontreremo negli stralci di codice hanno il seguente significato: "h" è l'header del pacchetto da normalizzare, e "r" è una struttura di tipo "pf_rule" definita in pfvar.h, che in sostanza rappresenta il contenuto di una regola di PF, in questo caso è una regola per lo scrubbing.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Identification        |Flags|      Fragment Offset    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |         Header Checksum       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Source Address                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination Address                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Il pacchetto viene scartato se la lunghezza della sua intestazione (hlen) è minore di 20 byte, ovvero se l'header, raffigurato in figura 1 è incompleto:
if (hlen < (int)sizeof(struct ip)) goto drop;
e viene scartato anche se hlen è maggiore della lunghezza totale del pacchetto:
if (hlen > ntohs(h->ip_len)) goto drop;
Se il flag "Don't fragment" è settato e il fragment offset è diverso da zero il pacchetto viene scartato in quanto malformato:
if (h->ip_off & htons(IP_DF)) {
  goto bad;
}
Alcuni sistemi operativi, però, generano pacchetti frammentati con il bit "don't fragment" settato, questo succede ad esempio con NFS.
Come visto in precedenza, scrub rifiuterà tali pacchetti a meno che non sia specificata l'opzione "no-df". Usando l'opzione "no-df" invece viene azzerato il bit "don't fragment" nell'intestazione dei pacchetti IP.

Se viene usata l'opzione min-ttl di scrub, che impone un valore minimo del Time To Live (TTL) nell'intestazione dei pacchetti IP, il TTL di ogni pacchetto (h->ip_ttl) viene forzato al valore passato a scrub.
Ad esempio con la regola

scrub in on rl0 all min-ttl 20
si impone il valore 20 come TTL per tutti quei pacchetti che hanno un valore inferiore. Questa è una opzione da usare con cautela in quanto c'è la possibilità di creare loop infiniti, oltre a creare problemi a traceroute e simili.
if (r->min_ttl && h->ip_ttl < r->min_ttl)
  h->ip_ttl = r->min_ttl;
Se è impostata l'opzione "random-id" all'interno della regola di scrubbing il valore del campo "identification" dei pacchetti IP in uscita viene sostituito con un valore casuale, generato dalla funzione ip_randomid, per compensare i valori predicibili usati da alcuni sistemi operativi.
if (r->rule_flag & PFRULE_RANDOMID)
  h->ip_id = ip_randomid();

Normalizzazione IPv6

La funzione che si occupa di normalizzare il traffico IPv6 è pf_normalize_ip6 in /usr/src/sys/net/pf_norm.c [2], di seguito il prototipo:
int pf_normalize_ip6(struct mbuf **, int, struct ifnet *, u_short *)
Anche nel caso di pacchetti IPv6 [7], figura 2, è possibile gestire il Time To Live attraverso l'opzione min-ttl di scrub.
Il valore passato come argomento a min-ttl va a modificare il campo "Hop limit" (ip6_hlim).
if (r->min_ttl && h->ip6_hlim < r->min_ttl)
  h->ip6_hlim = r->min_ttl;
Anche in questo caso, come per i pacchetti IP, l'opzione min-ttl è da usare con estrema cautela.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| Traffic Class |           Flow Label                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Payload Length        |  Next Header  |   Hop Limit   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
|                                                               |
+                         Source Address                        +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
|                                                               |
+                      Destination Address                      +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Oltre ai soliti controlli sulle dimensioni delle intestazioni dei pacchetti e delle estensioni, particolare attenzione viene rivolta ai jumbogram.

Senza entrare troppo nei dettagli, non è questa la sede adatta, un jumbogram è un pacchetto IPv6 contenente un payload di dimensione superiore ai 65.535 byte. Tali pacchetti sono segnalati dalla presenza dell'opzione "Jumbo Payload" di tipo "hop-by-hop".
L'header di un pacchetto IPv6 ha un campo "Payload Length" di 16 bit, mentre l'opzione "Jumbo Payload", figura 3, mette a disposizione il campo "Jumbo Payload Length" di 32-bit e permette quindi di gestire i cosidetti jumbogram.

                                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                |  Option Type  |  Opt Data Len |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     Jumbo Payload Length                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
In sintonia con le specifiche del protocollo (RFC2675) [7], il campo "Payload Length" (ip6_plen) dell'intestazione IPv6 deve essere posto a zero in ogni pacchetto che presenta l'opzione "Jumbo Payload", in caso contrario il pacchetto viene scartato.
if (h->ip6_plen != 0)
  goto drop;
Se è presente l'opzione "Jumbo Payload" e la lunghezza (jumbolen) del pacchetto IPv6 (senza l'intestazione), è minore di IPV6_MAXPACKET, che vale 65535, il pacchetto viene scartato.
if (jumbolen <= IPV6_MAXPACKET)
  goto drop;
Il pacchetto viene scartato anche se il campo "Payload Length" del pacchetto IPv6 è posto a zero e non è presente l'opzione Jumbo Payload.
if (ntohs(h->ip6_plen) == 0)
  plen = jumbolen;
else
  plen = ntohs(h->ip6_plen);
if (plen == 0)
  goto drop;
Come detto in precedenza "jumbolen" è la lunghezza del pacchetto IPv6 con "Jumbo Payload", e vale zero se non è presente tale opzione.

In conclusione il pacchetto IPv6 viene rifiutato anche se oltre all'opzione "Jumbo Payload" è presente l'estensione "Fragment".

Normalizzazione TCP

La funzione che si occupa di normalizzare i pacchetti TCP [7], figura 4, è pf_normalize_tcp in /usr/src/sys/net/pf_norm.c [2], di seguito il prototipo:
int pf_normalize_tcp(int, struct ifnet *, struct mbuf *,
                     int, int, void *, struct pf_pdesc *);
Per i nomi delle variabili abbiamo che "th" è una struttura di tipo "tcphdr", e "flags" corrisponde a "th->th_flags".
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data |           |U|A|P|R|S|F|                               |
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |
|       |           |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             data                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Se sono settati i flag SYN e RST il pacchetto è illegale e viene scartato, se invece oltre a SYN è settato il flag FIN, quest'ultimo viene posto a zero e il pacchetto prosegue per la sua strada.
Il pacchetto viene scartato anche se si ha allo stesso tempo SYN=0, ACK=0 e RST=0.
if (flags & TH_SYN) {
  if (flags & TH_RST)
    goto tcp_drop;
  if (flags & TH_FIN)
    flags &= ~TH_FIN;
} else {
  if (!(flags & (TH_ACK|TH_RST)))
    goto tcp_drop;
}
Poiché i flag FIN, PUSH e URG sono validi solo se anche ACK è settato, in tutti gli altri casi il pacchetto viene rifiutato, ad esempio se FIN=1 e ACK=0.
if (!(flags & TH_ACK)) {
  if ((flags & TH_FIN) || (flags & TH_PUSH) || (flags & TH_URG))
    goto tcp_drop;
}
Se il flag URG non è settato viene rimosso l'urgent pointer.
if (!(flags & TH_URG) && th->th_urp) {
  th->th_urp = 0;
}
Il pacchetto viene scartato anche se la lunghezza dell'header TCP non è corretta.
if (th->th_off < (sizeof(struct tcphdr) >> 2))
  goto tcp_drop;
Eseguendo uno scanning con Nessus su un firewall con PF + SCRUB viene fuori da Nessus questo messaggio:

"The remote host does not discard TCP SYN packet which have the FIN flag set. Depending on the kind of firewall you are using, an attacker may use this flaw to bypass its rules."

Brevemente dice che i pacchetti TCP SYN con il flag FIN settato non vengono scartati dall'host remoto e che di conseguenza è possibile usarli per bypassare le regole del firewall.

A questo punto uno potrebbe chiedersi "allora scrub non serve a niente o non funziona", in realtà il problema è tutto di Nessus.
Vediamo cosa succede.

Se la regola di scrubbing è verificata, di fronte ad un pacchetto con i flag SYN e FIN settati, scrub azzera il bit FIN.
Dopodiché il pacchetto, che adesso non ha più il flag FIN settato, passa alle regole di filtraggio.
Se nessuna regola lo blocca, prosegue il suo cammino e molto probabilmente sarà generato in risposta un SYN+ACK.

È questo comportamento che fa credere a Nessus che il pacchetto con SYN+FIN ha avuto successo, ma in realtà non è vero.
Nessus non può sapere che il flag FIN è stato rimosso, non fa altro che inviare un SYN+FIN e ricevere un SYN+ACK in risposta, e questo gli basta per trarre le conclusioni, in questo caso sbagliate.
Maggiori informazioni sul test di Nessus si trovano nel file

/nessus-plugins/scripts/tcpip_ambiguities.nasl

che non fa altro che "Sends a SYN+FIN packet and expects a SYN+ACK".

Ascolto e dimentico, vedo e ricordo, faccio e capisco

Ed ora, dopo tante parole e tanto codice, andiamo a vedere come si comporta il nostro dispositivo sul campo.
Per farlo creeremo dei pacchetti ad hoc e li invieremo al nostro end-point facendoli passare attraverso una macchina che fungerà da firewall. Per creare i pacchetti "ambigui" useremo Nemesis [8].
Sul firewall, realizzato ovviamente con OpenBSD e PF, sono caricate solo le regole di scrubbing, in particolare per i pacchetti in ingresso abbiamo impostato un valore minimo di TTL pari a 20:
# pfctl -s rules
@0 scrub in all min-ttl 20 fragment reassemble
@1 scrub out all fragment reassemble
Di seguito vedremo tre esempi di normalizzazione:

1) Pacchetto con i flag SYN e FIN settati.
Come abbiamo visto in precedenza tale pacchetto non viene scartato, ma scrub interviene ponendo a zero il flag FIN.
Creiamo il pacchetto con nemesis-tcp

# nemesis-tcp -fS -fF -S 192.168.1.1 -D 192.168.2.1
e vediamo cosa arriva all'end-point:
# tcpdump -n -t -vv -i rl0
tcpdump: listening on rl0
192.168.1.1.42069 > 192.168.2.1.23: S [tcp sum ok] 420:420(0)
  win 512 (DF) [tos 0x18] (ttl 254, id 11367)
come si vede dall'output di TcpDump [8], il pacchetto arriva con il solo flag SYN settato.

2) Time To Live inferiore al valore di min-ttl.
Quando al firewall arriva un pacchetto con un TTL inferiore al valore passato come argomento a min-ttl nella regola di scrubbing, nel nostro caso 20, il firewall riscrive il pacchetto con un nuovo valore per TTL.
Creiamo un pacchetto con un Time To Live pari a 12

# nemesis-tcp -fS -S 192.168.1.1 -D 192.168.2.1 -T 12
e vediamo che al destinatario finale arriva un pacchetto con un valore di TTL pari a 20
# tcpdump -n -t -vv -i rl0
tcpdump: listening on rl0
192.168.1.1.42069 > 192.168.2.1.23: S [tcp sum ok] 420:420(0)
  win 512 (DF) [tos 0x18] (ttl 20, id 61302)

3) Pacchetto con il solo flag FIN settato.
Per concludere "iniettiamo" nella rete un pacchetto non valido, avente il solo flag FIN settato.
Creiamo il nostro pacchetto con il solito Nemesis:

# nemesis-tcp -fF -S 192.168.1.1 -D 192.168.2.1
e vediamo cosa accade.
Stavolta TcpDump, posto in ascolto sull'end-point, non ci è d'aiuto poiché il pacchetto ha terminato la sua corsa sul firewall.
È stato scartato perché non corretto, il flag FIN è ritenuto valido solo se anche il flag ACK è settato.

Ovviamente non si esauriscono qui le prove che si possono fare per vedere all'opera il normalizzatore, ma le lascio al lettore come utile esercizio per la comprensione delle reti e dello scrubbing.

PARTE III

Real World: unclean, il sanity check di IPTables

Per concludere facciamo una breve panoramica sulla normalizzazione eseguita da IPTables, evitando di ripetere i concetti già espressi nella trattazione di PF e OpenBSD.

All'interno di IPTables è il modulo Unclean che si occupa della normalizzazione, anche se in questo caso, forse, è più corretto parlare di sanity check.
Per poter usufruire delle funzionalità di unclean occorre caricare il modulo usando l'opzione "-m unclean" all'interno della regola che comprende il traffico che si vuole normalizzare, ad esempio:

iptables -A INPUT -i eth0 -m unclean -j DROP
non sono previste opzioni per tale modulo.

L'unico scopo del modulo unclean è quello di individuare i pacchetti sospetti, e a tale proposito sono implementati una serie di check che operano sui diversi protocolli.
In dettaglio le funzioni che si occupano dei suddetti controlli sono check_icmp, check_udp, check_tcp e check_ip definite nel file /net/ipv4/netfilter/ipt_unclean.c all'interno del kernel di Linux [6].
Le operazioni eseguite da queste funzioni sono quelle elencate nella prima parte di questo articolo, alcune le abbiamo viste in dettaglio nella descrizione di scrub, ed in sostanza restano ancora valide per IPTables.

Il coreteam di Netfilter ha deciso di rimuovere il modulo ipt_unclean dal kernel 2.6 di Linux.
Ecco come spiega questa decisione Harald Welte, sulla mailing list netfilter-devel@, in risposta ad una domanda riguardante unclean:

From: laforge<at>gnumonks<dot>org (Harald Welte)
Date: Mon, 15 Sep 2003 11:52:04 +0200
Subject: missing ipt_unclean support in 2.6test5
In-Reply-To: <20030914200347.GA811@baryon>
References: <20030914200347.GA811@baryon>
Message-ID: <20030915095204.GC777@obroa-skai.de.gnumonks.org>

On Sun, Sep 14, 2003 at 10:03:47PM +0200, garde<at>benben<dot>com wrote:
> I just saw ipt_unclean matching support was removed from the 2.6 tree, 
> is there a place I can find some background on the reason behind this?
> Couldn't find anything on the lkml or google that provided a better 
> reason than "we feel it's a rather insecure part of the netfilter code".

What more background do you want to have?  The netfilter coreteam has
made this decision at the recent netfilter developer conference, because
we think it is a dangerous match.

Filtering like the unclean match can easily introduce
forwards-compatibility problems.  Just look what happened when the ECN
bits in the ip and tcp header had been defined for congestion
notification by the IETF: suddenly everybody sending packets adhering to
this new standard was unable to reach any machine/network protected by a
netfilter/iptables firewall that used the unclean match.

We just want to make sure that this doesn't ever happen again.  We were
unable to remove ipt_unclean in 2.4.x because of compatibility reasons.
But now in 2.6 it is easy to have it removed.

Harald Welte

Conclusioni

Abbiamo visto che la direttiva scrub di PF e il modulo unclean di IPTables (che ad essere precisi, non è un normalizzatore nel vero senso del termine), non sono confrontabili per funzionalità e stabilità (il modulo unclean è ancora indicato come "experimental").
Abbiamo anche visto che basta un dispositivo, relativamente semplice, per evitare numerosi grattacapi ai NIDS e ai firewall, ma anche alle normali applicazioni; è anche chiaro che messo da solo a guardia di una rete, un normalizer, serve davvero a ben poco.

Informazioni sull'autore

Gianluigi Spagnuolo, studente. Si interessa di programmazione, sicurezza informatica, buddismo zen, reti e sistemi operativi liberi.
È tra i fondatori dell' Italian Ruby User Group e dell'Irpinia Linux User Group e moderatore della mailing list Security4Dummies.
Home page: http://kirash.interfree.it

È possibile consultare l'elenco degli articoli scritti da Gianluigi Spagnuolo.

Altri articoli sul tema OpenBSD / Networking.

Risorse

  1. "Insertion, Evasion and Denial of Service: Eluding Network Intrusion Detection", T.H.Ptacek, T.N.Newsham
    http://www.securityfocus.com/data/library/ids.ps
  2. La parte dei sorgenti del kernel di OpenBSD relativa al Packet Filter
    http://www.openbsd.org/cgi-bin/cvsweb/src/sys/net/
  3. Le faq ufficiali di OpenBSD
    http://www.openbsd.org/faq/pf/
  4. "Network Intrusion Detection: Evasion, Traffic Normalization, and End-to-End Protocol Semantics", M.Handley, V.Paxson, C.Kreibich
    http://www.icir.org/vern/papers/norm-usenix-sec-01-html/index.html
  5. "Transport and Application Protocol Scrubbing" G.R.Malan, D.Watson, F.Jahanian
    http://www.ieee-infocom.org/2000/papers/340.ps
  6. I sorgenti del kernel di Linux
    http://www.kernel.org/pub/linux/
  7. Request for Comments
    RFC0791: Internet Protocol Specification
    RFC0793: Transmission Control Protocol Specification
    RFC2460: Internet Protocol, Version 6 (IPv6) Specification
    RFC2675: IPv6 Jumbograms
    http://www.ietf.org/rfc/
  8. Nemesis Project
    http://www.packetfactory.net/projects/nemesis/
  9. TcpDump
    http://www.tcpdump.org/
Discuti sul forum   Stampa

Cosa ne pensi di questo articolo?

Discussioni

Questo articolo o l'argomento ti ha interessato? Parliamone.