
Attivazione e analisi della vulnerabilità del kernel Android CVE-2019-2215
Nel novembre 2017 è stato rilevato un bug use-after-free nel kernel linux dal sistema syzkaller. A febbraio 2018 è stato corretto in alcuni kernel linux e versioni android.
Questa correzione non è mai stata inclusa nei bollettini di sicurezza mensili di Android, quindi non è stata applicata a molti dispositivi appena rilasciati, come Pixel e Pixel2.
A settembre 2019, Android è stato informato delle implicazioni di sicurezza di questo bug da Project Zero. Successivamente, Android ha assegnato CVE-2019-2215 a questa vulnerabilità per renderla più formale e conosciuta.
CVE-2019-2215 è un use-after-free in binder.c che consente l'elevazione dei privilegi (ottenere l'accesso root) da un'applicazione android. Non è necessaria alcuna interazione da parte dell'utente per sfruttare questa vulnerabilità. Richiede solo l'installazione di un'applicazione locale dannosa.
Qui introdurremo questa vulnerabilità del kernel android in maggiori dettagli e useremo questa vulnerabilità per ottenere l'accesso root (escalation dei privilegi) dell'intero dispositivo android.
Useremo questo Proof of Concept (PoC):
https://github.com/cloudfuzz/android-kernel-exploitation
Per prima cosa ti mostreremo un modo per innescare questa vulnerabilità su un emulatore android e causare un crash del kernel. Poi, per vedere quanto potrebbe essere pericolosa, continueremo usando il PoC per ottenere l'accesso root nel dispositivo android simulato. Quindi analizzeremo il codice del kernel per capire qual è la causa (analisi statica e analisi dinamica).
Dopo l'analisi, vedremo come abbiamo ottenuto l'accesso root. Infine vedremo come questa vulnerabilità viene mitigata tramite patch.
Per causare un crash del kernel innescando questa vulnerabilità, usiamo questi passaggi:
Guarda il seguente video:
[video]
In questa (e nella prossima) sezione capiremo perché il crash avviene usando l'analisi statica e dinamica.
Qui analizzeremo il codice del kernel (analisi statica) per capire il problema. In crash_report.txt c'è il report di KASan che dice che questo è un bug use-after-free. Significa che un oggetto viene allocato nell'heap (e ne abbiamo un riferimento), poi liberiamo l'oggetto dall'heap e infine lo richiamiamo erroneamente tramite un riferimento. In questo report viene stampata la stack trace di queste tre fasi.
Se ricordi da prima, abbiamo usato 'trigger.cpp' nel PoC.
Ecco il codice principale di trigger.cpp:``` int main() { //1 int fd, epfd; //2 struct epoll_event event = {.events = EPOLLIN}; //3 fd = open("/dev/binder", O_RDONLY); //4 epfd = epoll_create(1000); //5 epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event); //6 ioctl(fd, BINDER_THREAD_EXIT, NULL); }
Vediamo cosa fa 'trigger.cpp'.
In Android (come in altri sistemi operativi basati su Unix) abbiamo alcuni processi. Ogni programma che eseguiamo crea uno (o più) processi e questi processi sono gestiti dal sistema operativo. Il SO può passare dall'uno all'altro (multi-tasking) o terminare un processo, ecc. Per ragioni di sicurezza, i processi sono isolati gli uni dagli altri per impostazione predefinita.
In alcuni casi, un processo potrebbe aver bisogno di scambiare dati con un altro processo. Viene chiamata comunicazione tra processi (IPC). Ci sono diversi modi per i processi di comunicare in linux. Android ha introdotto uno specifico meccanismo IPC chiamato **'Binder'**. Binder è un driver del kernel che facilita la comunicazione tra processi.
In android, l'IPC può essere effettuato chiamando direttamente alcuni metodi del kernel (la maggior parte si trova in drivers/binder.c) o usando implementazioni ad alto livello (ad esempio in java).

Per usare **binder**, dobbiamo aprire il modulo binder del kernel. Questo viene fatto usando la riga 3 di trigger.cpp. Poi abbiamo un puntatore a file descriptor; usando questo fd, l'iniziatore e i destinatari dell'IPC possono essere identificati dal kernel.
Tutte le interazioni con il driver avverranno tramite un piccolo insieme di comandi **'ioctl**' (BINDER_THREAD_EXIT, BINDER_WRITE_READ, ...).
Maggiori informazioni su binder: [link1](https://www.nds.ruhr-uni-bochum.de/media/attachments/files/2012/03/binder.pdf), [link2](http://rts.lab.asu.edu/web_438/project_final/CSE_598_Android_Architecture_Binder.pdf)
In linux, abbiamo un concetto chiamato '**event polling**'. L'API '**epoll**' viene usata quando vogliamo monitorare più file descriptor (i file descriptor sono ciò che otteniamo quando apriamo un driver o lavoriamo con IO, ecc).
**epoll** è una struct del kernel che ha due campi importanti.
* interest list = elenco dei file descriptor che vogliamo monitorare.
* ready list = elenco dei file descriptor pronti per I/O.
Per usare l'event polling, prima creiamo una epoll (riga 4), poi aggiungiamo o eliminiamo (EPOLL_CTL_ADD) un evento (&event) associato a un file descriptor (**fd**) alla nostra epoll creata (**epfd**) chiamando il metodo **epoll_ctl** del kernel.
=> `epoll_event event` è un evento, attivato quando il file associato (fd) è disponibile per un'operazione di lettura.
Ora capiamo cosa fa **trigger.cpp** (non c'è bisogno di andare in profondità!). Apre il modulo **binder**, crea una **epoll** per ascoltarlo quando è pronto. Poi alla riga 6, usciamo dal binder che abbiamo avviato alla riga 3.
#### Allocazione:
Chiamando open(), chiamiamo in realtà **open_binder()** (implementazione di open() in binder.c); in open_binder(), verrà creata una nuova struct **'binder_proc'** e:
` fd->pricate_data = binder_proc`
Chiamando **epoll_create(),** verrà creata una nuova struct epoll e aggiunta a una struttura a coda.

Chiamando **epoll_ctrl(epdf, ADD, fd, event)**, viene creato un nuovo **ep_item**, associa **fd **(file descriptor da ascoltare) a questo **ep_item**, e viene inserito nell'**albero rosso-nero** di event_poll ( una struttura dati in ep per salvare gli ep_item ). Chiama anche **ep_item_poll(),** questo metodo gestisce l'associazione della funzione di callback a ep_item.