Questa volta in italiano.
Ogni tanto vedo notizie strettamente collegate ad argomenti di cui ho parlato a lezione pochissimi giorni prima (esempio).
Ieri sera ne ho visto una molto interessante, legata proprio alla lezione di Cybersecurity di ieri mattina.
La lezione era la prima sull'argomento "Memory corruption". Tra le varie cose, ho detto che:
- Non tutti gli errori ("bug") sono vulnerabilità; non tutte le vulnerabilità sono sfruttabili (exploitable).
- L'impatto di una vulnerabilità dipende non tanto dall'errore ma dal programma in cui è stato commesso l'errore. Tale dipendenza è estremamente complessa e pressoché impossibile da prevedere in generale. Uno stesso errore in programmi diversi può non avere alcun impatto di sicurezza, avere impatto marginale, avere impatto potenzialmente catastrofico.
- Le stesse considerazioni valgono per la possibilità di sfruttare una vulnerabilità conseguente ad una particolare tipologia di errore: può essere pressoché impossibile, molto complicato, complicato, banale. Dipende dal programma in cui è stato commesso l'errore.
- Da moltissimi anni, una tipologia di errore relativamente comune è il buffer overflow: scrittura di dati "subito dopo la fine o subito dopo l'inizio" di un array. Questa tipologia di errore è tipica dei programmi scritti in C o in C++ (linguaggi cosiddetti memory unsafe) e non può accadere, ad esempio, in Java, Python o Rust (linguaggi memory safe).
- Gli errori dovuti ai buffer overflow molto spesso corrispondono a vulnerabilità. Molti anni fa, tali vulnerabilità erano molto spesso sfruttabili facilmente ed avevano impatto catastrofico. Oggi le vulnerabilità buffer overflow sono molto più difficili da sfruttare, per motivi impossibili da sintetizzare qui (ne parlo alla fine delle lezioni sulla "Memory corruption"). Continuano ad esserci, ma sono molto difficili da sfruttare.
- Tra le possibili tipologie di impatto di una vulnerabilità dovuta ad un buffer overflow c'è il code injection: l'attaccante modifica il contenuto della memoria di un processo in esecuzione "iniettandoci codice", cioè inserendovi una sequenza di byte che rappresenta una sequenza di istruzioni CPU; e, il programma eseguito dal processo ad un certo punto esegue proprio quella sequenza di istruzioni (chiamata in gergo "shellcode").
- Nella lezione precedente avevo anche detto che il testing di un software tipicamente evidenzia la presenza di bug. Determinare se il bug rilevato è una vulnerabilità e, soprattutto, se quella vulnerabilità è effettivamente sfruttabile è molto complicato. In estrema sintesi, e semplificando moltissimo, il comportamento malevolo potrebbe non essere conseguenza immediata ed evidente dell'input che ha sollecitato il bug. Potrebbe emergere solo a seguito di ulteriori input, in parti del sistema completamente diverse. Input che sfruttano la "contaminazione" dello stato interno al sistema provocata dal bug in modo da aumentare l'insieme delle variabili interne controllate dall'attaccante.
Bene, ieri è stata resa pubblica una vulnerabilità nei sistemi Linux che riassume, diciamo così, tutti questi punti perfettamente.
- Il kernel ha alcune variabili utilizzate per velocizzare l'accesso ai file su disco. In particolare, per alcuni file, il contenuto di queste variabili è gestito come una copia del contenuto del file. La lettura di un file è cioè la lettura del contenuto di queste variabili ("file cache").
- Una certa system call ha un buffer overflow. Il buffer overflow permette di sovrascrivere proprio parte della file cache. Un attaccante può quindi inserire in parte della file cache dei dati di propria scelta, ad esempio uno shellcode.
- Invocando quella system call, è possibile ottenere questo effetto: una successiva invocazione del comando Linux su (quindi creazione di un processo che esegue codice prelevato dal file di nome su), usa come contenuto di quel file proprio ciò che l'attaccante ha inserito nella file cache.
- L'attaccante può quindi forzare l'esecuzione di uno shellcode. Poiché il file eseguibile su è di proprietà dell'utente root ed ha un certo attributo che dice "chi esegue questo file ha gli stessi diritti di root", la conseguenza è fantastica o terribile a seconda dei punti di vista: l'attaccante esegue codice di propria scelta con "i diritti di root".
Questo è il writeup completo (mooooolto complesso). E' interessante soprattutto perché è un esempio realistico e presentato in maniera eticamente corretta di uso della cosiddetta "AI". Emerge chiaramente che il loro è un lavoro "AI-assisted" che richiede molto tempo e molte competenze. La cosiddetta "AI" è stata uno strumento che li ha aiutati. Il che è esattamente ciò che accade da sempre nella ricerca di vulnerabilità: molto tempo, molte competenze, molti strumenti. Ed è esattamente il contrario di quello che l'industria con enormi interessi economici sta cercando, con successo, di fare credere (uno e due, ma ho ancora altre cose da scrivere su questo punto).
Questa è una sintesi decisamente non tecnica ma utile per capire l'essenza del problema.
Commenti