
Desencadenando y analizando la vulnerabilidad del kernel de Android CVE-2019-2215
En noviembre de 2017, el sistema syzkaller detectó un error use-after-free en el kernel de Linux. En febrero de 2018, esto fue parcheado en algunos kernels de Linux y versiones de Android.
Esta corrección nunca se incluyó en los boletines mensuales de seguridad de Android, por lo que no fue parcheada en muchos dispositivos recién lanzados, como Pixel y Pixel2.
En septiembre de 2019, Project Zero informó a Android sobre las implicaciones de seguridad de este error. Luego, Android asignó CVE-2019-2215 a esta vulnerabilidad para hacerla más formal y conocida.
CVE-2019-2215 es un use-after-free en binder.c que permite la escalada de privilegios (obtener acceso root) desde una aplicación de Android. No es necesario la interacción del usuario para explotar esta vulnerabilidad. Solo requiere la instalación de una aplicación local maliciosa.
Aquí vamos a presentar esta vulnerabilidad del kernel de Android con más detalle y usaremos esta vulnerabilidad para obtener acceso root (escalada de privilegios) de todo el dispositivo Android.
Usaremos esta Prueba de Concepto (PoC):
https://github.com/cloudfuzz/android-kernel-exploitation
Primero te mostraremos una forma de desencadenar esta vulnerabilidad en un emulador de Android y provocar un fallo del kernel. Luego, para ver lo peligroso que sería, continuamos usando la PoC para obtener acceso root en el dispositivo Android simulado. Luego analizaremos el código del kernel para ver cuál es la razón (análisis estático y análisis dinámico). Después del análisis, veremos cómo obtuvimos el acceso root. Por último, veremos cómo se mitiga esta vulnerabilidad mediante parches.
Para provocar un fallo del kernel desencadenando esta vulnerabilidad, seguimos estos pasos:
Mira el siguiente video:
[video]
En esta (y la siguiente) sección entenderemos por qué ocurre el fallo mediante análisis estático y dinámico.
Aquí analizaremos el código del kernel (análisis estático) para entender el problema. En crash_report.txt está el informe de KASan que indica que este es un error de use-after-free. Significa que se asignó un objeto en el montón (y tenemos una referencia a él), luego liberamos el objeto del montón y luego lo invocamos erróneamente mediante una referencia. En este informe se imprime la traza de pila de estas tres etapas.
Si recuerdas de lo anterior, usamos 'trigger.cpp' en la PoC.
Aquí está el código principal de 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); }
Veamos qué está haciendo 'trigger.cpp'.
En Android (como en otros sistemas operativos basados en Unix) tenemos algunos procesos. Cada programa que ejecutamos crea uno (o más) procesos y estos procesos son manejados por el SO. El SO puede cambiar entre ellos (multitarea) o terminar un proceso, etc. Por razones de seguridad, los procesos están aislados entre sí por defecto.
En algunos casos, un proceso puede necesitar intercambiar datos con otro proceso. Esto se llama Comunicación entre Procesos (IPC). Hay varias formas en que los procesos pueden comunicarse en linux. Android introdujo un mecanismo IPC específico llamado **'Binder'**. Binder es un controlador del kernel para facilitar la comunicación entre procesos.
En Android, la IPC se puede realizar llamando directamente a algunos métodos del kernel (la mayoría de ellos están en drivers/binder.c) o usando implementaciones de alto nivel (por ejemplo en java).

Para usar **binder**, debemos abrir el módulo binder del kernel. Esto se hace usando la línea 3 de trigger.cpp. Luego tenemos un puntero de descriptor de archivo. Usando este fd, se pueden identificar el iniciador del kernel y los destinatarios de la IPC.
Todas las interacciones con el controlador se realizarán a través de un pequeño conjunto de comandos **'ioctl'** (BINDER_THREAD_EXIT, BINDER_WRITE_READ, ...).
Más sobre 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)
En linux, tenemos un concepto llamado '**event polling**'. La API '**epoll**' se usa cuando queremos monitorear múltiples descriptores de archivo (los descriptores de archivo son lo que tenemos al abrir un controlador o trabajar con E/S, etc.).
**epoll** es una estructura del kernel que tiene dos campos importantes.
* lista de interés = lista de descriptores de archivo que queremos monitorear.
* lista lista = lista de descriptores de archivo que están listos para E/S.
Para usar event polling, primero creamos un epoll (línea 4), luego agregamos o eliminamos (EPOLL_CTL_ADD) un evento (&event) asociado con un descriptor de archivo (**fd**) a nuestro epoll creado (**epfd**) llamando al método **epoll_ctl** del kernel.
=> `epoll_event event` es un evento que se activa cuando el archivo asociado (fd) está disponible para una operación de lectura.
Ahora entendemos qué está haciendo **trigger.cpp** (¡no es necesario profundizar!). Abre el módulo **binder**, crea un **epoll** para escucharlo cuando esté listo. Luego, en la línea 6, salimos del binder que iniciamos en la línea 3.
#### Asignar:
Al llamar a open(), en realidad llamamos a **open_binder()** (implementación de open() en binder.c). En open_binder(), se creará una nueva estructura **'binder_proc'** y:
` fd->private_data = binder_proc`
Al llamar a **epoll_create()**, se creará una nueva estructura epoll y se agregará a una estructura de cola.

Al llamar a **epoll_ctrl(epdf, ADD, fd, event)**, se crea un nuevo **ep_item**, se asocia **fd** (descriptor de archivo a escuchar) con este **ep_item** y se inserta en el **árbol rojo-negro** de event_poll (una estructura de datos en ep para guardar ep_items). También llama a **ep_item_poll()**, este método maneja la asociación de la función callback con ep_item.