Traffico TCP sulla porta 0

Qualche giorno fa Nagios mi ha segnalato un allarme di sicurezza che mi ha lasciato alquato basito:

When a syslog message is generated by the device a SEC info IPACCESSLOGP list 102 denied tcp 77.223.136.170(0) - 95.245.162.62(0), 1 packet 9:5:46:40.62

In soldoni, l’ACL attiva sull’interfaccia dialer (WAN) del mio router ha “scartato” un segmento TCP proveniente dall’IP 77.223.136.170 (porta 0) e diretto al mio (vecchio) IP pubblico (ovvero 95.245.162.62, porta 0).

Ora, è sufficiente avere un minimo di dimestichezza con lo stack TCP/IP per capire che l’anomalia sta proprio nella porta sorgente ed in quella di destinazione. Infatti, la porta 0 è riservata ed il suo impiego può avvenire per i motivi (leciti) più disparati.

Ad esempio, nel caso in cui il pacchetto IP (formato da header + payload) superi la MTU del link che deve attraversare, esso subirà un processo specifico, denominato “frammentazione”.

In particolare, essendo l’header TCP parte del payload IP (per via dell’incapsulamento – vedi immagine sottostante), ciascun frammento potrebbe contenere la suddetta intestazione per intero o solo parzialmente. Nel primo caso, l’header TCP originario si troverebbe soltanto nel primo frammento, mentre tutti gli altri conterrebbero la sola intestazione IP (dotata di ID specifico del frammento, offset per la ricostruzione del traffico in fase di ricezione – dimensione e bit MFMore Fragment – pari a 0 o 1 a seconda che si tratti dell’ultimo frammento o meno).

encaps

Più in generale, la porta TCP/UDP pari a 0 potrebbe stare a significare, molto semplicemente, assenza di layer 4 (basti pensare al protocollo ICMP).

Per quanto riguarda, invece, l’uso malevolo della porta 0, possiamo distinguere 3 macro-tipologie di casi:

1) Attacchi di tipo fingerprint. Essi si basano sul presupposto che spesso e volentieri le ACL non possono essere definite in modo tale da droppare esplicitamente il traffico in ingresso sulla porta 0 (TCP o UDP), ergo puntare ad essa è un modo abbastanza efficace (ma non infallibile) per ottenere info sulla tipologia di router oggetto di interesse (OS fingerprint – leggete questo per ulteriori dettagli);

2) Attacchi volti ad “aggirare” le ACL. Ad esempio, forzando la frammentazione del pacchetto IP ed agendo sul campo offset,  si potrebbe riuscire, in teoria, a sovrascrivere parte dell’intestazione TCP originaria (che punta ad una porta di destinazione “lecitamente” raggiungibile dall’esterno, come potrebbe essere la 25 per il protocollo SMTP) con una porta non accessibile dall’esterno (ad esempio la 23 per il protocollo Telnet). Per ulteriori dettagli su tale tipologia di attacco (denominato tiny overlapping fragment attack) leggete questo.

3) Attacchi di tipo DoS/DDoS. Forzando la frammentazione dei pacchetti IP sottostanti al layer 4 (TCP/UDP) si potrebbe riuscire a “sovraccaricare” il router di destinazione, soprattutto per via dell’overhead dovuto all’attività di “ricostruzione” del traffico. Inoltre, puntando ad una porta non lecita, ad esempio la 0, lo si costringerebbe ad inviare dei messaggi ICMP Port Unreachable (se abilitati sull’interfaccia), sovraccaricandolo ulteriormente.

Della casistica appena illustrata credo che l’allarme ricada, molto banalmente, negli attacchi di tipo fingerprint. Ovviamente ho alzato il livello di attenzione per evitare “brutte” sorprese in futuro.

Vi terrò comunque aggiornati.

Aggiornamento 1

A quanto pare si è trattato proprio di un portscan/fingerprint. Infatti, dopo circa 5 giorni dallo scan sulla porta TCP 0, ho registrato il seguente traffico:

Jan 25 15:29:15 192.168.2.1 1738: 001735: Jan 25 15:29:14.483 UTC: %SEC-6-IPACCESSLOGP: list 101 permitted tcp 77.223.136.170(443) -> 79.31.118.x(43616), 1 packet
Jan 25 15:29:17 192.168.2.1 1739: 001736: Jan 25 15:29:15.988 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 77.223.136.170(256) -> 79.31.118.x(43617), 1 packet
Jan 25 15:29:18 192.168.2.1 1740: 001737: Jan 25 15:29:17.200 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 77.223.136.170(111) -> 79.31.118.x(43617), 1 packet
Jan 25 15:29:19 192.168.2.1 1741: 001738: Jan 25 15:29:18.204 UTC: %SEC-6-IPACCESSLOGP: list 101 permitted tcp 77.223.136.170(1106) -> 79.31.118.x(43616), 1 packet
Jan 25 15:29:20 192.168.2.1 1742: 001739: Jan 25 15:29:19.204 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 77.223.136.170(700) -> 79.31.118.x(43617), 1 packet
Jan 25 15:29:20 192.168.2.1 1743: 001740: Jan 25 15:29:20.208 UTC: %SEC-6-IPACCESSLOGP: list 101 permitted tcp 77.223.136.170(3493) -> 79.31.118.x(43616), 1 packet
Jan 25 15:29:22 192.168.2.1 1744: 001741: Jan 25 15:29:21.213 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 77.223.136.170(726) -> 79.31.118.x(43617), 1 packet
Jan 25 15:29:24 192.168.2.1 1746: 001743: Jan 25 15:29:23.365 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 77.223.136.170(777) -> 79.31.118.x(43618), 1 packet
Jan 25 15:29:26 192.168.2.1 1747: 001744: Jan 25 15:29:25.046 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 77.223.136.170(111) -> 79.31.118.x(43618), 1 packet

Aggiornamento 2

In data 26 Gennaio ho ricevuto un altro pacchetto da/verso la suddetta porta:

When a syslog message is generated by the device a SEC info IPACCESSLOGP list 101 denied tcp 23.236.147.202(0) - 79.31.118.x(0), 1 packet 0:14:52:02.45

Vediamo se tra qualche giorno il nuovo IP sorgente utilizzerà lo stesso ordine di port probing del suo predecessore.

Aggiornamento 3

Come volevasi dimostrare, a distanza di qualche ora è arrivato il port probing vero e proprio:

Jan 27 18:36:33 192.168.2.1 19380: 019433: Jan 27 18:36:32.479 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 23.236.147.202(139) -> 79.31.118.x(46096), 1 packet
Jan 27 18:36:35 192.168.2.1 19381: 019434: Jan 27 18:36:34.475 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 23.236.147.202(1002) -> 79.31.118.x(46097), 1 packet
Jan 27 18:36:36 192.168.2.1 19382: 019435: Jan 27 18:36:35.500 UTC: %SEC-6-IPACCESSLOGP: list 101 permitted tcp 23.236.147.202(2701) -> 79.31.118.x(46095), 1 packet
Jan 27 18:36:36 192.168.2.1 19383: 019436: Jan 27 18:36:36.500 UTC: %SEC-6-IPACCESSLOGP: list 101 permitted tcp 23.236.147.202(11967) -> 79.31.118.x(46095), 1 packet
Jan 27 18:36:38 192.168.2.1 19384: 019437: Jan 27 18:36:37.552 UTC: %SEC-6-IPACCESSLOGP: list 101 permitted tcp 23.236.147.202(1494) -> 79.31.118.x(46095), 1 packet
Jan 27 18:36:39 192.168.2.1 19385: 019438: Jan 27 18:36:38.556 UTC: %SEC-6-IPACCESSLOGP: list 101 permitted tcp 23.236.147.202(8021) -> 79.31.118.x(46095), 1 packet
Jan 27 18:36:43 192.168.2.1 19387: 019440: Jan 27 18:36:42.353 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 23.236.147.202(139) -> 79.31.118.x(46097), 1 packet
Jan 27 18:36:48 192.168.2.1 19388: 019441: Jan 27 18:36:47.154 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 23.236.147.202(139) -> 79.31.118.x(46095), 1 packet

Anche in questo caso le porte oggetto dell’attacco sono quelle > 1023. Ho quindi fatto qualche modifica all’ACL configurata sulla dia0 del mio router, sostituendo la regola:

access-list 101 permit tcp any gt 1023 any log

con:

access-list 101 permit tcp any gt 1023 any established log

in modo tale da consentire il traffico proveniente dalle porte > 1023 solo se si riferiscono a delle connessioni già esistenti.

Aggiornamento 4

Sono riuscito a ricreare parzialmente la suddetta tipologia di traffico, utilizzando un apposito tool denominato hping, il quale è in grado di forgiare dei pacchetti RAW/TCP/UDP/ICMP da impiegare durante le operazioni di OS fingerprint e non solo.

Ad esempio, mediante il comando:

[root@linuxbox ~]# hping3 -S -V <mio indirizzo IP>

sono riuscito a generare una sfilza di pacchetti TCP recanti i classici 40 byte di header, payload 0 e flag SYN settata a 1, la cui destinazione proprio è la famigerata porta 0.

Di seguito riporto il tracciato tcpdump da locale (grazie al quale si può notare che la porta sorgente è una porta alta, ovvero maggiore di 1023, e che la porta di destinazione è sempre la 0):

09:41:14.967184 IP 192.168.1.1.1232 > 79.31.118.x.0: Flags [S], seq 2094000106, win 512, length 0
09:41:15.967305 IP 192.168.1.1.1233 > 79.31.118.x.0: Flags [S], seq 517260399, win 512, length 0
09:41:16.967425 IP 192.168.1.1.1234 > 79.31.118.x.0: Flags [S], seq 399923675, win 512, length 0
09:41:17.967477 IP 192.168.1.1.1235 > 79.31.118.x.0: Flags [S], seq 725026004, win 512, length 0
09:41:18.967573 IP 192.168.1.1.rmtcfg > 79.31.118.x.0: Flags [S], seq 1830159741, win 512, length 0
09:41:19.967638 IP 192.168.1.1.1237 > 79.31.118.x.0: Flags [S], seq 342672970, win 512, length 0

Mentre il log generato dal mio router è il seguente:

Apr  3 09:39:35 192.168.2.1 108805: 108943: Apr  3 08:39:34.979 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 95.254.89.x(1232) -> 79.31.118.x(0), 1 packet
Apr  3 09:39:37 192.168.2.1 108807: 108945: Apr  3 08:39:36.979 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 95.254.89.x(1233) -> 79.31.118.x(0), 1 packet
Apr  3 09:39:39 192.168.2.1 108808: 108946: Apr  3 08:39:38.980 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 95.254.89.x(1234) -> 79.31.118.x(0), 1 packet
Apr  3 09:39:41 192.168.2.1 108809: 108947: Apr  3 08:39:40.980 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 95.254.89.x(1235) -> 79.31.118.x(0), 1 packet
Apr  3 09:39:43 192.168.2.1 108810: 108948: Apr  3 08:39:42.981 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 95.254.89.x(1236) -> 79.31.118.x(0), 1 packet
Apr  3 09:39:45 192.168.2.1 108811: 108949: Apr  3 08:39:44.977 UTC: %SEC-6-IPACCESSLOGP: list 101 denied tcp 95.254.89.x(1237) -> 79.31.118.x(0), 1 packet

Invece, forzando a 0 la porta sorgente del suddetto traffico (e mantenendola tale per tutta la durata del test – opzione -k) attraverso il comando:

[root@linuxbox ~]# hping3 -S -VV -s 0 -k <mio indirizzo IP>

sono riuscito ad intercettarlo localmente come dimostrato dal seguente tracciato:

10:32:21.495218 IP 192.168.1.1.0 > 79.31.118.x.0: Flags [S], seq 1600061111, win 512, length 0
10:32:22.495334 IP 192.168.1.1.0 > 79.31.118.x.0: Flags [S], seq 965785080, win 512, length 0
10:32:23.495411 IP 192.168.1.1.0 > 79.31.118.x.0: Flags [S], seq 677172521, win 512, length 0
10:32:24.495492 IP 192.168.1.1.0 > 79.31.118.x.0: Flags [S], seq 473418678, win 512, length 0

ma è stato successivamente scartato dal router sorgente, ergo non è mai giunto a destinazione.

Nei prossimi giorni proverò a ripetere il test utilizzando il router di un vendor differente.

CentOS 6 e Nagios: script bash per monitorare la scadenza dei certificati X.509

A quanti di voi sarà capitato di dover fare i conti con un certificato X.509 scaduto (magari installato su un server in produzione) che avete scordato (per un motivo o per un altro) di rinnovare?

Ebbene, tramite il seguente scrip bash (da integrare a Nagios) non dovremo più “appuntarci” la data di scadenza dei suddetti certificati, poichè sarà proprio l’NMS a ricordarci (a tempo debito) di procedere con il rinnovo.

nagiosEcco lo scrip:

#!/bin/bash

############Scrip powered by nightfly. Default values for warn and crit############
############(if not defined by command line) are 10 and 3 respectivley##############

host=$1
port=$2
warn=$3
crit=$4

function get_cert_datetime() {

        date=`echo | openssl s_client -connect $host:$port 2>/dev/null | openssl x509 -noout -dates | grep notAfter | awk -F "=" '{print $2 }'`

        day=`echo $date | awk '{print $2}'`

        case $day in

        1)

                day=01;
                ;;

        2)

                day=02;
                ;;

        3)

                day=03;
                ;;

        4)

                day=04;
                ;;

        5)

                day=05;
                ;;

        6)

                day=06;
                ;;

        7)

                day=07;
                ;;

        8)

                day=08;
                ;;

        9)

                day=09;
                ;;

        esac

        month=`echo $date | awk '{print $1}'`

        case $month in

        Jan)

                 month=01;
                ;;

        Feb)

                month=02;
                ;;

        Mar)

                month=03;
                ;;

        Apr)

                month=04;
                ;;

        May)

                month=05;
                ;;

        Jun)

                month=06;
                ;;

        Jul)

                month=07;
                ;;

        Aug)

                month=08;
                ;;

        Sep)

                month=09;
                ;;

        Oct)

                month=10;
                ;;

        Nov)

                month=11;
                ;;

        Dec)

                month=12;
                ;;
        esac

        year=`echo $date | awk '{print $4}'`

        time=`echo $date | awk '{print $3}'`

        hours=`echo $time | awk -F ":" '{print $1}'`

        minutes=`echo $time | awk -F ":" '{print $2}'`

        seconds=`echo $time | awk -F ":" '{print $3}'`

        datecheck="$year$month$day"

        datecheckepoch=`date -d "$datecheck" +"%s"`

        datenow=`date +"%Y%m%d"`

        datenowepoch=`date -d "$datenow" +"%s"`

}

get_cert_datetime;

get_time_diff() {

        #time difference in hours, minutes and seconds

        hoursnow=`date +"%H"`

        minutesnow=`date +"%M"`

        secondsnow=`date +"%S"`

        hourstoseconds=`expr $hoursnow "*" 3600`

        minutestosecond=`expr $minutesnow "*" 60`

        secondsnowtot=`expr $hourstoseconds + $minutestosecond + $secondsnow`

        hourschecktoseconds=`expr $hours "*" 3600`

        minuteschecktoseconds=`expr $minutes "*" 60`

        secondschecktot=`expr $hourschecktoseconds + $minuteschecktoseconds + $seconds`

        secondsdiff=`expr $secondschecktot - $secondsnowtot`

        secondstoexpire=`expr 86400 - $secondsnowtot + $secondschecktot`

        minutestoexpire=`expr $secondstoexpire / 60`

        spareseconds=`expr $secondstoexpire % 60`

        hourstoexpire=`expr $minutestoexpire / 60`

        spareminutes=`expr $minutestoexpire % 60`
}

get_time_diff;

timestamp_to_days() {

        #unix timestamp to days conversion

        secondsleft=`expr $datecheckepoch - $datenowepoch`

        minutesleft=`expr $secondsleft / 60`

        hoursleft=`expr $minutesleft / 60`

        daysleft=`expr $hoursleft / 24`

}

timestamp_to_days;

if [[ ! -z $host && ! -z $port ]];then

        if [[ $datecheckepoch -gt $datenowepoch ]];then

                if [[ -z $warn && -z $crit ]];then

                        if [[ $daysleft -ge 10 ]];then

                                echo "OK: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                exit 0

                        elif [[ $daysleft -lt 10 && $daysleft -gt 3 ]];then

                                echo "WARNING: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                exit 1

                        elif [[ $daysleft -lt 3 && $daysleft -gt 0 ]];then

                                echo "CRITICAL: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                exit 2

                        elif [[ $daysleft -eq 0 && $secondsdiff -gt 0 ]];then

                                echo "CRITICAL: the X.509 certificate will expire in $hourstoexpire hours $spareminutes minutes $spareseconds seconds"

                                exit 2

                        fi

                elif [[ ! -z $warn && ! -z $crit ]];then

                        if [[ $warn -gt $crit ]];then

                                if [[ $daysleft -ge $warn ]];then

                                        echo "OK: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                        exit 0

                                elif [[ $daysleft -lt $warn && $daysleft -gt $crit ]];then

                                        echo "WARNING: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                        exit 1

                                elif [[ $daysleft -lt $crit && $daysleft -gt 0 ]];then

                                        echo "CRITICAL: the X.509 certificate will expire in $daysleft days $hourstoexpire hours $spareminutes minutes $spareseconds seconds | days_left=$daysleft"

                                        exit 2

                                elif [[ $daysleft -eq 0 && $secondsdiff -gt 0 ]];then

                                        echo "CRITICAL: the X.509 certificate will expire in $hourstoexpire hours $spareminute minutes $spareseconds seconds | days_left=$daysleft"

                                        exit 2

                                fi

                        fi

                        else

                                echo "Usage: check_ssl_cert_exp <host> <port> [<warn left days> <crit left days>]"
                                echo "Warn threshold has to be greater then the Crit one"
                                exit 3
                        fi

                else

                        echo "Usage: check_ssl_cert_exp <host> <port> [<warn left days> <crit left days>]"
                        echo "Both thresholds have to be used"

                        exit 3
                fi
        else

                echo "CRITICAL: the X.509 certificate is expired"

                exit 2

        fi

else

        echo "Usage: check_ssl_cert_exp <host> <port> [<warn left days> <crit left days>]"
        echo "Please set the hostname and the port at least"

        exit 3
fi

Ho deciso di adoperare le funzioni in modo da suddividere logicamente le varie operazioni che vengono effettuate per il calcolo del tempo che intercorre alla data di scadenza del certificato.

I parametri da utilizzare sono 4 in tutto, di cui 2 mandatori (hostname/FQDN e porta) e 2 facoltativi (soglia di WARNING e soglia di CRITICAL).

Per prima cosa (tramite la funzione get_cert_datetime()) individuo la data di scadenza riportata sul certificato  (campo notAfter), utilizzando l’applicativo s_client della suite openssl. Successivamente effettuo un parsing dell’output, ricavando giorno, mese, anno, ora, minuto e secondo di scadenza, convertendo quindi tali informazioni in Unix epoch.

Successivamente, mediante la funzione get_time_diff(), calcolo la differenza (in termini di ore, minuti e secondi), tra l’ora attuale e quella di scadenza del certificato. Inoltre, utilizzando la variabile secondsdiff, nel caso in cui mi dovessi trovare proprio nel giorno di scadenza, riesco a capire se il certificato sta per scadere oppure è già scaduto.

A questo punto procedo con tutta una serie di confronti tra variabili e nella fattispecie verifico che la data attuale sia anteriore a quella di scadenza:

if [[ $datecheckepoch -gt $datenowepoch ]];then

Se è effettivamente questo il caso, controllo, rispettivamente, che le soglie di WARNING e di CRITICAL siano state definite e che siano logicamente coerenti (con WARNING > CRITICAL):

 elif [[ ! -z $warn && ! -z $crit ]];then                        
      if [[ $warn -gt $crit ]];then

Dopodichè faccio un confronto tra il giorno attuale e quello di scadenza, tenendo in considerazione le suddette soglie oppure i valori di default (10 e 3 nel caso in cui esse non siano state definite esplicitamente). Se mi trovo proprio nel giorno di scadenza verifico che la variabile secondsdiff sia un intero strettamente positivo, restituendo un errore di tipo CRITICAL ed il tempo mancante alla scadenza.

Ovviamente, anche nel caso in cui la data attuale sia successiva a quella di scadenza verrà restituito un errore di tipo CRITICAL segnalando che il certificato è già scaduto.

Configurazione di Nagios

Per prima cosa occorre definire il comando che fa uso del suddetto scrip (check_ssl_cert_exp), ovvero:

# 'check_ssl_exp' command definition
define command{
        command_name    check_ssl_exp
        command_line    $USER1$/check_ssl_cert_exp $HOSTADDRESS$ $ARG1$ $ARG2$ $ARG3$
        }

Successivamente possiamo creare il servizio che si occuperà di controllare la scadenza del certificato X.509:

define service{
        use                             local-service         ; Name of service template to use
        host_name                       localhost
        service_descripion              SSL Certificate Expiration Date
        check_command                   check_ssl_exp!443!10!3
        notifications_enabled           0
        }

Infine riavviamo Nagios per rendere effettive le suddette modifiche:

[root@NMS ~]# service nagios reload

E’ tutto. Alla prossima.

WI-FI IP camera (made in China): mettere al sicuro l’accesso da remoto

Problema

Negli ultimi anni si è assistito ad un vero e proprio boom riguardante l’acquisto di dispositivi di videosorveglianza basati sul protocollo IP,  ergo facilmente integrabili con le piccole reti domestiche preesistenti. Fin qui nulla di male, se non fosse che, spesso e volentieri, tali “aggeggi” montino dei firmware poco affidabili dal punto di vista della cybersecurity. Mi spiego meglio: cosa accadrebbe se consentissi l’accesso da remoto alla pagina Web di amministrazione della mia telecamera IP? Chi mi garantisce che:

1) Il codice server-side della suddetta pagina non sia vulnerabile;

2) Che non siano presenti una o più backdoor all’interno del codice sorgente del firmware.

dbpowerSoluzione

Se disponete di un server Linux a risparmio energetico (come nel mio caso) è possibile mettere al sicuro l’accesso da remoto alla nostra telecamera IP mediante l’uso di un reverse proxy. In particolare, la configurazione che sto per illustrarvi riguarda uno dei server Web più utilizzati in ambito open source, ovvero Apache.

Supponendo che la mia telecamera IP sia in ascolto sulla porta TCP 81 (HTTP-Alt) e che risponda all’indirizzo 192.168.1.5 , ecco la configurazione da me adoperata:

<VirtualHost *:81>
    ProxyPass / http://192.168.1.5:81/
    ProxyPassReverse / http://192.168.1.5:81/

    <Proxy *>
        Order deny,allow
        Allow from all
        AuthName "IPCamera Access"
        AuthType Basic
        AuthUserFile /etc/ipcam/passwd
        Require valid-user
    </Proxy>

</VirtualHost>

Nella fattispecie, oltre a loggare tutte le richieste di accesso provenienti dall’esterno (in modo da tenerne traccia), ho aggiunto un ulteriore meccanismo di autentica a quello già previsto dalla pagina Web di gestione. In questo modo sono riuscito a garantirmi 2 risultati:

1) Protezione contro attacchi di tipo Web-based;

2) Protezione contro eventuali backdoor presenti nel firmware.

Inoltre, dopo aver definito il suddetto VirtualHost, è stato necessario abilitare il modulo che fa funzionare Apache come reverse proxy, ovvero mod_proxy, decommentando o inserendo la seguente direttiva:

LoadModule proxy_module modules/mod_proxy.so

Per ciò che concerne le credenziali di accesso, invece, è stato possibile definirle utilizzando il comando htpasswd nel seguente formato:

[root@linuxbox ~]# htpasswd -c /etc/ipcam/passwd nomeutente

Infine ho ricaricato la configurazione di Apache per rendere effettive le suddette modifiche:

[root@linuxbox ~]# service httpd reload

Ora la vostra telecamera IP può essere considerata “relativamente” al sicuro.

Alla prossima.

CentOS 6 e Postfix: tenere a bada il fenomeno denominato backscatter

In questo e questo post ho mostrato come configurare Postfix in modo da farlo funzionare come antispam. In quest’altro post, invece, ho illustrato la configurazione di postgrey e qui ho parlato di opendkim e di come integrarlo a Postfix.

Adesso vedremo come mitigare un fenomeno tipico dello spam e sempre più in aumento, ovvero il backscatter.

postfixUn po’ di teoria

Supponiamo che uno spammer voglia utilizzare il vostro dominio come mittente di una mail fraudolenta appositamente forgiata, da inoltrare ad un server SMTP autoritativo per un dominio X, che chiameremo dominiotarget.com.

La sequenza dei comandi che lo spammer utilizzerà sarà simile alla seguente:

ehlo mail.dominiotarget.com
mail from:<pluto@vostrodominio.com>
rcpt to:<pippo@dominiotarget.com>
data
some data
.
quit

Come potete notare, lo spammer ha utilizzato il vostro dominio (mail from:) come mittente della mail di spam che intende inviare ad utente@dominiotarget.com.

Supponiamo adesso che, per qualche ragione, il server SMTP di dominiotarget.com respinga la suddetta email fraudolenta (poichè, ad esempio, non esiste la mailbox pippo@dominiotarget.com), generando, successivamente, un messaggio apposito per indicare che tale email è stata respinta (mail bounced). Tale messaggio di “errore” verrà successivamente inoltrato al server SMTP autoritativo (ovvero quello specificato nei record MX della zona DNS) per vostrodominio.com.

A questo punto, se non opportunamente configurato, il vostro server SMTP riceverà il suddetto messaggio e lo inoltrerà alla casella di posta dell’utente spoofato dallo spammer, ovvero pluto@vostrodominio.com (se esiste), oppure verrà semplicemente scartata. Nel primo caso si rischia di saturare abbastanza velocemente la casella email del malcapitato utente (soprattutto se dotata di DISK QUOTA); nel secondo caso, invece, si rischia di creare eccessiva entropia, non consentendo all’amministratore della macchina di discernere (ad esempio attraverso l’analisi del file maillog) tra le email di backscatter e quelle che sono state respinte per un motivo lecito.

Come fare dunque per limitare tale fenomeno? Ecco alcune possibili soluzioni.

Utilizzare SPF

Se il server SMTP di dominiotarget.com è stato configurato in modo da utilizzare il framework SPF, e nella vostra zona DNS è presente un record di tipo TXT che specifica l’elenco dei server che possono inviare email utilizzando vostrodominio.com, esso sarà in grado di bloccare il tentativo di inoltro senza inviare alcun messaggio di errore al vostro server SMTP. Purtroppo però l’SPF non è configurato su tutti i server SMTP sparsi per la Rete, ergo bisogna correre ai ripari configurando opportunamente Postfix sul server antispam che intendiamo utilizzare.

Configurazione di Postfix

Per prima cosa occorre fare in modo che le email dirette a caselle di posta non esistenti vengano automaticamente respinte. Per fare ciò è possibile configurare la seguente direttiva all’interno del file /etc/postfix/main.cf:

unknown_local_recipient_reject_code = 550

Tale settaggio rappresenta comunque un’ottima prima difesa contro il backscatter, poichè, nella stragrande maggioranza dei casi, lo spammer forgerà delle email pseudorandomiche da utilizzare come mittente, del tipo AjsheUbjdX@vostrodominio.com. Esistono, però, dei casi particolari in cui lo spammer è a conoscenza di alcuni indirizzi leciti (ed attivi) su vostrodominio.com, ragion per cui occorre affinare ulteriormente la nostra opera di filtraggio. In particolare, è possibile impostare delle regole opportune basate su espressioni regolari in grado di “capire” quando un messaggio di mail bounced è stato causato dal backscatter e quando no. Tali regole potranno essere applicate durante l’analisi dell’header e del corpo del messaggio.

Ad esempio, possiamo popolare il file /etc/postfix/header_checks con le seguenti direttive:

if /^Received:/
/^Received: +from +(vostrodominio\.com) +/
        reject forged client name in Received: header: $1
/^Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)(vostrodominio\.com)\)/
        reject forged client name in Received: header: $2
/^Received:.* +by +(vostrodominio\.com)\b/
        reject forged mail server name in Received: header: $1
endif
/^Message-ID:.* <!&!/ DUNNO
/^Message-ID:.*@(vostrodominio\.com)/
        reject forged domain name in Message-ID: header: $1

Inoltre, le suddette entry dovranno essere collocate anche all’interno del file /etc/postfix/body_checks.

Infine, è necessario editare il file /etc/postfix/master.cf come segue:

body_checks = pcre:/etc/postfix/body_checks

header_checks = pcre:/etc/postfix/header_checks

Da notare che non occorre postmappare i suddetti file ed è sufficiente un semplice reload di Postfix per rendere effettive le modifiche apportate:

[root@antispam ~]# service postfix reload

Adesso il nostro server antispam sarà sicuramente in grado di filtrare un gran numero di messaggi di backscatter ma non sarà comunque a prova di bomba (ecco perchè ho parlato di “mitigare” e non di “bloccare” tale fenomeno). Ciò è dovuto al fatto i messaggi di mail bounced possono variare sia nella sostanza che nella forma in base all’applicativo che funge da SMTP, rendendo comunque possibile l’eventualità che esso non venga matchato dai filtri appena configurati.

A presto.

SElinux e CentOS 6: ricevere un’email di notifica in caso di policy violate

In questo post ho discusso di SElinux, illustrandone la logica di funzionamento ed i comandi da utilizzare per la sua configurazione e gestione.

Adesso vedremo come rendere tale sistema di sicurezza il più proattivo possibile, ovvero facendo in modo che sia in grado di inviarci una mail di notifica nel caso in cui una o più policy MAC vengano violate.

selinux-penguin-new_medium

Installazione e configurazione di SEtroubleshoot

Il software in grado di implementare la funzionalità in questione prende il nome di setroubleshoot. Per installarlo possiamo utilizzare il packet manager di casa CentOS/RedHat, ovvero yum:

[root@linuxbox ~]# yum install setroubleshoot

Successivamente, passiamo alla configurazione dell’applicativo in questione, editando il file /etc/setroubleshoot/setroubleshoot.conf. Le entry da modificare sono le seguenti:

recipients_filepath = /var/lib/setroubleshoot/email_alert_recipients
smtp_port = 25
smtp_host = localhost
from_address = mittente@vostrodominio.com
subject = SELinux AVC Alert for nomeserver

Va da sè che l’opzione smtp_host deve essere popolata in base al server SMTP che si vuole utilizzare. Ad esempio, nel mio caso, esiste un’unica macchina che funge da STMP “in uscita”, configurata in modo tale da interfacciarsi con un relay host “esterno” ed “affidabile” (in tal modo evito che le mie email vengano respinte costantemente poichè provenienti da un indirizzo IP pubblico di tipo dinamico e non dotato di record DNS PTR).

Inseriamo adesso, all’interno del file /var/lib/setroubleshoot/email_alert_recipients, il destinatario delle email di notifica:

[root@linuxbox ~]# echo "indirizzo.email@vostrodominio.com" >> /var/lib/setroubleshoot/email_alert_recipients

e riavviamo il servizio che si occupa di gestire tale funzionalità (in modo da attivare le suddette modifiche):

[root@linuxbox ~]# service messagebus restart

Test

Per testare il corretto funzionamento del sistema di notifica appena configurato è necessario fare in modo che uno dei moduli SELinux custom che abbiamo realizzato venga momentaneamente disattivato (in tal modo “forzeremo” il popolamento del file audit.log generando così il trigger che causerà l’inoltro della notifica email sulla nostra casella di posta):

[root@linuxbox ~]# semodule -d <nomemodulo>

Se tutto funziona come dovrebbe, riceveremo una notifica dal contenuto simile al seguente:

[SELinux AVC Alert for linuxbox] SELinux is preventing /usr/sbin/sedispatch from sendto access on the unix_dgram_socket 

SELinux is preventing /usr/sbin/sedispatch from sendto access on the unix_dgram_socket .

*****  Plugin catchall (100. confidence) suggests   **************************

... OMISSIS ...

Alla prossima.

NRPE_NT e Nagios: tenere sotto controllo gli aggiornamenti di Windows

Installare prontamente gli aggiornamenti di Windows, soprattutto se si ha a che fare con i security update, è sempre cosa buona e giusta. Spesso, però, capita che l’amministratore di sistema debba gestire N macchine e non abbia il tempo materiale di loggarsi su ciascuna di esse e constatare l’eventuale disposnibilità di aggiornamenti. Personalmente credo che non li si debba mai scaricare nè tantomeno installare automaticamente sulla macchina ospite, soprattutto se si ha a che fare con dei sistemi in produzione.

Cosa fare dunque per automatizzare tale task di verifica della disponibilità degli aggiornamenti? Semplice, utilizzare il nostro NMS preferito, ovvero Nagios.

nagiosIngredienti

Per prima cosa è necessario che sulla macchina da monitorare sia installato (ed attivo) il servizio NRPE_NT. Inoltre, ai plugin utilizzati dal servizio in questione, occorre aggiungere uno scrip PowerShell creato appositamente. Infine,  occorre creare su Nagios un comando ed un servizio specifico, in modo che il nostro NMS sia in grado di effettuare il controllo degli aggiornamenti in modo automatico e ad intervalli di tempo regolari.

Configurazione di NRPE_NT

Poichè la verifica della disponibilità degli aggiornamenti è un’operazione che può richiedere parecchio tempo, è necessario fare in modo che il timeout di NRPE_NT venga appositamente incrementato, editando la direttiva command_timeout presente all’interno del file nrpe.cfg (nel mio caso ho impostato una soglia pari a 600 secondi):

command_timeout= 600

Per ciò che concerne lo scrip PowerShell, esso può essere scaricato da qui, per poi posizionarlo all’interno della directory Plugins di NRPE_NT. A questo punto, sempre lato NRPE_NT, è possibile creare un comando specifico che si occuperà di verificare la disponibilità degli aggiornamenti di Windows. Tale definizione va inserita all’interno del file V2_nrpe_commands.cfg e presenta la seguente struttura:

command[get_windows_updates]=cscrip.exe //nologo //T:600 c:\nrpe\plugins\v2\check_windows_updates.wsf /w:0 /c:1

Restartiamo infine il servizio NRPE_NT mediante i comandi:

net stop NRPE_NT
net start NRPE_NT

e passiamo alla configurazione di Nagios.

Configurazione di Nagios

Come già affermato in precedenza, la configurazione dell’NMS si basa su due passaggi: la creazione del comando e l’assegnazione del check che ne fa uso ad un servizio legato all’host da monitorare.

Di seguito riporto il comando (da inserire nel file commands.cfg presente in /etc/nagios/objects):

# nagios-WMI-windows-updates check command definition

define command {
        command_name    check_WMI_windows_updates
        command_line    /usr/lib64/nagios/plugins/check_nrpe -H $HOSTADDRESS$ -t $ARG1$ -c get_windows_updates
        }

Mentre il servizio è così definito:

define service{
        use                             generic-service         ; Name of service template to use
        host_name                       Windows-Machine
        service_description             query WMI Updates for Microsoft Windows Machine
        check_command                   check_WMI_windows_updates!600
        normal_check_interval           120
        }

Da notare che l’unico parametro passato al comando riguarda il numero di secondi di attesa (600) prima del timeout. Inoltre, ho fatto in modo che i check vengano eseguiti a distanza di 2 ore l’uno dall’altro (normal_check_interval 120).

A configurazione ultimata riavviamo Nagios per rendere effettive le modifiche:

[root@NMS ~]# service nagios reload

In caso di disponibilità di aggiornamenti, il cruscotto dell’NMS ci apparirà in un modo simile al seguente:

cruscotto_nagios

Alla prossima.

Cisco 877: configurare il QoS per il traffico VOIP (SIP/RTP)

Un po’ di teoria (ToS vs DiffServ)

Il QoS (acronimo che sta ad indicare il cosiddetto Quality of Service) è una tecnica L3 per la prioritizzazione del traffico, in grado cioè di garantire una banda minima e/o bassi livelli di latenza e jitter (a seconda dei casi e delle esigenze). Tutte le politiche di QoS si basano su un campo dell’header IP, denominato DS filed (un tempo ToS – Type of Service – ormai deprecato). In particolare, esso presenta la seguente struttura:

DS5     DS4     DS3     DS2     DS1     DS0     ECN     ECN

dove i 3 bit più significativi (DS5, DS4 e DS3) rappresentano, per il ToS, alla cosiddetta IP Precedence. Ad esempio, il pattern DiffServ 101 000 indica una priorità pari a 5 (ovvero la traduzione del digit 101 in decimale). Da ciò si evince che tutte le politiche QoS basate su DiffServ che tengono conto dei soli 3 bit più significativi, sono perfettamente retrocompatibili con il  ToS (e quindi con tutti i dispositivi più datati che supportano esclusivamente tale tipologia di QoS).

NB: I 6 bit DS prendono il nome di DSCP (Differentiated Services Code Point).

Il motivo per cui il protocollo DiffServ è riuscito a soppiantare quasi completamente il ToS è molto semplice: la sua maggiore granularità nella gestione del traffico interessante (ovvero quello oggetto delle politiche di QoS) rispetto al suo predecessore.

 Ancora teoria (DiffServ PHB)

qosdiagramIl DiffServ prevede 5 tipologie di comportamento (PHB – Per Hop Behaviour) che i router attraversati da uno specifico datagramma possono intraprendere. Essi sono i seguenti:

1) Default PHB, con i bit DS  settati a  000 000 – Best Effort (aka nessuna garanzia di instradamento);

2) Expedited Forwarding (EF PHB), con i bit DS settati a 101110 – il migliore in assoluto (poichè molto affidabile, prevede bassissimi tempi di latenza e valori di jitter minimi, nonchè basse probabilità che il datagramma venga droppato);

3) Assured Forwarding (AF PHB), che fa fede alla seguente tabella:

Low Drop AF11 (DSCP 10) AF21 (DSCP 18) AF31 (DSCP 26) AF41 (DSCP 34)
Med Drop AF12 (DSCP 12) AF22 (DSCP 20) AF32 (DSCP 28) AF42 (DSCP 36)
High Drop AF13 (DSCP 14) AF23 (DSCP 22) AF33 (DSCP 30) AF43 (DSCP 38)

4) Voice Admit PHB, con i bit DS settati a 101100 – identico all’EF PHB per affidabilità e bassa latenza, si basa sul CAC (Call Admission Control);

5) Class Selector (CS PHB):  retrocompatibile con l’IP Precedence del ToS, fa fede alla seguente tabella:

DSCP    Binary     Decimal  Typical Application     Examples
CS0     000 000    0        N/A                  N/A (best effort)    
CS1     001 000    8        Scavenger            YouTube, Gaming, P2P
CS2     010 000    16       OAM                  SNMP,SSH,Syslog
CS3     011 000    24       Signaling            SCCP,SIP,H.323
CS4     100 000    32       Realtime             TelePresence
CS5     101 000    40       Broadcast video      Cisco IPVS
CS6     110 000    48       Network Control      EIGRP,OSPF,HSRP,IKE

In definitiva, è possibile utilizzare la seguente tabella per la mappatura dei pattern DSCP con l’IP Precedence:

Value Decimal Meaning Drop Probability Equivalent IP Precedence Value
 101 110 46   EF               N/A        101 Critical
 000 000  0   Best Effort      N/A        000 - Routine
 001 010 10   AF11             Low        001 - Priority
 001 100 12   AF12             Medium     001 - Priority
 001 110 14   AF13             High       001 - Priority
 010 010 18   AF21             Low        010 - Immediate
 010 100 20   AF22             Medium     010 - Immediate
 010 110 22   AF23             High       010 - Immediate
 011 010 26   AF31             Low        011 - Flash
 011 100 28   AF32             Medium     011 - Flash
 011 110 30   AF33             High       011 - Flash
 100 010 34   AF41             Low        100 - Flash Override
 100 100 36   AF42             Medium     100 - Flash Override
 100 110 38   AF43             High       100 - Flash Override

Definizione delle politiche di QoS sul router Cisco 877

Dopo questa breve carrellata teorica veniamo alla pratica. Per prima cosa occorre distinguere tra le politiche di QoS in ingresso e quelle in uscita.

Le prime si basano sul cosiddetto CAR (Committed Access Rate); per le seconde, invece, bisogna utilizzare l’accoppiata class-map/policy-map. In entrambi i casi occorre definire un’opportuna ACL per l’identificazione del traffico interessante, ad esempio:

access-list 105 remark inbound VOIP (SIP/RTP) traffic gets top priority (5) with CAR
access-list 105 permit udp any any eq 5060
access-list 105 permit udp any any range 10000 20000

Alla luce di quanto detto, per il QoS del traffico in ingresso possiamo utilizzare la seguente configurazione:

rate-limit input access-group 105 128000 65536 65536 conform-action set-prec-transmit 5 exceed-action set-prec-continue 0 
rate-limit input 13414000 13000000 13400000 conform-action transmit exceed-action drop

Per il traffico in uscita, invece, dobbiamo digitare:

class-map voice
match access-group 105

policy-map policy1
class voice
priority 128
class class-default
bandwidth 795
fair-queue

Per prima cosa abbiamo definito la class-map voice che fa uso dell’ACL precedentemente definita. La suddetta class-map andrà a popolare la policy-map policy1 (che può essere vista come una sorta di contenitore di una o più class-map), dentro la quale definiremo il traffico prioritario (grazie alla direttiva priority). Il valore si intende espresso in kbps.

La class-default, invece, consente di riservare la banda minima garantita per tutto il traffico che non rientra nel QoS, dove fair-queue è sinonimo di best effort. Anche in questo caso parliamo di kbps. Ovviamente i valori assegnati sono stati calcolati basandosi sull’uplink rate dell’interfaccia ADSL del router.

NB: per traffico prioritario si intende quello con bassa probabilità di drop e bassi valori di latenza/jitter.

L’ultimo step consiste nell’assegnazione della policy-map policy1 all’interfaccia fa0 (LAN) ed ATM0 (WAN). Per la LAN avremo:

 interface FastEthernet0
 no ip address
 service-policy output policy1

mentre per la WAN:

interface ATM0
 no ip address
 no atm ilmi-keepalive
 pvc 8/35
  vbr-nrt 923 923 1
  tx-ring-limit 3
  service-policy out policy1
  pppoe-client dial-pool-number 1
 !

In particolare, la policy-map va assegnata direttamente al PVC ATM (e non all’interfaccia dialer come ci si potrebbe aspettare), previa definizione delle direttive vbr-nrt e tx-ring-limit.

La prima è indispensabile per la politica di controllo del traffico (detta GTS Generic Traffic Shaping) supportata da questo tipo di interfaccia, dove il termine vbr-nrt sta per variable bit rate not real time (GTS ed ubr, ovvero l’undefined bit rate impostato di default sul PVC, non sono tra loro compatibili). La sintassi da utilizzare è la seguente:

vbr-nrt <Peak Cell Rate> <Sustained Cell Rate> <Maximum Burst Size>

Inoltre, per individuare i valori di PCR ed SCR ho applicato la seguente prassi:

1) ho identificato l’uplink rate della linea ADSL, utilizzando il comando:

sh atm int atm0

il cui output è simile al seguente:

VCIs per VPI: 1024,
Max. Datagram Size: 4528
PLIM Type: ADSL - 972Kbps Upstream, DMT, TX clocking: LINE

2) ho calcolato il 95% dell’ upstream rate, ed il valore risultante è servito a popolare sia PCR che SCR;

3) ho impostato l’MBR a 1.

La seconda direttiva (ovvero tx-ring-limit), serve, invece, per la gestione della coda (a livello HW) dell’interfaccia ATM. Nella fattispecie, limitando il buffer a soli 3 pacchetti, si fa in modo che la politica QoS possa essere applicata ai dati prima del loro ingresso nel buffer (dove il QoS non può più essere attuato).

Per ciò che concerne la visualizzazione delle statistiche relative al QoS (ad esempio il drop rate), è sufficiente utilizzare il comando show policy map, specificando l’interfaccia oggetto di analisi:

#sh policy-map interface atm0

il cui output sarà simile al seguente:

 ATM0: VC 8/35 -

  Service-policy output: policy1

    queue stats for all priority classes:

      queue limit 64 packets
      (queue depth/total drops/no-buffer drops) 0/0/0
      (pkts output/bytes output) 5590/3316821

    Class-map: voice (match-all)
      5590 packets, 3316821 bytes
      5 minute offered rate 0 bps, drop rate 0 bps
      Match: access-group 105
      Priority: 128 kbps, burst bytes 4470, b/w exceed drops: 0

    Class-map: class-default (match-any)
      270413 packets, 94967696 bytes
      5 minute offered rate 7000 bps, drop rate 0 bps
      Match: any
      Queueing
      queue limit 64 packets
      (queue depth/total drops/no-buffer drops/flowdrops) 0/147/0/147
      (pkts output/bytes output) 270266/94753611
      Fair-queue: per-flow queue limit 16
      bandwidth 795 kbps

Prima di concludere il post occorre fare una precisazione: ho adoperato un’ACL per la definizione del traffico interessante poichè, in questo modo, lato PBX non è stato necessario effettuare alcuna modifica di configurazione. Infatti, per utilizzare, ad esempio, una class-map così definita:

class-map match-any VOIP-SIGNAL
 match ip dscp cs5
 match ip precedence 4
 match ip precedence 3
class-map match-any VOIP-RTP
 match ip dscp ef
 match ip precedence 5

sarebbe stato necessario imporre al PBX (Asterisk nel mio caso) di “modificare” il contenuto dell’header dei pacchetti IP (DS Field) applicando un particolare pattern DSCP o un determinato valore di IP Precedence (definendo, nel file sip.conf, delle entry come tos_sip=cs3 per la segnalazione, tos_audio=ef per il traffico RTP audio e tos_video=af41 per il traffico RTP video).

E’ tutto, alla prossima.

PS: La banda che ho definito per il QoS si riferisce ad un’unica chiamata in ingresso/uscita. In particolare, essa dipende strettamente da 2 fattori, ovvero:

1) il numero di chiamate che si vogliono gestire in contemporanea;

2) il tipo di codec da utilizzare per il traffico voce (specificato all’interno del protocollo SDP, insieme alle porte coinvolte nel traffico RTP). Per maggiori dettagli sulla corrispondenza codec/banda è possibile consultare la seguente tabella:

VoIP-Codec-table-from-Cisco-sml

CentOS 6: monitorare le ACL del nostro router Cisco mediante Nagios ed snmptt

Durante gli ultimi mesi ho discusso ampiamente della configurazione di Nagios per la ricezione dei check passivi, quali trap SNMP o security alert. Adesso vi mostrerò come integrare le predette tipologie di allarmi in modo da riuscire a monitorare le ACL del nostro router Cisco.

Premessa

Esiste un modo più veloce per ottenere il monitoraggio delle ACL rispetto a quello che sto per illustrare. Esso consiste, fondamentalmente, nella configurazione di un syslog server (dotato dell’applicativo swatch per l’analisi in tempo reale dei file di log) a cui il nostro router Cisco dovrà puntare. Nel caso in cui un determinato pattern (ad esempio / denied /) dovesse essere matchato da swatch, quest’ultimo genererà un’opportuna notifica email da inoltrare all’amministratore di rete.

La mia configurazione, invece, si basa sulla logica seguente: il router Cisco genererà delle opportune trap SNMP segnalando ciò che avviene a livello di ACL (traffico consentito oppure negato). Esse verranno intercettate da snmptrapd e tradotte da snmptt, per poi essere elaborate dall’event handler submit_check_result e date in pasto a Nagios. Ho optato per tale configurazione poichè sulla mia linux box che funge da syslog server e da NMS sono già attivi sia snmptrapd che snmptt e quindi ho ritenuto conveniente sfruttarli piuttosto che mettere in funzione swatch.

nagiosConfigurazione del router Cisco

La configurazione del router Cisco si basa in 3 passaggi: settaggio della direttiva log per ciascuna entry che andrà a formare l’ACL da monitorare,  settaggio del syslog server a cui inviare le trap ed abilitazione di queste ultime.

Ad esempio, l’ACL di nostro interesse dovrà essere formata da entry di questo tipo:

access-list 102 deny ip host 255.255.255.255 any log

Inoltre, per definire la community string RO (read only) e l’indirizzo IP del server che si occuperà della ricezione delle trap, occorrerà digitare le seguenti direttive:

snmp-server host 192.168.1.1 keypublic
snmp-server community keypublic RO

mentre le trap potranno essere abilitate nel modo seguente:

snmp-server enable traps snmp authentication linkdown linkup coldstart warmstart
snmp-server enable traps tty
snmp-server enable traps config-copy
snmp-server enable traps config
snmp-server enable traps resource-policy
snmp-server enable traps cpu threshold
snmp-server enable traps syslog
snmp-server enable traps firewall serverstatus

 Contenuto dello scrip submit_check_result

Secondo quanto già riportato in questo post, sappiamo che snmptt effettua una traduzione “statica” delle trap (mediante il comando snmpttconvertmib). Per tale motivo, affinchè si possa indurre Nagios a generare dei security alert solo dopo la ricezione di trap ben determinate, occorre modificare il codice sorgente relativo all’event handler submit_check_result. Di seguito riporto il contenuto dello scrip in questione da me customizzato:

 echocmd="/bin/echo"

CommandFile="/var/spool/nagios/cmd/nagios.cmd"

# get the current date/time in seconds since UNIX epoch
datetime=`date +%s`

# create the command line to add to the command file
if [[ $1 == "ip router" ]];then

        if [[ $4 =~ "denied igmp" ]];then

                exit 0;

        elif  [[ $4 =~ "denied" ]];then

                service="Router ACL Connection Denied";

                output="$4 | connection_denied=1"
                cmdline="[$datetime] PROCESS_SERVICE_CHECK_RESULT;router;$service;$3;$output";

        else

                cmdline="[$datetime] PROCESS_SERVICE_CHECK_RESULT;router;$2;$3;$4";

        fi

else

   cmdline="[$datetime] PROCESS_SERVICE_CHECK_RESULT;$1;$2;$3;$4";

fi

# append the command to the end of the command file
`$echocmd $cmdline >> $CommandFile`

Nella fattispecie, vengono dapprima identificate le trap che riguardano il router. Successivamente, nel caso in cui esse contengano il pattern igmp denied, lo scrip uscirà senza effettuare alcuna operazione. Tale “scrematura” è stata necessaria poichè il mio ISP effettua regolarmente del polling IGMP mediante il proprio querier 192.168.100.1 (per il servizio IPTV).

Nel caso in cui, invece, le trap dovessero contenere la stringa denied, la variabile cmdline verrà popolata con l’host name dell’oggetto monitorato da Nagios (per il quale si dovrà aggiornare lo stato del servizio che si occupa di tenere d’occhio le ACL). Nel mio caso tale host name è semplicemente router. Inoltre, l’output  verrà rimaneggiato in modo da tenere traccia delle performance data (graficizzandole mediante l’uso di pnp4nagios).

MIB da utilizzare e configurazione di Nagios

Per prima cosa occorre scaricare le MIB CISCO-SYSLOG-MIB e CISCO-SYSLOG-MIB-V1SMI dal repository FTP della Cisco, effettuandone il parsing mediante snmpttconvertmib.

Successivamente, su Nagios occorrerà definire per l’host name router il servizio che si occuperà di monitorare le ACL:

define service{
        use                             local-service
        host_name                       router
        service_descripion              Router ACL Connection Denied
        check_command                   check_passive
        passive_checks_enabled          1
        active_checks_enabled           0
        max_check_attempts              1
        is_volatile                     1
        check_freshness                 1
        freshness_threshold             600
        flap_detection_enabled          0
        }

Come ultimo step, a configurazione ultimata possiamo riavviare Nagios per rendere effettive le suddette modifiche:

[root@NMS ~]# service nagios reload

ed abbiamo finito.

A presto.

NRPE_NT: script powershell per la modifica della direttiva allowed_hosts in nrpe.cfg

Scenario

Sistema di monitoraggio (Nagios) connesso ad Internet mediante indirizzo IP pubblico di tipo dinamico. Esso deve tenere sotto controllo, tramite opportune query NRPE, alcuni servizi attivi su di un server remoto (Windows 2008 R2), raggiungibile mediante FQDN ed IP statico.

powershellProblema

Per evitare che eventuali client non autorizzati possanno connettersi al servizio NRPE_NT in ascolto sul server remoto (porta 5666/TCP), è possibile definire, all’interno del suo file di configurazione (nrpe.cfg), gli indirizzi IP a cui è consentito eseguire le query. Ad esempio, mediante la direttiva allowed_hosts, siamo in grado di dichiarare la seguente regola:

allowed_hosts=79.37.83.59

ed ecco, per l’appunto, dov’è il problema: essendo il suddetto indirizzo definito in modo statico, nel momento in cui, per un motivo o per un altro, Nagios dovesse cambiare il proprio IP pubblico, le query NRPE fallirebbero miseramente (check timeout).

Soluzione

A questo punto è necessario fare una premessa: esiste una soluzione più pulita di quella che sto per riportare, che consiste, fondamentalmente, nell’uso di NSClient++ abbinato ad NRPE_NT, grazie ai quali è possibile realizzare un meccanismo di autenticazione client/server mediante lo scambio di certificati X.509 (in tal caso la direttiva allowed_host sarebbe del tutto superflua poichè autenticazione e confidenzialità verrebbero garantite mediante i suddetti certificati).

Per ciò che concerne, invece, la soluzione da me individuata, essa consiste nella realizzazione di uno scrip powershell che “interroga” l’FQDN di Nagios ogni 5 minuti, verificando che il suo indirizzo IP pubblico sia diverso da quello presente nel file di configurazione di NRPE. In tal caso lo scrip modificherà l’IP definito nella direttiva allowed_hosts.

Di seguito riporto il contenuto dello scrip:

$IP = nslookup nagios.vostrodominio.com | Select-String -Pattern '\d{1,3}(\.\d{1,3}){3}' | Select-String -notmatch 'IP del server da monitorare'

$PublicIP = $IP.tostring().Split(" ")[-1]

$OldIP = (Get-Content C:\nrpe\bin\nrpe.cfg) | Select-String 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}'

$OldPublicIP = $OldIP.tostring().Split("=")[-1]

$data = Get-Date;

if ($PublicIP -ne $OldPublicIP -and $PublicIP)
{

    (Get-Content C:\nrpe\bin\nrpe.cfg) -replace 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}', "allowed_hosts=$(echo $PublicIP)" | Set-Content C:\nrpe\bin\nrpe.cfg

    net stop nrpe_nt

    Start-Sleep -s 5

    net start nrpe_nt

    echo "$data NRPE_NT configuration has been updated. The new authorized Public IP is $PublicIP"

    exit 0;
}
else
{
    echo "$data Nothing to do. Exiting..."

    exit 0;
}

Per prima cosa ho individuato l’indirizzo IP pubblico di Nagios mediante una query di tipo A effettuata mediante nslookup:

$IP = nslookup nagios.vostrodominio.com | Select-String -Pattern '\d{1,3}(\.\d{1,3}){3}' | Select-String -notmatch 'IP del server da monitorare'

$PublicIP = $IP.tostring().Split(" ")[-1]

Ovviamente ho dovuto formattare l’output relativo alla suddetta query, identificando, tramite espressione regolare, le stringhe che riportano gli indirizzi IP coinvolti, escludendo successivamente quella che contiene l’IP del server da monitorare. Inoltre, è stato necessario salvare (dopo opportuna conversione) l’indirizzo IP di Nagios all’interno della variabile $PublicIP.

Discorso molto simile riguarda l’individuazione dell’IP configurato all’interno di nrpe.cfg, ottenuta mediante l’applet Get-Content:

$OldIP = (Get-Content C:\nrpe\bin\nrpe.cfg) | Select-String 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}'

$OldPublicIP = $OldIP.tostring().Split("=")[-1]

A questo punto è stato possibile procedere con il confronto tra l’IP attuale di Nagios e quello presente nel file di configurazione di NRPE_NT. Nel caso in cui questi differiscano (e la variabile $PublicIP non sia nè vuota, nè nulla), la direttiva allowed_hosts verrà modificata, con il conseguente riavvio del servizio in oggetto:

(Get-Content C:\nrpe\bin\nrpe.cfg) -replace 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}', "allowed_hosts=$(echo $PublicIP)" | Set-Content C:\nrpe\bin\nrpe.cfg

    net stop nrpe_nt

    Start-Sleep -s 5

    net start nrpe_nt

    echo "$data NRPE_NT configuration has been updated. The new authorized Public IP is $PublicIP"

Infine, per eseguire tale scrip in modo automatico, ho creato un task mediante l’applicativo Task Scheduler di Windows, il quale si ripete giornalmente ogni 5 minuti e la cui azione è la seguente:

powershell -command "C:\Users\Administrator\Documents\scrip\nrpe_ip_update.ps1 >> C:\nrpe_ip_update.log"

Da ora in poi le query NRPE da indirizzo IP dinamico non saranno un problema.

A presto.

Avast! free e lo scan che non ti aspetti

Ieri, spulciando gli allarmi di Nagios relativi alla mia rete domestica, ho notato la presenza di una sequela di eventi di questo tipo:

***** Nagios HOME *****

 Notification Type: PROBLEM

 Service: HTTP Not Found
 Host: localhost
 Address: 127.0.0.1
 State: WARNING

 Date/Time: Sat Dec 26 09:04:58 CET 2015

 Additional Info:

 192.168.1.8 - - [26/Dec/2015:09:04:57 +0100] GET /HNAP1/ HTTP/1.1 404 204

ovvero il mio PC client (192.168.1.8) ha provato, in modo automatico, ad accedere a determinate URI HTTP, puntando all’indirizzo IP del suo default gateway (192.168.1.1). In particolare, il file /var/log/httpd/error_log di quest’ultimo riportava le seguenti hit:

[Sat Dec 26 09:04:57 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/HNAP1
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/rom-0
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] scrip not found or unable to stat: /var/www/cgi-bin/webproc
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/a2
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/ajaxmail
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/arr
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/at3
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/atc
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/atx
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/auth
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/bbs
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/bbs
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/bp_revision.cgi
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/br5.cgi
[Sat Dec 26 09:05:03 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/click.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/clicks.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/crtr
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/fg.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/findweather
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/findweather
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/frame_html
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/getattach
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/hotspotlogin.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/hslogin.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/ib
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/index.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/index
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/krcgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/krcgistart
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/link
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/login.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/login
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/logout
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/logout
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/mainmenu.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/mainsrch
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/msglist
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/navega
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/openwebmail
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/out.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/passremind
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/rbaccess
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/rbaccess
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/readmsg
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/rshop.pl
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/search.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/spcnweb
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/sse.dll
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/start
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/te
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/tjcgi1
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/top
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/traffic
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/verify.cgi
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/webproc
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/webscr
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/wingame.pl
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/das
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/fcgi-bin
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/fcgi-bin
[Sat Dec 26 09:05:04 2015] [error] [client 192.168.1.8] File does not exist: /var/www/html/redir

Alla luce di ciò mi sono allarmato, e, credendo che si trattasse di un malware, ho trascorso circa 2 ore tra scansioni antivirus (Avast! Free per l’appunto), Malwarebytes e SuperAntiSpyware, senza ottenere grandi risultati. Infine, giusto per scrupolo, sono andato a controllare il file /var/log/httpd/access_log che riportava lo UA (User Agent) utilizzato per accedere alle suddette URI. Di seguito ne riporto il contenuto:

192.168.1.8 - - [26/Dec/2015:09:04:57 +0100] "GET / HTTP/1.1" 200 - "-" "avast! Antivirus"
192.168.1.8 - - [26/Dec/2015:09:04:57 +0100] "GET /HNAP1/ HTTP/1.1" 404 204 "-" "avast! Antivirus"
192.168.1.8 - - [26/Dec/2015:09:05:03 +0100] "GET /rom-0 HTTP/1.1" 404 203 "-" "avast! Antivirus"
192.168.1.8 - - [26/Dec/2015:09:05:03 +0100] "GET /cgi-bin/webproc?getpage=/../../etc/passwd&var:language=en_us&var:page=* HTTP/1.1" 404 213 "-" "avast! Antivirus"

ovvero l’origine del presunto “attacco” era, molto semplicemente, Avast! free. Infatti, andando a spulciare tra le funzionalità del suddetto antivirus, ho notato la presenza della cosiddetta Protezione rete domestica, la quale non fa altro che scansionare il range di IP della LAN su cui è attestato il client, identificando i vari dispositivi connessi ed i servizi attivi su ciascuno di essi. avast In più, credendo che il mio default gateway fosse uno dei tanti home router dozzinali che si trovano ai discount, ha iniziato a ricercare le suddette URI palesemente vulnerabili (per maggiori info basta cercare home router vulnerabilities su Google).

Tutto è bene quel che finisce bene.

Alla prossima.