- CVE-2023-6241 es un error lógico en la unidad de administración de memoria de la GPU Arm Mali, que permite que una app maliciosa de Android llegue a ejecutar código arbitrario en el kernel y obtener privilegios root incluso en un Pixel 8 con MTE del kernel activado
- Afecta a dispositivos recientes con GPU Arm Mali que usan Command Stream Frontend (CSF), incluidos Google Pixel 7 y Pixel 8
- La vulnerabilidad funciona aprovechando una breve ventana en la que se libera un lock durante la expansión de memoria JIT, creando una inconsistencia entre los mapeos de GPU y el arreglo de backing pages
- El exploit usa un mapeo de GPU restante hacia una backing page liberada, hace que esa página se reutilice como PGD del contexto de GPU y luego mapea memoria del kernel y código del kernel
- MTE detecta discrepancias entre punteros y etiquetas de memoria, pero este flujo de ataque avanza fuera del alcance de protección de MTE porque la GPU accede directamente a direcciones físicas
Alcance de la vulnerabilidad y estado del parche
- CVE-2023-6241 es una vulnerabilidad de la GPU Arm Mali que permite que una app maliciosa de Android obtenga ejecución de código arbitrario en el kernel y privilegios root en el dispositivo
- Fue reportada a Arm el 15 de noviembre de 2023 y corregida en el driver Arm Mali r47p0, publicado el 14 de diciembre de 2023
- La corrección de Android se incluyó en la actualización de seguridad de marzo de 2024
- Afecta a dispositivos recientes con GPU Arm Mali que usan la función CSF (Command Stream Frontend); se citan como ejemplos Google Pixel 7 y Pixel 8
- Se confirmó que el exploit funciona incluso con MTE del kernel activado en Pixel 8
Modelo de defensa de Arm64 MTE
- MTE (Memory Tagging Extension) es una función de hardware de los procesadores Arm modernos que compara las etiquetas de punteros y bloques de memoria para detectar corrupción de memoria
- Aunque los punteros Arm64 son de 64 bits, el espacio de direcciones real de las aplicaciones suele ser de 52 bits o menos, por lo que algunos bits superiores pueden usarse para almacenar etiquetas
- En un desbordamiento lineal, la etiqueta de un bloque de memoria adyacente puede diferir de la etiqueta del puntero; en un use-after-free, puede producirse una discrepancia por el cambio de etiqueta durante la liberación y reasignación
- A diferencia de mitigaciones de etapas posteriores como kCFI, MTE es una mitigación de etapa temprana que intenta detectar la corrupción de memoria en el momento en que ocurre por primera vez
- Como la cantidad de bits de etiqueta es limitada, las colisiones son inevitables; aun usando solo etiquetas de 4 bits, la tasa de éxito aleatoria se reduce a 1/16
- Si se filtran valores de punteros y bloques de memoria mediante ataques de canal lateral como Spectre, se puede eludir MTE acertando la etiqueta correcta, pero este tipo de filtraciones suelen estar al alcance de atacantes locales
- Actualmente solo Google Pixel 8 permite activar MTE desde las opciones de desarrollador, y MTE viene desactivado por defecto
- Para activar MTE en el kernel se requieren pasos adicionales
Carrera en la memoria JIT de Mali
- Una app de usuario que usa el driver de la GPU Mali abre el archivo del driver y crea e inicializa el objeto de kernel
kbase_contextmediante llamadasioctl kbase_contextadministra varios tipos de memoria compartida entre el dispositivo GPU y la aplicación en espacio de usuario- Una región de memoria de la GPU Mali se representa como
kbase_va_region;nr_pagesindica el tamaño virtual ygpu_alloc->nentsindica la cantidad real de backing pages - La memoria JIT es native memory cuyo ciclo de vida administra el driver del kernel, y la app asigna o libera memoria JIT mediante comandos de GPU
- En las GPU CSF, los comandos de software y de hardware se colocan en colas distintas
- Con
KBASE_IOCTL_KCPU_QUEUE_CREATEse puede crear unakbase_kcpu_command_queue - Con
KBASE_IOCTL_KCPU_QUEUE_ENQUEUEse encolan comandos BASE_KCPU_COMMAND_TYPE_JIT_ALLOCyBASE_KCPU_COMMAND_TYPE_JIT_FREEse usan para asignar y liberar JIT
- Con
kbase_jit_allocatebusca una región reutilizable en el pool de memoria JIT liberada y, si el tamaño físico no alcanza, aumenta las backing pages conkbase_jit_growkbase_jit_growpuede liberar temporalmentekctx->reg_lockykctx->mem_partials_lockdurante la llamada akbase_mem_pool_grow- Como
kctx->reg_lockprotege el acceso concurrente a las regiones de memoria, el tramo en el que se libera el lock se convierte en una ventana de carrera
Flujo de activación de CVE-2023-6241
- Si la GPU accede a una dirección de una región de memoria que no está respaldada por páginas físicas, se produce un fallo de acceso a memoria de GPU
kbase_mmu_page_fault_workercomprueba si la región puede crecer y luego puede asignar y mapear de inmediato las backing pages necesarias- Al crearse, una región JIT cumple las condiciones GROWABLE_FLAGS_REQUIRED, que incluyen
KBASE_REG_PF_GROWyKBASE_REG_GPU_WR - La flag
KBASE_REG_DONT_NEED, que se añade cuando se libera una región JIT, se elimina al inicio dekbase_jit_growenkbase_mem_evictable_unmake - Como resultado, si durante la ventana de carrera de
kbase_mem_pool_growse genera un fallo de página de GPU sobre la misma región JIT, el manejador de fallos puede expandir esa región - Si el manejador de fallos cambia
reg->gpu_alloc->nents, los valoresold_sizeydeltaguardados previamente porkbase_jit_growdejan de coincidir con el estado real - Luego,
kbase_alloc_phy_pages_helper_lockedykbase_mem_grow_gpu_mappingrealizan la asignación de backing pages y el mapeo de GPU usando valores stale, lo que genera una inconsistencia entre el mapeo de GPU y el arreglopages - Esta carrera es fácil de ganar porque
kbase_mem_pool_growincluye una asignación de memoria grande
Cómo cambió el ataque después del parche de GHSL-2023-005
- En la vulnerabilidad anterior GHSL-2023-005, otro hilo podía reducir la región JIT con
KBASE_IOCTL_MEM_COMMIT, invalidandoold_sizeydelta - Después del parche de GHSL-2023-005, ya no es posible cambiar el tamaño de la memoria JIT con
KBASE_IOCTL_MEM_COMMIT ioctl - En CVE-2023-6241, durante la ventana de carrera no se puede reducir la región, solo aumentarla
- Si simplemente se aumenta, algunas de las últimas backing pages no quedan mapeadas en la GPU, pero el mapeo continuo desde el inicio se mantiene, por lo que no causa un problema inmediato
- El exploit crea un nuevo mapeo detrás de un gap sin mapear mediante un fallo adicional de GPU y luego, al liberar el JIT, ajusta el punto de shrink dentro de ese gap para dejar un estado explotable
Suposición vulnerable al desmontar mapeos de GPU
kbase_mmu_teardown_pgd_pagesrecorre las tablas de páginas de GPU y marca entradas como inválidas para eliminar mapeos de direcciones de GPU- Esta función, si una PTE de nivel alto es inválida, asume que todo el rango grande de direcciones cubierto por esa entrada ya está unmapped y lo omite
- Una PTE de nivel 2 cubre un rango de 512 páginas
- En una
kbase_va_regionnormal, las direcciones virtuales mapeadas siempre son continuas desde el inicio de la región y no tienen gaps intermedios, por lo que este comportamiento de omisión es seguro - El exploit de CVE-2023-6241 crea un gap sin mapear entre mapeos y ubica el punto inicial de shrink dentro de ese gap
kbase_mmu_teardown_pgd_pagesencuentra una PTE inválida de nivel 2 y salta 512 páginas, pero algunas direcciones posteriores podrían estar realmente mapeadas- Las direcciones de GPU omitidas por error conservan el acceso a esas páginas físicas incluso después de liberar las backing pages
Proceso que lleva a la ejecución de código en el kernel
- Al liberar la región JIT, se devuelven las backing pages, pero el mapeo de GPU que quedó por error puede seguir accediendo a las páginas liberadas
- Las backing pages liberadas pueden reutilizarse luego como otras páginas del kernel
- Una de las técnicas usadas consiste en hacer que una backing page liberada se reutilice como PGD (page table global directory) del
kbase_contextde GPU - La asignación de backing pages del driver Mali se realiza de forma jerárquica
- Primero toma páginas del
kbase_mem_pooldelkbase_contextactual - Si no alcanza, usa
pool->next_pool - Si aun así no alcanza, asigna páginas directamente mediante el buddy allocator del kernel
- Primero toma páginas del
pool->next_pooles un pool de memoria administrado por el driver Mali y compartido por todos loskbase_context, y también se usa para asignar PGD de contextos de GPU- Si una página liberada se reutiliza como PGD, la GPU puede volver a escribir ese PGD usando la dirección de GPU que quedó activa
- Reescribir el PGD permite mapear memoria arbitraria del kernel y código del kernel en la GPU
- En ese estado, es posible reescribir código del kernel para lograr ejecución de código arbitrario en el kernel, y leer y escribir datos del kernel para modificar credenciales de procesos y desactivar SELinux
- El exploit para Pixel 8 y notas de configuración están publicados en el repositorio de GitHub Security Lab
Por qué se puede eludir MTE
- Este flujo de exploit no requiere una etapa específica para eludir MTE
- MTE detecta desreferenciaciones inválidas comprobando si coincide la etiqueta del bloque de memoria al que apunta un puntero
- Al activar CVE-2023-6241, aparece una inconsistencia entre el arreglo
pagesy el mapeo de GPU, pero si se observa cada uno por separado, no hay ninguna entrada inválida - Cuando
kbase_mmu_teardown_pgd_pagesomite la eliminación del mapeo de GPU, la dirección física de la página de memoria liberada queda en la tabla de páginas de GPU - Cuando la GPU accede a esa página liberada, accede directamente a la dirección física, por lo que no pasa por una comprobación de desreferenciación de punteros
- Tampoco está claro qué efecto tiene MTE sobre los accesos de memoria de la GPU
- En consecuencia, este bug elude la protección de MTE aprovechando una ruta en la que la GPU, como coprocesador, accede directamente a la memoria física
Superficie de ataque que persiste incluso después de MTE
- CVE-2023-6241 demuestra que incluso en un Pixel 8 con MTE del kernel activado, un solo bug puede alcanzar ejecución de código arbitrario en el kernel
- MTE es un avance importante en mitigación de corrupción de memoria y puede volver inexplotables muchas vulnerabilidades de corrupción de memoria, pero no es una defensa universal
- En este caso, la GPU elude MTE accediendo directamente a la memoria física
- A medida que aumentan las mitigaciones de hardware y software del lado de la CPU, los coprocesadores y sus drivers de kernel pueden seguir siendo una superficie de ataque poderosa
1 comentarios
Opiniones de Hacker News
La clave aquí es que la GPU ha sido desde hace tiempo un dolor de cabeza para Android.
La GPU tiene un acceso muy fuerte al AP, por lo que en la práctica puede eludir las mitigaciones implementadas delante. Los bugs en el código de mapeo del driver derivan en primitivas de ataque potentes, y se han explotado repetidamente en exploits reales en circulación. Al final, parece difícil que cambie mucho hasta que se rediseñe la arquitectura.
Lo interesante de esta vulnerabilidad es que es un bug lógico en la unidad de gestión de memoria de la GPU Arm Mali, y que puede eludir Memory Tagging Extension.
Pero el resto del artículo parece explicar que la causa real es una condición de carrera, y que el uso después de liberar memoria es una consecuencia de eso.
¿También habría afectado a instalaciones de GrapheneOS anteriores a la actualización de marzo?
A veces adoptan niveles de parches de seguridad de AOSP antes del lanzamiento, o hacen backport de correcciones de seguridad de AOSP o del kernel que todavía no se publicaron.
Corrección: ignoren esto. Lo confundí con una publicación reciente del blog de GrapheneOS que decía “encontramos un problema por el cual MTE también se aplica a todas las apps del sistema”. GrapheneOS incorporó el “nivel completo de parches de seguridad 2024-03-05” en la versión 2024030600, así que parece que este parche también quedó incluido.
La seguridad de memoria probabilística de Arm MTE es un peldaño hacia el hardware determinista CHERI, https://saaramar.github.io/memory_safety_blogpost_2022/ y https://news.ycombinator.com/item?id=39668053
Las mitigaciones correctas deben apuntar a la primitiva de ataque primaria, es decir, la causa raíz del bug. Como soluciones de hardware están CHERI(Morello, CheriIoT) y MTE; como mitigaciones de software están kalloc_type+dataPAC, AUTOSLAB, Firebloom, GuardedMemcpy, CastGuard y la reducción de la superficie de ataque; y como lenguajes de programación seguros están Rust y Swift. MTE y CHERI encajan bien y ayudan a eliminar desde la causa raíz los bugs de este tipo. MSR, MSRC y Azure Silicon impulsaron la reducción de CHERI hasta RISC-V32E, la especificación de núcleo RISC-V más pequeña
Microsoft Research publicó como open source el stack de hardware/software CHERI para dispositivos IoT, https://msrc.microsoft.com/blog/2023/02/first-steps-in-cheri...
Los microcontroladores basados en CHERI buscan lograr garantías de seguridad muy fuertes mediante el codiseño de la arquitectura de conjunto de instrucciones (ISA), la interfaz binaria de aplicaciones (ABI), el modelo de aislamiento y las partes centrales del stack de software. Estos microcontroladores logran mitigación determinista de la seguridad espacial mediante funciones CHERI-ISA; mitigación determinista de la seguridad temporal del heap y de la pila entre compartimentos mediante barreras de carga, puesta a cero, recuperación y control de flujo de información de 1 bit; y compartimentación fina mediante funciones CHERI-ISA adicionales y un monitor pequeño
David Chisnall, U of Cambridge, https://lobste.rs/s/gnjx2n/c_can_be_memory_safe#c_9ohzku vía https://eclypsium.com/blog/a-faster-path-to-memory-safety-ch...
“Hay unas 13 mil millones de líneas de código C/C++ open source que forman parte de varias bases de cómputo confiables, y la cifra crece si se incluye código propietario. Incluso si todo el mundo dejara de escribir C/C++ ahora y todos los ingenieros de software se enfocaran en reescribir el código legado en lenguajes seguros, reemplazarlo todo tomaría entre 5 y 10 años, y al sustituir código muy probado por código nuevo que requiere algoritmos y estructuras de datos diferentes para ajustarse a los modismos permitidos de los lenguajes seguros, es muy probable que también aparezcan muchos bugs lógicos.”
“Si no se reescribe nada y solo se deja de escribir C/C++, con la tasa normal de reemplazo de código tomaría unos 50 años que las bases de cómputo confiables sean completamente seguras. Si no todos se ponen de acuerdo para dejar C/C++, serían al menos 100 años.”
“En cambio, si los principales fabricantes de CPU lanzan CPU CHERI dentro de 5 años, entonces dentro de 15 años a partir de hoy la mayoría de las máquinas, especialmente las de alto valor, tendrán seguridad de memoria sin que los programadores cambien su comportamiento.”
La primera es el sistema Sonata: https://github.com/lowRISC/sonata-system. Consiste en una PCB dedicada con un FPGA, varios periféricos y headers. El diseño de la PCB ya está terminado y estará disponible a través de Mouser; incluso el layout de la placa es open source, así que también puedes ensamblarla por tu cuenta si quieres. Actualmente están trabajando en el RTL para el FPGA. Cuando esté listo, tendrás un sistema tipo microcontrolador basado en CHERIoT, con documentación y herramientas
Además, también están creando el sistema Symphony, que combina Sonata con la raíz de confianza OpenTitan Earl Grey: https://github.com/lowRISC/symphony-system
Además, la última vez que lo revisé, CHERI no era sound. Todavía se podían escribir bugs de memoria encima de él; ¿eso ya se corrigió?
Solo las discusiones de bike-shedding probablemente tomarían eso
Este exploit probablemente se podría escribir en la mayoría de los lenguajes, incluido Rust
¿El hardware es tan malo? Dios...
Este enfoque es posible porque la GPU no permite un acceso relativamente directo al hardware como lo hace la CPU
Excelente investigación y artículo; me sorprende un poco, pero me alegra, que se haya publicado en el blog de GitHub.
¿Alguien sabe cuál es la “razón de negocio” de GitHub para hacer este tipo de investigación? No digo que necesariamente haga falta una razón de negocio, pero me sorprendió verlo aquí.
Esa función de investigación continuó como GitHub Security Lab. Semmle creó CodeQL, que ahora ofrece GitHub (https://docs.github.com/en/code-security/code-scanning/intro...). GitHub y Microsoft quieren asociar CodeQL con “información profunda de seguridad” (https://www.microsoft.com/en-us/security/blog/2023/11/02/ann...)
Por eso siguen financiando investigaciones de seguridad novedosas como esta, y los profesionales de seguridad de la industria lo agradecen.
También existe la app de GitHub, y la seguridad de esa app no está fuera del alcance de GitHub. Si un atacante puede instalar una app oculta en un teléfono, puede hacer varias cosas haciéndose pasar por el usuario. Si se trata de alguien con influencia en GitHub, el daño podría ser bastante grande, así que encontrar este tipo de vulnerabilidades también beneficia a GitHub.
Por eso podrían estar interesados en revisar y validar las funciones de seguridad del hardware Arm para el sandboxing usando MTE.
En primera instancia, un producto como GitHub no necesariamente necesita expertos en seguridad de Android. Pero a largo plazo hay beneficios potenciales.
[0]: https://en.wikipedia.org/wiki/Basic_research
Me sorprende que todavía no haya casos de CPUs y teléfonos con poca o ninguna GPU que se vendan como teléfonos de negocios.
Las ventajas en seguridad, costo y consumo de energía parecen claras.