
CVE-2019-5700
Purtroppo, non ho un nome alla moda e intelligente per questa vulnerabilità. Questo bug è abbastanza semplice da sfruttare. In realtà ci ho riflettuto per parecchio tempo finché non mi sono reso conto che questo potrebbe colpire più dispositivi di quanto pensassi inizialmente.
Questo bug colpisce i SoC Nvidia Tegra t114-t210 che avviano le boot image Android; tuttavia c'è un'importante distinzione da fare:
I dispositivi basati su t114 sono molto più colpiti a causa del loro bootloader unificato che carica anche il TOS (TLK - il monitor sicuro di Nvidia), il che significa che il bootloader è già in esecuzione in un contesto sicuro...
Sui dispositivi t124+, Nvidia ora utilizza nvtboot come bootloader di prima fase che carica il TOS prima che il bootloader vulnerabile venga eseguito, il quale carica ed esegue il kernel. Ciò significa che siamo in esecuzione in un contesto non sicuro.
Diamo un'occhiata all'header della boot image Android (v1 - precedente ad 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];
};
Vediamo i campi per alcune immagini familiari: 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 */
e a volte tags (spesso usato per il device tree o ATAGS):
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
E che dire dei campi 'second'?
uint32_t second_size; /* size in bytes */
uint32_t second_addr; /* physical load addr */
Anche se personalmente non l'ho mai visto usato su un dispositivo di produzione, a quanto mi risulta l'immagine 'second' può essere utilizzata per un bootloader di secondo stadio o per un'immagine aggiuntiva che deve essere caricata. Nella mia esperienza di ricerca, la maggior parte dei bootloader di altri OEM ignora questo campo - poiché non è usato - oppure applica controlli di sanità su dimensione e indirizzo.
Il bootloader di Nvidia APPLICA invece controlli di sanità a kernel, ramdisk e dtb - tutti con indirizzi di caricamento hardcoded, il che significa che gli indirizzi di caricamento nell'header vengono ignorati. Tuttavia, sembra che Nvidia si sia forse dimenticata di questa immagine per lo più dimenticata... caricandola arbitrariamente senza alcun controllo di sanità su indirizzo o dimensione.
Esatto - puoi caricare arbitrariamente qualsiasi immagine, di qualsiasi dimensione, in qualsiasi indirizzo di memoria accessibile (tutto su t114, memoria non sicura su t124+).
Ciò significa che possiamo modificare liberamente il bootloader in memoria per adattarlo alle nostre esigenze. Sia che vogliamo saltare la verifica delle firme (che avviene immediatamente dopo il caricamento di tutte le immagini), dirottare il caricamento del TOS su t114 per ottenere l'esecuzione di codice in un contesto sicuro, o semplicemente caricare un po' di shellcode per estrarre informazioni sensibili.
Lo sfruttamento è semplice come creare una boot image con l'immagine secondaria come payload/shellcode e i campi addr/len impostati appropriatamente alla dimensione del payload e a dove lo si vuole far finire. L'immagine può essere piccola fino a 4 byte.
Sui dispositivi t124+ non abbiamo il contesto sicuro, poiché nvtboot ha già caricato il TOS in una fase precedente dell'avvio, che poi passa l'esecuzione a un bootloader di fase successiva (EBT), ma possiamo comunque scrivere liberamente in memoria su questo bootloader di fase successiva. Il bootloader vulnerabile è quello che carica il kernel, quindi è comunque piuttosto prezioso se si vuole eseguire codice arbitrario non firmato.
Non solo l'indirizzo di caricamento non viene verificato, ma nemmeno la dimensione, il che apre la porta a CVE-2019-5699 poiché possiamo causare l'overflow di varie variabili/puntatori creando un campo secondary_size malevolo. Non posso però prendermi il merito di questo, poiché me ne sono reso conto solo col senno di poi, dopo la pubblicazione del bollettino di sicurezza iniziale. Sembra anche molto più lavoro...
t114:
IROM->BCT->EBT->TOS->KERNEL
t124+ (potrebbe essere leggermente diverso per i più recenti SoC a 64 bit):
IROM->BCT->NVTBOOT->TOS->EBT->KERNEL
Questo problema è stato inizialmente segnalato il 29 luglio 2019 al PSIRT di Nvidia (Product Security Incident Response Team). Mi hanno cortesemente chiesto di ritardare la divulgazione finché tutti i clienti interessati non fossero stati informati e le correzioni fossero state distribuite - cosa che ho fatto volentieri. Hanno mantenuto la comunicazione durante tutto il processo e sono stati piacevoli con cui lavorare. Oggi (5 dicembre 2019) Nvidia ha finalmente completato il rilascio della correzione per il codice interessato e ha pubblicato due diversi bollettini di sicurezza che riflettono questo problema, dandomi anche il via libera per la divulgazione.
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 per avermi spinto a controllare meticolosamente come i bootloader analizzano gli header delle boot image