
CVE-2019-5700
Leider habe ich keinen schicken und klugen Namen für diese Schwachstelle. Dieser Bug ist recht einfach auszunutzen. Ich habe ihn tatsächlich eine ganze Weile zurückgehalten, bis mir klar wurde, dass dies mehr Geräte betreffen könnte als ursprünglich angenommen.
Dieser Bug betrifft Nvidia Tegra t114-t210 SoCs, die Android-Boot-Images booten, allerdings gibt es einen wichtigen Unterschied zu beachten:
t114-basierte Geräte sind deutlich stärker betroffen, da ihr einheitlicher Bootloader auch das TOS (TLK – Nvidias Secure Monitor) lädt, was bedeutet, dass der Bootloader bereits im sicheren Kontext läuft...
Bei t124+-Geräten verwendet Nvidia jetzt nvtboot als frühen Bootloader, der das TOS lädt, bevor der anfällige Bootloader ausgeführt wird, der dann den Kernel lädt und startet. Das bedeutet, dass wir im nicht-sicheren Kontext arbeiten.
Schauen wir uns den Android-Boot-Image-Header an (v1 – vor 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];
};
Wir sehen die Felder für einige bekannte Images: 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 */
und manchmal tags (was oft für Device Tree oder ATAGS verwendet wird)
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
Was ist mit den 'second'-Feldern?
uint32_t second_size; /* size in bytes */
uint32_t second_addr; /* physical load addr */
Obwohl ich es persönlich noch nie auf einem Produktionsgerät verwendet gesehen habe, verstehe ich es so, dass das 'second'-Image für einen Second-Stage-Bootloader oder ein zusätzliches Image verwendet werden kann, das geladen werden muss. Meiner Erfahrung aus der Forschung nach ignorieren die meisten Bootloader anderer OEMs dieses Feld – da es nicht verwendet wird – oder sie wenden Plausibilitätsprüfungen auf Größe und Adresse an.
Nvidias Bootloader wendet durchaus Plausibilitätsprüfungen auf Kernel, Ramdisk und DTB an – alle haben fest codierte Ladeadressen, was bedeutet, dass die Ladeadressen im Header ignoriert werden. Allerdings scheint es, als hätte Nvidia dieses weitgehend vergessene Image vielleicht übersehen... indem es willkürlich geladen wird, ohne jegliche Prüfung von Adresse oder Größe. Das ist richtig – man kann jedes Image beliebiger Größe an jede erreichbare Speicheradresse laden (alles auf t114, nicht-sicherer Speicher auf t124+).
Das bedeutet, wir können den Bootloader im Speicher nach Belieben patchen. Ob wir die Signaturprüfung (die unmittelbar nach dem Laden aller Images erfolgt) überspringen, das Laden des TOS auf t114 kapern, um Codeausführung im sicheren Kontext zu erreichen, oder einfach etwas Shellcode laden, um vertrauliche Informationen auszulesen.
Die Ausnutzung ist so einfach wie das Erstellen eines Boot-Images mit dem sekundären Image als Payload/Shellcode und den addr/len-Feldern, die entsprechend auf die Größe des Payloads und die gewünschte Zieladresse gesetzt werden. Das Image kann nur 4 Bytes groß sein.
Auf t124+-Geräten haben wir keinen sicheren Kontext, da nvtboot das TOS bereits in einer früheren Boot-Phase geladen hat, die dann die Ausführung an einen späteren Bootloader (EBT) übergibt, aber wir können trotzdem frei in diesen späten Bootloader im Speicher schreiben. Der anfällige Bootloader ist derjenige, der den Kernel lädt, also ist er immer noch recht wertvoll, wenn man beliebigen unsignierten Code ausführen möchte.
Nicht nur die Ladeadresse wird nicht überprüft, auch die Größe nicht, was Tür und Tor für CVE-2019-5699 öffnet, da wir durch das Erstellen eines bösartigen secondary_size-Feldes verschiedene Variablen/Zeiger überlaufen lassen können. Die Anerkennung dafür kann ich jedoch nicht übernehmen, da ich dies erst im Nachhinein erkannt habe, nachdem das ursprüngliche Sicherheitsbulletin veröffentlicht wurde. Es scheint auch viel mehr Arbeit zu sein...
t114:
IROM->BCT->EBT->TOS->KERNEL
t124+ (kann bei neueren 64-Bit-SoCs leicht abweichen):
IROM->BCT->NVTBOOT->TOS->EBT->KERNEL
Dieses Problem wurde ursprünglich am 29. Juli 2019 an Nvidias PSIRT (Product Security Incident Response Team) gemeldet. Sie baten höflich darum, die Offenlegung zu verschieben, bis alle betroffenen Kunden benachrichtigt und Patches ausgerollt worden seien – dem bin ich gerne nachgekommen. Sie haben während des gesamten Prozesses die Kommunikation aufrechterhalten und waren angenehm in der Zusammenarbeit. Heute (5. Dezember 2019) hat Nvidia endlich die Verteilung des Fixes an den betroffenen Code abgeschlossen und zwei verschiedene Sicherheitsbulletins veröffentlicht, die dieses Problem widerspiegeln, und mir grünes Licht für die Offenlegung gegeben.
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 dafür, dass ich akribisch überprüft habe, wie Bootloader Boot-Image-Header parsen