
CVE-2019-5700
Desafortunadamente, no tengo un nombre moderno e ingenioso para esta vulnerabilidad. Este bug es bastante simple de explotar. De hecho, lo mantuve guardado durante bastante tiempo hasta que me di cuenta de que podría afectar a más dispositivos de los que pensé inicialmente.
Este bug afecta a los SoCs Nvidia Tegra t114-t210 que arrancan imágenes de arranque de Android; sin embargo, hay una distinción importante que hacer:
Los dispositivos basados en t114 se ven mucho más afectados debido a su bootloader unificado que también carga el TOS (TLK - el monitor seguro de Nvidia), lo que significa que el bootloader ya se está ejecutando en contexto seguro...
En dispositivos t124+, Nvidia ahora usa nvtboot como bootloader de etapa temprana que carga el TOS antes de que se ejecute el bootloader vulnerable, el cual carga y ejecuta el kernel. Esto significa que nos estamos ejecutando en un contexto no seguro.
Echemos un vistazo al encabezado de la imagen de arranque de Android (v1 - anterior a Android 9):
struct boot_img_hdr
{
uint8_t magic[BOOT_MAGIC_SIZE];
uint32_t kernel_size; /* size in bytes */
uint32_t kernel_addr; /* physical load addr */
uint32_t ramdisk_size; /* size in bytes */
uint32_t ramdisk_addr; /* physical load addr */
uint32_t second_size; /* size in bytes */
uint32_t second_addr; /* physical load addr */
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
uint32_t unused;
uint32_t os_version;
uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
uint8_t cmdline[BOOT_ARGS_SIZE];
uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
};
Vemos los campos para algunas imágenes familiares: kernel, ramdisk:
uint32_t kernel_size; /* size in bytes */
uint32_t kernel_addr; /* physical load addr */
uint32_t ramdisk_size; /* size in bytes */
uint32_t ramdisk_addr; /* physical load addr */
y a veces tags (que a menudo se usa para device tree o ATAGS)
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
¿Qué hay de los campos 'second'?
uint32_t second_size; /* size in bytes */
uint32_t second_addr; /* physical load addr */
Aunque personalmente nunca lo he visto usado en un dispositivo de producción, tengo entendido que la imagen 'second' puede usarse para un bootloader de segunda etapa o una imagen adicional que necesite ser cargada. Por mi experiencia en investigación, la mayoría de los bootloaders de otros OEM ignoran este campo, ya que no se usa, o aplican comprobaciones de coherencia sobre el tamaño y la dirección.
El bootloader de Nvidia SÍ aplica comprobaciones de coherencia al kernel, ramdisk y dtb, todos los cuales tienen direcciones de carga fijas en el código, lo que significa que las direcciones de carga del encabezado se ignoran. Sin embargo, parece que Nvidia quizás olvidó esta imagen casi olvidada... cargándola arbitrariamente sin ninguna comprobación de coherencia sobre la dirección o el tamaño. Así es: puedes cargar arbitrariamente cualquier imagen, de cualquier tamaño, en cualquier dirección de memoria accesible (todo en t114, memoria no segura en t124+).
Esto significa que podemos parchear libremente el bootloader en memoria para adaptarlo a nuestras necesidades. Ya sea que queramos omitir la verificación de firma (que ocurre inmediatamente después de cargar todas las imágenes), secuestrar la carga del TOS en t114 para obtener ejecución de código en contexto seguro, o simplemente cargar algo de shellcode para volcar información sensible.
La explotación es tan simple como crear una imagen de arranque con la imagen secundaria como tu payload/shellcode y los campos addr/len configurados adecuadamente según el tamaño de tu payload y hacia dónde quieres que vaya. La imagen puede ser tan pequeña como 4 bytes.
En dispositivos t124+ no tenemos contexto seguro, ya que nvtboot ya ha cargado el TOS en una etapa anterior del arranque, que luego entrega la ejecución a un bootloader de etapa posterior (EBT), pero aun así podemos escribir libremente en este bootloader de etapa tardía en memoria. El bootloader vulnerable es el que carga el kernel, por lo que sigue siendo bastante valioso si se quiere ejecutar código arbitrario sin firmar.
No solo no se verifica la dirección de carga, tampoco el tamaño, lo que abre la puerta a CVE-2019-5699 ya que podemos desbordar varias variables/punteros manipulando un campo secondary_size malicioso. Sin embargo, no puedo atribuirme el mérito de esto, ya que solo me di cuenta en retrospectiva después de que se publicara el boletín de seguridad inicial. También parece mucho más trabajo...
t114:
IROM->BCT->EBT->TOS->KERNEL
t124+ (may be slightly different for newer 64-bit SoC):
IROM->BCT->NVTBOOT->TOS->EBT->KERNEL
Este problema se informó inicialmente el 29 de julio de 2019 al PSIRT de Nvidia (Product Security Incident Response Team). Solicitaron cortésmente que retrasara la divulgación hasta que se hubiera notificado a todos los clientes relevantes y se implementaran las correcciones, lo cual he cumplido con gusto. Mantuvieron la comunicación durante todo el proceso y fue agradable trabajar con ellos. Hoy (5 de diciembre de 2019) Nvidia finalmente ha terminado de lanzar el parche al código afectado y ha publicado dos boletines de seguridad diferentes que reflejan este problema, además de darme luz verde para la divulgación.
https://nvidia.custhelp.com/app/answers/detail/a_id/4875
https://nvidia.custhelp.com/app/answers/detail/a_id/4910
balika011
beaups
npjohnson
djrbliss/Dan Rosenberg por hacerme revisar meticulosamente cómo los bootloaders parsean los encabezados de las imágenes de arranque.