span.fullpost {display:inline;}

venerdì 14 agosto 2009

Attacco e difesa...un esempio pratico: difesa iniziale

Casualmente sono venuto in possesso di un rootkit/trojan per linux. Voglio provare ad analizzarlo per verificare quanto sia in grado di coprire la propria presenza e quanto un sistema decentemente protetto sia in grado di resistere o di segnalare il problema.
Per fare questo mettiamo in piedi una sandbox pulita e protetta al meglio possibile. Come sempre la partenza è una Debian hardenizzata con bastille.

In particolare:
1) Un utente non root non può eseguire le utilità di amministrazione, come ifconfig, fsck... questo permette di ridurre la possibilità di exploit su programmi setuid.
2) Tutti i servizi insicuri (occhio, però, a considerare troppo sicuro ssh...) sono chiusi
3) è consentito a root di connettersi solo da locale. Viene disabilitata la connessione dall'esterno.
Per proteggersi dai rootkit è bene, oltre a usare un detector specifico, avere una tabella degli hash md5 per tutti i file installati sul sistema. Debian mette a disposizione un tool per fare questo.

sandbox:~# apt-get clean
sandbox:~# aptitude install debsums
...
sandbox:~# debsums_init
...
Finished generating md5sums!
Checking still missing md5files...

In questo modo abbiamo creato una serie di hash di tutti i pacchetti installati sul sistema. Per ogni nuovo pacchetto installato verrà creato l'hash.
Si può, a questo punto, creare una voce in crontab di root che periodicamente effettui il controllo degli hash:

0 0 * * * debsums -c | logger -t "Hash modificato" -p warning >/dev/null 2>&1

in questo modo ogni mezzanotte verrà effettuato un controllo degli hash e, in caso di variazione, nei messages di sistema comparirà una linea del tipo (se si vuole controllare anche gli hash dei file di configurazione basta dare debsums -ca invece che debsums -c):

...
Aug 14 00:00:10 sandbox Hash modificato: /var/lib/aspell/it.compat
...


Ora chiaramente c'è un problema fondamentale... un attaccante può sostituire un eseguibile, magari con una versione troianizzata, ricalcolare l'hash e inserirlo nella tabella. In questo modo il sistema non segnalerà l'anomalia.
Possiamo procedere in questa maniera:
1) creiamo uno script che estrae tutti gli hash e li ingloba in un unico file
2) spostiamo questo file su un supporto read only
3) effettuiamo il controllo degli hash confrontando quelli generati per i vari file con la tabella salvata
4) creiamo uno script che ci permetta di modificare gli hash presenti nella tabella quando, ad esempio, aggiorniamo il sistema.
Un modo più raffinato consiste nell' inserire tutte le info in un db. In tal modo (ammesso che l'attaccante non riesca anche a bucare il db...) sarà più semplice la gestione degli upgrade e della modifica dei singoli hash.
Vediamo la soluzione più grezza ;)
1.
Gli hash si trovano in /var/lib/dpkg/info all'interno della directory ci sono una serie di file, ciascun file rappresenta un pacchetto installato e, per ciascun pacchetto, il file con estensione md5sums contiene l'hash per tutti i singoli file installati.
una prima bozza di script potrebbe essere:

#!/bin/bash
# Script /usr/bin/chkhash
#
SOURCE_DIR="/var/lib/dpkg/info"
TARGET="/var/tmp/md5_hash_table"

if [ ! -e $TARGET ]; then
touch $TARGET
else
rm $TARGET && touch $TARGET
fi

find $SOURCE_DIR -name '*.md5sums' -exec cat {} \; > $TARGET

2.
Il file /var/tmp/md5_hash_table va ora spostato su un supporto protetto da scrittura. A questo proposito magari si può usare una SD che può essere protetta e sprotetta, in modo da non dover poi utilizzare enormi quantità di CD man mano che si modificano gli hash.
3. e 4. :)
supponendo che, ora, il file degli hash sia su /dev/sda1 e che questo device sia montato su /mnt/sda1 andremo a effettuare il controllo di integrità dei file direttamente dalla lista appena prodotta:

sandbox:~# debsums -m /mnt/sda1/md5_hash_table -c
/var/lib/aspell/it.compat

quindi nel crontab andremo a modificare l'istruzione:

0 0 * * * debsums -c -m /mnt/sda1/md5_hash_table | logger -t "Hash modificato" -p warning >/dev/null 2>&1

5.
Come dicevo in precedenza a seguito di un aggiornamento o anche per l'installazione di qualche nuova funzionalità e, comunque, a seguito della modifica anche di un solo byte del file, l'hash cambia. Per fare un esempio, supponiamo di avere un file "a" composto da 1000 caratteri a e un file "b" composto da 999 caratteri a e un carattere b. Proviamo a fare l'hash di ciascun file:

sandbox:~# perl -e 'print "a" x1000' > a
sandbox:~# echo -n "b" > b
sandbox:~# perl -e 'print "a" x999' >> b
sandbox:~# diff a b
1c1
< aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
---
> baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
sandbox:~# md5sum a
d4bce459cbbb478eda5d9bf3bf7e2db3 a
sandbox:~# md5sum b
1760ddef13fa66838b8356333c56ada6 b

Come si può vedere gli hash sono del tutto diversi, anche per la modifica dello 0.1% del contenuto del file.
A questo punto sono possibili due soluzioni. La prima prevede, semplicemente, di verificare tutte le variazioni negli hash che vengono riportate nei messages di sistema, individuando quali possono corrispondere a modifiche non volute o estranee. Questo sicuramente potrebbe essere un approccio "safe" però richiede anche una certa opera di analisi dei log.
Altrimenti possiamo effettuare uno script che aggiorni, dopo installazioni e/o upgrade la lista degli hash. La cosa più semplice che si possa fare è inserire nel file /etc/apt/apt.conf.d/90debsums una chiamata allo script realizzato in precedenza.
90debsums contiene le istruzioni che vengono effettuate da apt-get (e da aptitude) dopo l'installazione del pacchetto. Nel mio sistema il file ha il seguente contenuto:

DPkg::Post-Invoke { "if [ -x /usr/bin/debsums ]; then /usr/bin/debsums --generate=nocheck -sp /var/cache/apt/archives; fi"; };

Volendo integrare lo script che abbiamo scritto prima:

DPkg::Post-Invoke { "if [ -x /usr/bin/debsums ]; then /usr/bin/debsums --generate=nocheck -sp /var/cache/apt/archives && /usr/bin/chkhash; fi"; };

In questo modo ad ogni installazione, update... verrà rigenerata la lista completa di hash del sistema. A questo punto non resta che copiarla sul supporto che poi renderemo protetto da scrittura. Se si sceglie questa strada è bene non sovrascrivere direttamente la versione precedente della lista, in modo da poter eventualmente avere uno storico delle varie modifiche. Il limite di questo sistema è che se un attaccante scala i privilegi e diventa root potrebbe installare un rootkit come fosse un pacchetto, attivando quindi il processo di ricalcolo del checksum.
Il mio consiglio è quello di aggiornare periodicamente la lista degli hash, verificando ogni segnalazione che compaia nei log di sistema per capire se si tratta della modifica di un file innocuo oppure no.
Attenzione che, comunque, questo sistema non calcola l'hash di altri file critici come potrebbe essere /etc/passwd o /etc/shadow e, in genere, di tutto quello che si trova in /etc, salvo pochissimi casi.
Infine trovo sia utile utilizzare, sempre schedulandolo in crontab, anche chkrootkit che, basandosi su firme e/o meccanismi euristici riesce a determinare se i file del sistema siano stati o meno compromessi. E' bene, chiaramente, effettuare da subito la scansione in modo da avere un punto iniziale consistente:

sandbox:/var/tmp# aptitude install chkrootkit
...
sandbox:/var/tmp# chkrootkit -e
ROOTDIR is `/'
Checking `amd'... not found
Checking `basename'... not infected
Checking `biff'... not found
Checking `chfn'... not infected
Checking `chsh'... not infected
...


A questo punto il sistema è abbastanza protetto, nelle prossime puntate ci caleremo nel ruolo dell'attaccante e, nell'ultima, dell'analista forense :D

[EDIT]: ovviamente il sistema dovrebbe essere protetto anche dall'esterno, con l'utilizzo di firewall, ids... questa vuole essere solo una dimostrazione :) quindi supporremo che l'attaccante sia già entrato nel sistema :)

0 commenti: