Ci siamo lasciati l’ultima volta con le librerie dinamiche in macOS. Cambiamo argomento: i processi vengono avviati nello spazio degli indirizzi virtuali: storicamente, l’avvio dei processi veniva eseguito in modo deterministico. Le cose sono (fortunatamente) un pò cambiate. Scopriamo perchè!
Preambolo
Nel vecchio approccio di gestione dei sistemi operativi, quando un processo veniva avviato possedeva un immagine in memoria virtuale. Questa immagine, per un certo programma è esattamente identica alla stessa immagine, dello stesso programma, su un altro calcolatore.
Dov’è il problema?
Il problema è dato dal fatto che, anche durante la vita del processo stesso, la maggior parte delle allocazioni erano eseguite nello stesso modo, che inevitabilmente ha portato ad avere indirizzi molto prevedibili all’interno della memoria.
Essere in possesso di indirizzi prevedibili, ha fornito un vantaggio enorme per gli hacker. Uno dei principali modi per eseguire un attacco utilizzato dagli hacker è il cosidetto code injection: in buona sostanza, sovrascrivendo il puntatore di funzione in memoria, possono in un certo senso modificare l’esecuzione del programma, facendolo puntare a del codice arbitrario, appositamente scritto per eseguire azioni malevoli (termine relativo, malevoli nei confronti dell’integrità del sistema operativo, non per forza nel senso stretto del termine).
Più comunemente, il metodo utilizzato per eseguire queste modifiche è cercare di causare un buffer overflow, che prevede che se in un buffer di una data dimensione vengono scritti dati di dimensioni maggiori, viene sovrascritta parte della zona di memoria immediatamente adiacente al buffer in questione.
E’ evidente che la bravura degli hacker che eseguono questa tecnica è cercare di determinare cosa sovrascrivere e sopratutto, in quale zona della memoria risiederà il codice che tentano di iniettatare, munendosi del buffer overflow; da qui il nome code injection.
Cos’è l’ASLR?
Da questa breve introduzione capirete il motivo per il quale non era conveniente eseguire processi in modo deterministico, attraverso delle locazioni conosciute da tutti; da qui nasce l’Address Space Layout Randomization (ASLR), che è una tecnica che viene impiegata nella maggior parte dei sistemi operativi, essendo una protezione significativa contro l’hacking dei sistemi.
Ogni volta che un processo va in esecuzione, lo spazio degli indirizzi viene “mescolato leggermente” in modo da proteggere gli indirizzi delle funzioni più importanti nelle aree di memoria: in questo modo l’attaccante è costretto a cercare gli indirizzi del codice e dei dati che gli servono prima di poterli utilizzare.
Vi chiederete: se gli indirizzi di memoria sono camuffati, com’è possibile per un hacker riuscire a superare questo vincolo?
Se durante l’esecuzione di codice arbitrario l’attaccante accede a una struttura dati con un indirizzo sbagliato si ottengono dati errati; se invece viene chiamata una funzione con un indirizzo errato si provoca una eccezione, che causa un crash. Il trucco è tenere traccia dell’eccezione generata da un crash, sfruttandola a proprio vantaggio: attraverso la memorizzazione del tentativo fallito, si possono raccogliere sempre maggiori informazioni, fino a conoscere con precisione (prima o poi) l’indirizzo di memoria delle risorse necessarie per l’esecuzione di codice arbitrario.
macOS Leopard è stata la prima versione di macOS a introdurre l’Address Space Layout Randomization, sebbene in una forma inizialmente molto primitiva, randomizzando esclusivamente il caricamento delle librerie: Snow Leopard ha apportato si alcuni miglioramenti, ma l’heap e lo stack erano entrambi ancora prevedibili e lo spazio degli indirizzi assegnato era persistente ad ogni riavvio del sistema. macOS Lion è la prima versione di macOS a supportare la questa tecnica in maniera completa, fornendo una randomizzazione fino 16 bit nei segmenti di testo.
Mountain Lion ha migliorato ulteriormente la tecnica, introducendo l’ASLR direttamente in kernel space: viene offerta una nuova chiamata di sistema, kas_info, per ottenere informazioni sullo spazio degli indirizzo del kernel (introdotto successiva anche su iOS con XNU) offrendo la randomizzazione completa in kernel space nel tentativo di contrastare i jailbreaker prevenendo il code injection all’interno del kernel di iOS.
L’ASLR aiuta sicuramente, ma non è una panacea. L’unica vera protezione per mantenere l’integrità del sistema è scrivere del codice più “sicuro” e sottoporlo a revisioni rigorose, sia automatizzate che manuali.
Anche per questo articolo è tutto. Per eventuali domande, curiosità o feedback potete lasciare un commento qui in basso, a presto!