• Non ci sono risultati.

3.4 Protezione da heap overflow

3.4.2 Implementazione delle syscall di salvataggio e controllo

progettuali ed implementative prese durante l’implementazione delle due syscall, partendo da sys secure store.

L’obiettivo della syscall sys secure store `e quello di consentire il salva- taggio di un valore del quale si vuole conservare l’integrit`a. Per fare questo l’idea `e quella di salvarla in un’area di memoria accessibile solo dal kernel di modo che sia protetta da eventuali scritture arbitrarie in memoria da par- te di utenti non privilegiati che ne vanificherebbero l’utilit`a. Questi valori sono legati ad un flusso di esecuzione, quindi `e possibile che ogni thread in

esecuzione possa richiedere il salvataggio di pi`u valori. Per gestire questa particolare situazione si `e scelta la soluzione pi`u semplice, ovvero memo- rizzare questi valori all’interno della struttura task struct che gestisce il record di processo e tutte le informazioni ad esso associate. Per fare questo si `e andati a modificare il file sched.h sito in include/linux/sched.h, ag- giungendo alla struttura dati il campo secure value dove salvare il valore ricevuto come argomento della system call.

A questo punto all’interno del codice della syscall potremo accedere a tale campo sfruttando il puntatore al record di processo current. Tale valore `e definito per ogni cpu e viene aggiornato dallo scheduler del kernel, il quale fa in modo che esso punti all’istanza della struttura task struct relativa al processo in esecuzione. Dunque l’espressione current->secure value ci permette di accedere al valore da noi aggiunto alla struttura, in questo caso per salvare il valore ricevuto.

Passando all’implementazione della funzione sys secure check pi`u scel- te sono possibili, dal semplice controllo all’integrazione di funzionalit`a di notifica fino alla possibilit`a di terminare il processo. In questa implemen- tazione propedeutica si `e deciso di integrare il controllo sull’integrit`a del valore ed una notifica attraverso le funzionalit`a di logging del kernel nel caso il controllo non abbia successo.

Terminiamo la presentazione del lavoro mostrando un programma di esempio che invochi le due syscall, cos`ı da illustrare come pu`o essere eseguita la chiamata alle syscall da noi definite ed un estratto di codice assembly x86-32 che ci permetta di eseguire la stessa operazione e che potrebbe essere parte del codice che dovr`a essere aggiunto dal compilatore per sfruttare sys secure store e sys secure check.

Listing 3.5: Esempio di chiamata alle syscall da un programma C

1 #include<stdio.h>

2 #include<linux/unistd.h> 3 #define __NR_store 346 4 #define __NR_check 347

5 int main( int argc, char **argv ) {

6 syscall(__NR_store, 42);

7 syscall(__NR_check, 7337);

8 return 0;

9 }

Come possiamo notare nel listato in figura 3.5 dobbiamo invocare la funzione syscall per poter eseguire una system call di cui conosciamo solo il numero. Dunque come possiamo vedere per passare dei parametri alla syscall invocata ci baster`a passarli semplicemente alla funzione syscall stessa.

Listing 3.6: Esempio di codice assembly per invocare le due syscall

1 movl $42, %ebx 2 movl $346, %eax 3 call *%gs:0x10 4 5 movl $7337, %ebx 6 movl $347, %eax 7 call *%gs:0x10

Dalla figura 3.6 possiamo notare che per invocare una system call `e necessario inserire nei registri corretti gli eventuali parametri della chiamata, seguendo le convenzioni di chiamata dell’architettura usata (in questo caso IA-32). Nel nostro caso questo si traduce nell’assegnare il valore dell’argomento da passare al registro %ebx, inserire il numero della syscall da invocare nel regi- stro %eax e poi eseguire un’istruzione call. Questa istruzione ci permette di invocare una system call tramite il meccanismo di chiamata che fa uso delle istruzioni sysenter e sysexit introdotte nell’istruction set IA-32 a partire dai processori Pentium II [8]. Per capire come l’istruzione call *%gs:0x10 possa funzionare ci `e necessario spiegare come viene effettivamente eseguita la chiamata ad una system call da parte di un processo in userspace.

Prendendo come esempio il programma C in figura 3.5 vediamo come come abbiamo gi`a spiegato fa uso della funzione syscall per eseguire una system call. La libreria C standard che offre questa funzione in realt`a al suo interno andr`a ad usare una funzione offerta dal kernel, kernel vsyscall tramite la quale chieder`a l’esecuzione della system call. L’indirizzo in me- moria di questa funzione non `e fisso, ma viene fornito ai processi userspace tramite il parametro AT SYSINFO del formato binario ELF. Al momento in cui il loader va a caricare in memoria la libreria C, esso andr`a anche a copiare il valore del parametro AT SYSINFO all’interno delle informazioni specifiche relative ad un thread che sono contenute nel Thread Control Block. Il registro di segmento %gs fa riferimento a questa struttura dati, quindi l’i- struzione di call non fa altro che chiamare la funzione il cui indirizzo si trova ad offset 0x10 rispetto all’inizio del segmento puntato dal registro %gs. Questo offset `e stato in questo caso recuperato manualmente, ma `e un processo che verr`a automatizzato ed integrato nella modifica al compilatore. L’obiettivo finale di questa soluzione `e dunque quello di fornire un’infra- struttura che permetta al compilatore modificato di generare eseguibili che, nel caso in cui il programma vada ad utilizzare dei puntatori a funzione, comprendano del codice aggiuntivo che vada a salvare il valore dei puntatori in modo che sia possibile verificarne l’integrit`a al momento del loro uso ed impedire cos`ı lo sfruttamento di heap overflow.

Risultati sperimentali

Obiettivo di questo capitolo `e quello di dimostrare come libDefender raggiunga gli obiettivi di funzionalit`a e di prestazioni che ci siamo prefissati in sede progettuale.

4.1

Funzionalit`a

Il modo pi`u immediato di fare questo `e mettere alla prova libDefender usando una serie di programmi vulnerabili che abbraccino le classi di vulne- rabilit`a di cui vogliamo impedire lo sfruttamento. L’insieme dei programmi di prova usato per i test `e costituito da dieci programmi vulnerabili che implementano diverse tecniche di attacco. Gli attacchi implementati sfrut- tano vulnerabilit`a di tipo buffer overflow sullo stack o format string e sono stati sviluppati per architettura IA-32, architettura a cui faremo dunque riferimento nell’illustrarne il funzionamento.

Documenti correlati