
CVE-2019-5700
К сожалению, у меня нет модного и остроумного названия для этой уязвимости. Эта ошибка довольно проста в эксплуатации. Я на самом деле долго держал её при себе, пока не понял, что она может затронуть больше устройств, чем я изначально думал.
Эта ошибка затрагивает SoC Nvidia Tegra t114–t210, загружающие загрузочные образы Android, однако есть одно важное различие:
Устройства на t114 значительно более уязвимы из-за их единого загрузчика, который также загружает TOS (TLK — безопасный монитор Nvidia), что означает, что загрузчик уже работает в защищённом контексте…
На устройствах t124+ компания Nvidia теперь использует nvtboot в качестве загрузчика ранней стадии, который загружает TOS до выполнения уязвимого загрузчика, который, в свою очередь, загружает и выполняет ядро. Это означает, что мы работаем в незащищённом контексте.
Давайте посмотрим на заголовок загрузочного образа Android (v1 — до Android 9):
struct boot_img_hdr
{
uint8_t magic[BOOT_MAGIC_SIZE];
uint32_t kernel_size; /* размер в байтах */
uint32_t kernel_addr; /* физический адрес загрузки */
uint32_t ramdisk_size; /* размер в байтах */
uint32_t ramdisk_addr; /* физический адрес загрузки */
uint32_t second_size; /* размер в байтах */
uint32_t second_addr; /* физический адрес загрузки */
uint32_t tags_addr; /* физический адрес для тегов ядра */
uint32_t page_size; /* предполагаемый размер страницы флеш-памяти */
uint32_t unused;
uint32_t os_version;
uint8_t name[BOOT_NAME_SIZE]; /* название продукта asciiz */
uint8_t cmdline[BOOT_ARGS_SIZE];
uint32_t id[8]; /* метка времени / контрольная сумма / sha1 / и т.д. */
uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
};
Мы видим поля для некоторых знакомых образов: ядро, ramdisk:
uint32_t kernel_size; /* размер в байтах */
uint32_t kernel_addr; /* физический адрес загрузки */
uint32_t ramdisk_size; /* размер в байтах */
uint32_t ramdisk_addr; /* физический адрес загрузки */
а иногда и теги (которые часто используются для дерева устройств или ATAGS)
uint32_t tags_addr; /* физический адрес для тегов ядра */
uint32_t page_size; /* предполагаемый размер страницы флеш-памяти */
Что насчёт полей «second»?
uint32_t second_size; /* размер в байтах */
uint32_t second_addr; /* физический адрес загрузки */
Хотя лично я никогда не видел, чтобы это использовалось на серийных устройствах, насколько я понимаю, образ «second» может использоваться для загрузчика второй стадии или дополнительного образа, который необходимо загрузить. Из моего опыта исследований, большинство загрузчиков от других OEM-производителей игнорируют это поле (так как оно не используется) либо применяют проверки корректности размера и адреса.
Загрузчик Nvidia ПРОВЕРЯЕТ корректность ядра, ramdisk и dtb — все они имеют жёстко заданные адреса загрузки, то есть адреса загрузки в заголовке игнорируются. Однако, похоже, Nvidia, возможно, забыла об этом почти забытом образе… загружая его произвольно без каких-либо проверок адреса или размера. Именно так — вы можете произвольно загрузить любой образ любого размера по любому доступному адресу памяти (всё на t114, незащищённая память на t124+).
Это означает, что мы можем свободно модифицировать загрузчик в памяти под свои нужды. Хотим ли мы пропустить проверку подписи (которая выполняется сразу после загрузки всех образов), перехватить загрузку TOS на t114 для выполнения кода в защищённом контексте или просто загрузить шелл-код для извлечения конфиденциальной информации.
Эксплуатация так же проста, как создание загрузочного образа, где вторичный образ является вашим пейлоудом/шелл-кодом, а поля addr/len установлены соответственно размеру вашего пейлоуда и месту, куда вы хотите его поместить. Образ может быть размером всего 4 байта.
На устройствах t124+ у нас нет защищённого контекста, так как nvtboot уже загрузил TOS на более раннем этапе загрузки, после чего передаёт выполнение загрузчику более поздней стадии (EBT), но мы всё ещё можем свободно записывать данные в этот загрузчик поздней стадии в памяти. Уязвимый загрузчик — это тот, который загружает ядро, поэтому он всё ещё довольно ценен, если вы хотите выполнить произвольный неподписанный код.
Не только адрес загрузки не проверяется, но и размер — что открывает путь к CVE-2019-5699, поскольку мы можем переполнить различные переменные/указатели, создав вредоносное поле secondary_size. Однако я не могу приписать это себе, так как осознал это только задним числом после публикации первоначального бюллетеня безопасности. Это также кажется гораздо более трудоёмким…
t114:
IROM->BCT->EBT->TOS->KERNEL
t124+ (может незначительно отличаться для более новых 64-битных SoC):
IROM->BCT->NVTBOOT->TOS->EBT->KERNEL
Эта проблема была первоначально сообщена 29 июля 2019 года в PSIRT Nvidia (группа реагирования на инциденты безопасности продукта). Они вежливо попросили меня отложить раскрытие до тех пор, пока все соответствующие клиенты не будут уведомлены и не будут выпущены исправления — с чем я с радостью согласился. Они поддерживали связь на протяжении всего процесса, и с ними было приятно работать. Сегодня (5 декабря 2019 года) Nvidia наконец завершила выпуск исправления для затронутого кода и опубликовала два различных бюллетеня безопасности, отражающих эту проблему, а также дала мне «зелёный свет» на раскрытие.
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 за то, что заставил меня тщательно проверять, как загрузчики разбирают заголовки загрузочных образов