PI Debug/ Da eCryptfs a terminale root

PI Debug/ Da eCryptfs a terminale root

Analisi del bug presente nella gestione del file system crittografico eCryptfs da parte del kernel Linux che permetteva a un pirata di aggirare il meccanismo di protezione e, nelle giuste condizioni, eseguire l'accesso come root
Analisi del bug presente nella gestione del file system crittografico eCryptfs da parte del kernel Linux che permetteva a un pirata di aggirare il meccanismo di protezione e, nelle giuste condizioni, eseguire l'accesso come root

Una delle cose peggiori che possano capitare in un sistema GNU/Linux è che una persona non autorizzata ottenga accesso a un terminale di root. Per fortuna, i sistemi GNU/Linux sono progettati per rendere questa eventualità rara e molto difficile; ogni tanto, però, si scopre un bug che in condizioni particolari potrebbe condurre a questo pericoloso risultato. ” Dirty Cow “, che sta facendo tanto discutere in questi giorni , non è quindi un caso isolato. Il fatto è che, nonostante gli incessanti sforzi di sviluppatori e community non esiste (e probabilmente non esisterà mai) un software perfetto. Basta una piccola imperfezione nel codice (un bug nel kernel, in un servizio di sistema, in un programma di terze parti o altro) per mettere a repentaglio la sicurezza dell’intero sistema, anche se GNU/Linux. A dimostrazione di ciò analizzeremo un bug, ormai risolto (lungi da noi l’idea di fornire strumenti che possano ancora arrecare danni), che ha coinvolto eCryptfs.

Alcuni mese fa, il team di Project Zero ha scoperto una vulnerabilità nel modo in cui viene implementato il file system eCryptfs, utilizzato su diverse distribuzioni per consentire agli utenti di cifrare la propria cartella personale. Nonostante gli sviluppatori abbiano previsto un sistema di controllo per impedire che le funzioni di questo file system possano essere utilizzate per mandare in overflow il kernel, non avevano comunque considerato che una particolare caratteristica di Ecyptfs. Uno degli esperti di sicurezza del Project Zero ha capito che era possibile aggirare il meccanismo di controllo ed ottenere comunque un overflow, con il quale dirottare il processore verso un codice malevolo capace di dare a un pirata un terminale con privilegi di root.

Una serie di livelli annidati
eCryptfs è un file system crittografico “stackable”: significa che i file system sono annidati come in un sistema di scatole cinesi. Ogni file system si trova su un livello differente e il file system crittografico viene registrato a monte di quello principale del sistema operativo. La gestione dei vari livelli impegna il kernel Linux, che rischierebbe di andare in overflow se qualche programma malevolo creasse troppi livelli (si parla di deep nesting ). Viene dunque implementato in ogni file system un meccanismo di controllo per assicurarsi che non possano essere creati troppi livelli ovvero, nella metafora, troppe scatole cinesi.

In eCryptfs è stato inserito un semplice ciclo if

s->s_stack_depth = path.dentry->d_sb->s_stack_depth + 1;

rc = -EINVAL;
if (s->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
pr_err("eCryptfs: maximum fs stacking depth exceeded\n");
goto out_free;
}

che lancia un errore nel caso in cui sia stato raggiunto il numero massimo di livelli (la profondità, o depth ) nello stack dei file system, impedendo correttamente l’overflow del kernel. Però, c’è un altro problema e per capirlo dobbiamo fare qualche passo indietro.

L’accesso ai file del disco da parte del sistema operativo non avviene in modo diretto: in realtà il sistema accede alla RAM. Esistono due tipi di memorie, quella fisica e quella virtuale. La memoria fisica è rappresentata dalla vera e propria RAM disponibile (ad esempio 8 GB); la memoria virtuale , invece, è una rappresentazione di quella che dovrebbe essere idealmente la memoria, ma che per ovvie ragioni di spazio non può essere. Per capirci meglio, quando accediamo a una partizione, il sistema operativo esegue una funzione chiamata mmap , per mappare sulla memoria virtuale il contenuto dell’intera partizione. Non vengono copiati i file, soltanto i riferimenti per poterli trovare sul disco: una mappa. Ovviamente, in questo modo è possibile rappresentare una memoria virtuale totale di parecchi TB avendo a disposizione una memoria RAM fisica di pochi GB. Il problema è che non è comunque possibile accedere contemporaneamente all’intero file system, perché la memoria fisica disponibile sarà, probabilmente, sempre inferiore alle dimensioni del file system.

Quando un processo cerca di accedere a un file, il sistema lo va a cercare nella memoria virtuale. A quel punto potrebbe accorgersi che tale file non è ancora stato caricato nella memoria fisica. Viene dunque lanciata un’eccezione chiamata page fault , e il sistema operativo cerca di risolverla recuperando il file dal file system (ovvero dal disco rigido) e copiandolo nella RAM fisica. Per gestire le varie pagine di memoria sono disponibili degli helper , ovvero delle funzioni di sistema che aiutano i file system a funzionare correttamente. La maggior parte dei file system funziona allo stesso modo, ma eCryptfs no: è un file system crittografico, quindi i file subiscono operazioni intermedie di cifratura e decifrazione non presenti in un normale ext4. eCryptfs ha dunque una cache per le pagine di memoria tutta sua, sulla quale eseguire le operazioni crittografiche. Tuttavia, le operazioni di mappatura, scrittura e lettura vengono eseguite utilizzando gli stessi helper degli altri file system.

Ora dobbiamo ricordare un piccolo particolare: abbiamo detto che il file system eCryptfs si trova a monte del file system principale del sistema operativo. Ciò significa che chi lavora solo sul file system principale non può accedere al file system eCryptfs, ma il contrario può accadere. E, visto che gli helper per le operazioni di base di Ecrpytfs sono gli stessi del file system principale, è possibile sfruttare le funzioni di eCryptfs per mappare dei file che normalmente non sarebbero mappabili, come quelli presenti nella directory /proc del file system principale. Questa cartella contiene i riferimenti di ogni processo attivo sul sistema operativo: potendovi accedere si può indurre ogni processo a chiedere la lettura delle variabili di ambiente di un altro processo, il che richiede ovviamente la loro mappatura. Si ottiene dunque una serie ricorsiva di mappature, aggirando il semplice blocco che era stato implementato in eCryptfs tramite il ciclo if che abbiamo visto. Queste mappature ricorsive finiscono ben presto per causare un overflow nello stack del kernel Linux, e un pirata può sfruttare l’overflow per eseguire del codice arbitrario, tramite il quale ottenere un terminale di root. Su un sistema come Ubuntu, che offre la possibilità di utilizzare eCryptfs a tutti gli utenti (con l’opzione “Cifra la tua cartella home”), anche un utente non amministratore potrebbe sfruttare il bug ed ottenere un terminale con privilegi di root.


L’opzione, durante l’installazione di Ubuntu, per cifrare la cartella home

L’exploit
Il codice dell’exploit basato su questo bug è molto lungo; di seguito ne analizziamo le parti più importanti.

int spammer_count = 300000 / pipes_per_process;
pid_t spammers[spammer_count];

Viene costruito un insieme di spammers , ovvero processi fork del programma malevolo che si occuperanno di eseguire la serie di mappature necessario a provocare un overflow. Il numero deve essere alto, ma comunque inferiore alla metà della profondità massima nello stack dei file system per evitare un blocco della procedura a causa del ciclo if che abbiamo visto all’inizio della spiegazione.

pid_t recurser_parent = fork();
if (recurser_parent == -1)
err(1, "fork");
if (recurser_parent == 0) {
raise(SIGSTOP);
usleep(100);
int child = clone(recurser_main, recurser_stack + sizeof(recurser_stack), CLONE_FILES | CLONE_FS | CLONE_IO | CLONE_SIGHAND | CLONE_SYSVSEM | CLONE_THREAD | CLONE_VM, NULL);
if (child == -1)
err(1, "clone");
syscall(__NR_exit, 0);
}

Il programma malevolo crea un proprio fork, ovvero un processo dipendente da se stesso. Questo fork sarà il genitore dei vari spammer. Al fork vengono assegnate alcune routine fondamentali clonandole dal programma malevolo. Gli spammer verranno poi continuamente creati tramite un semplice ciclo for. In poche parole, il processo genitore crea un nuovo processo, un figlio.

for (int i=0; i<spammer_count; i++) {
kill(spammers[i], SIGCONT);
if (waitpid(spammers[i], &status, WUNTRACED) != spammers[i])
err(1, "waitpid spammer");
}

Per essere sicuri che tutti i processi continuino a funzionare, viene inviato loro il segnale SIGCONT, che per l’appunto forza la ripresa dell’esecuzione di un processo anche se questo era stato bloccato (per esempio bloccato in attesa della risposta del processo figlio).

pid_t dumper_child = fork();
if (dumper_child == -1)
err(1, "fork dumper_child");
if (dumper_child == 0) {
puts("going to crash now");
raise(SIGSEGV);
}

Viene poi costruito un dumper per leggere il core dump dell’overflow, tramite il quale un classico suidhelper potrà dirottare l’esecuzione del programma per ottenere un terminale con privilegi di root. Il programma viene dunque mandato volontariamente in crash lanciando al kernel un errore di segmentazione.

La soluzione
Il bug è già stato risolto, applicando al kernel Linux la patch proposta dallo stesso scopritore del bug: grazie alla modifica, ora non è più possibile utilizzare gli strumenti di eCryptfs per gestire le pagine di memoria del file system principale. Ubuntu, e gli altri sistemi che offrivano la possibilità di cifrare le cartelle home degli utenti con eCryptfs hanno immediatamente inserito l’aggiornamento nei loro repository.


La patch è stata applicata al kernel Linux

AVVERTENZE
Le informazioni contenute in questo articolo sono state pubblicate a scopo puramente didattico , per consentire ai lettori di conoscere e imparare a difendersi dai pericoli a cui sono esposti navigando in Internet o in generale utilizzando applicazioni affette da vulnerabilità.
L’editore e la redazione non si assumono responsabilità alcuna circa l’utilizzo improprio di tali informazioni , che possa avere lo scopo di violare la legge o di arrecare danni a terzi. Per cui, eventuali sanzioni economiche e penali saranno esclusivamente a carico dei trasgressori.

Link copiato negli appunti

Ti potrebbe interessare

Pubblicato il
27 ott 2016
Link copiato negli appunti