Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Tecniche: SymLink Attack

Attacco attraverso un collegamento simbolico: le tecniche per ottenere l'accesso di root e i metodi per difendersene
Attacco attraverso un collegamento simbolico: le tecniche per ottenere l'accesso di root e i metodi per difendersene
Link copiato negli appunti

Introduzione

Mettiamoci nei panni di un attaccante che volesse introdursi in un
sistema informatico di tipo UNIX. La scelta tra i vari tipi di attacco
è vasta, ma una possibilità interessante sarebbe potere modificare,
in maniera conveniente, un file importante di configurazione, ad esempio
/ETC/PASSWD. Ovviamente non è pensabile che lo possa fare
direttamente, in quanto i files di configurazione di qualche rilievo
sono protetti da scrittura, a volte anche da lettura, nei confronti
degli utenti diversi da "root". Per cui sarà costretto a sfruttare
qualche bug di un'applicazione che opera con i privilegi di
"root". Anche in questo caso le possibilità sono molte, tra
le queste c'è il tipo di attacco che andiamo ad analizzare, il SymLink
Attack
.

La causa

I sistemi UNIX hanno un tipo di file molto particolare i "symlink",
ovvero "symbolic link", ovvero "collegamenti simbolici".
Per creare un "symlink" si usa:

$ ln -s /path/file_linkato /path/file_symlink

Tramite un symlink è possibile creare degli alias per qualunque files
o directory presente sul sistema, in maniera molto più elastica che
non usando un normale "hardlink".

Questi ultimi sono semplicemente delle associazioni tra una etichetta
(il nome del file o del link) e il numero identificativo "inode"
del file su uno specifico filesystem, per cui soffrono di molte limitazioni,
tra le quali, l'impossibilità di linkare directory, l'impossibilità
di utilizzarli su filesystem diversi dai classici UNIX e l'impossibilità
di linkare un file che stia su un'altra partizione o disco.

I symlink superano tutti questi problemi trattandosi di veri
e propri files che contengono un riferimento al file di destinazione.

Alcune "system call" (ovvero chiamate di sistema
- cioè funzioni utilizzate dal sistema operativo per svolgere le funzioni
necessarie) agiscono direttamente sul file di link, ma la maggior
parte agiscono sul file linkato di destinazione.

Tra le system call che operano sul file destinazione, anche
se chiamate in riferimento al file di link, c'è "open",
che è utilizzata per la lettura e scrittura su file.

Quindi, riassumendo: se abbiamo un programma "PRINT"
che vuole scrivere sul file "FILE_LINK", che è un
symlink al file "FILE_LINKATO", succederà che
"PRINT" scriverà su "FILE_LINKATO".

Il problema

Se il file "FILE_LINKATO" appartiene a root e non
ha permessi di scrittura per gli user, ad esempio:

-rw-r-r-  1 root   root    5650  3 mar  2004 file_linkato

potrà essere modificato solo dal primo, e quindi tramite un programma
azionato da lui o che abbia il bit "SETUID" attivo.
A parte ciò è comunque necessario che il programma punti, per le sue
operazioni di scrittura, al file bersaglio. Sembrerebbe quindi una
strada impercorribile per l'attaccante. Non è così!

Il problema nasce dal fatto che se il programma "PRINT",
azionato da root o con il bit setuid attivo, punta, per la scrittura,
all'innocuo file "FILE_INNOCUO":

-rw-r-r-  1 root   root    5650  3 mar  2004 file_innocuo

e l'attaccante riesce a sostituire quest'ultimo con un symlink che punta al suo vero bersaglio:

lrwxrwxrwx  1 user   user    16    3 mar  2004 file_innocuo -> /etc/passwd

otterrà lo scopo di sovrascrivere il file delle password! E ciò anche
se il symlink appartiene ad un utente qualunque.

In condizioni normali però non è possibile per un utente sostituire
un file che non gli appartiene, ma il discorso cambia se "FILE_INNOCUO"
non esiste già e deve essere creato dal programma di esempio "PRINT".
In tal caso se lo stesso, al momento di scrivere, trova il symlink,
come detto sopra, il gioco è fatto. Ovviamente, per creare il symlink,
presupposto necessario, è che deve essere consentita la scrittura
nella directory in cui deve stare.

L'errore

Riassumendo, quali sono le condizioni affinchè un attaccante possa
condurre un symlink attack?

  1. Avere accesso al sistema come utente senza privilegi.
  2. Un programma azionato da root o con il bit setuid attivo, ad esempio
    un daemon (servizio), sul cui output un utente possa influire.
  3. Che questo programma riversi tale output in un file che viene creato
    al volo.
  4. Che tale file venga creato in una directory scrivibile da tutti.

Queste condizioni ricorrono tipicamente nel caso di programmi che usano file temporanei per lo svolgimento delle loro funzioni.

Per motivi praticità e per rispetto degli standard, in genere, a questi
scopi viene utilizzata la directory:

drwxrwxrwt  31 root   root     4096  9 dic 18:06 /tmp

che come si può vedere è scrivibile da chiunque, e spesso, purtroppo,
il nome del file temporaneo, pur essendo univoco, è predicibile.

L'attacco

Un metodo piuttosto diffuso di determinazione del nome del file temporaneo,
che garantisce l'univocità, è basato sull'unione di un prefisso al
PID (numero di identificazione del processo) del programma, ad esempio
in Perl (ma il concetto vale per qualunque linguaggio di programmazione)
potrebbe essere:

$name = "programma".$$.".tmp"; # $$ contiene il PID del processo

che produrrà qualcosa del genere:

-rw-r-r-  1 root   root    5650  3 mar  2004 programma23456.tmp

a questo punto l'attaccante, tramite una qualunque console o terminale,
può verificare lo stato dei processi tramite, ad esempio il comando
ps:

$ ps -x

....

1862 ?        S      0:02 /usr/bin/lyx

1876 pts/1    S      0:00 /bin/bash

1907 pts/1    R      0:00 ps -x

ottiene la lista dei processi attivi in ordine di PID (il primo numero
sulla riga).

Il sistema quando crea un nuovo processo gli assegna un PID crescente,
superiore nel nostro caso a 1907, ed in genere consecutivo. Siccome
non possiamo essere sicuri che al momento in cui agiremo il contatore
del sistema non si sia già spostato, per prudenza possiamo pensare
che il PID del programma "PRINT" di esempio che vogliamo
sfruttare quando lo azioneremo sarà tra i successivi 100 (o anche
1000) rispetto a 1907. Per cui ci basterà digitare da linea di comando,
stando nella directory ove verrà creato il file temporaneo:

$ perl -e 'for($i=1908; $i<=2008; $i++){symlink("/etc/passwd",  "programma$i.tmp");}'

Si tratta di un banale programmino Perl da linea di comando che crea
100 symlink al file bersaglio. Ovviamente si può usare qualuque
altro sistema. Il programmino è così composto:

perl -e " - dice all'interprete di eseguire il codice posto tra le"

for () {} - crea un loop per cui il codice contenuto tra le {}
viene eseguito fino a che la condizione posta tra () è vera.

($i=1908; $i<=2008; $i++) - il loop verrà eseguito 100 volte fino a che $i che parte da 1908, ed è incrementato ad ogni ciclo, non sarà 2008

symlink("/etc/passwd","programma$i.tmp"); - ad ogni ciclo
viene eseguita la funzione di Perl symlink che crea un link simbolico
al file bersaglio (/etc/passwd) chiamato programma<numero_progressivo
da 1908 a 2007>.tmp

A questo punto quando "PRINT" verrà azionato, cercherà
di creare il file temporaneo programma<numero PID>.tmp, troverà uno
dei symlink creato da noi e sovrascriverà /etc/passwd !!!

Considerazioni molto simili possono essere condotte nei confronti
di quei programmi che utlizzano per la scelta del nome del file temporaneo
il tempo UNIX, cioè il numero di secondi trascorso dal 01/01/1970.

Il rimedio

Cosa può fare quindi il programmatore per evitare tutto ciò? Il rimedio
più semplice è utilizzare per la creazione dei file temporanei una
directory scrivibile solo da root, ma questo non è risolutivo, non
sempre è possibile e non rispetta gli standard.

Per cui è meglio puntare ad altri metodi. La linea di azione più solida
è fare in modo che i file temporanei non abbiano nomi predicibili
e che il programma effettui un controllo preventivo sull'esistenza
del nome scelto.

Il metodo migliore per scegliere un nome non predicibile consiste
nell'usare le funzioni e le librerie del proprio linguaggio di programmazione
in grado di generare numeri, o meglio stringhe random, ad esempio
in Perl si può utilizzare la classica sequenza di funzioni srand()
e rand(), oppure ancora meglio il comodo modulo String::Random
reperibile su http://search.cpan.org :

use String::Random;

$obj = new String::Random;

$nome = $obj->randregex('[a-zA-Z0-9_]{12}');

open(TEMP, ">/tmp/programma$nome.tmp");

in questo caso avremo il nome del file temporaneo in cui è inserita
una stringa casuale di 12 caratteri alfanumerici.

La seconda accortezza possibile è, dopo avere generato il nome del
file temporaneo, verificare se ne esiste già uno con lo stesso nome.
In caso positivo si può decidere di generare un nuovo nome o di cancellare
quello esistente (in quanto file sospetto) e di crearlo ex novo.

Conclusioni

Per concludere si può dire che qualunque programmatore responsabile
dovrebbe cercare di implementare queste sicurezze minime per evitare
che un attaccante possa utilizzare un attacco tanto pericoloso, quanto
semplice da evitare.

Ti consigliamo anche