Добавить в корзинуПозвонить
Найти в Дзене
mamaich

Загрузка неподписанного device tree на Яндекс мини 3 про

Это отложенная статья, так как информация передана в Яндекс Вug Bounty. Описанный ниже способ может работать не только на мини 3 про (процессор Amlogic A4, он же A113L2), но и на других устройствах с этой и некоторыми предыдущими моделями процессоров Amlogic. Пример эксплуатации будет в конце статьи. Устройства Amlogic в качестве одного, из цепочки, загрузчиков используют модифицированный u-boot. В современных ритейл устройствах по умолчанию включен Secure Boot, препятствующий запуску неавторизованного кода, а загрузка своего DTB позволяет частично снизить эффективность данного запрета. Исходные тексты u-boot с правками от Amlogic периодически обновляются в репозиториях Khadas, CoreELEC и других. В данной статье я для примеров кода использую репозиторий khadas-vim4-android14, дополняя цитатами из IDA. Содержимое device tree на Яндекс мини 3 про загружается в функции board_late_init(). Так как исходников u-boot Яндекс не выкладывает, я обычно смотрю в файлы, выложенные Khadas - в данн
Оглавление

Это отложенная статья, так как информация передана в Яндекс Вug Bounty.

Описанный ниже способ может работать не только на мини 3 про (процессор Amlogic A4, он же A113L2), но и на других устройствах с этой и некоторыми предыдущими моделями процессоров Amlogic.

Немного теории.

Пример эксплуатации будет в конце статьи.

Устройства Amlogic в качестве одного, из цепочки, загрузчиков используют модифицированный u-boot. В современных ритейл устройствах по умолчанию включен Secure Boot, препятствующий запуску неавторизованного кода, а загрузка своего DTB позволяет частично снизить эффективность данного запрета.

Исходные тексты u-boot с правками от Amlogic периодически обновляются в репозиториях Khadas, CoreELEC и других. В данной статье я для примеров кода использую репозиторий khadas-vim4-android14, дополняя цитатами из IDA.

Содержимое device tree на Яндекс мини 3 про загружается в функции board_late_init(). Так как исходников u-boot Яндекс не выкладывает, я обычно смотрю в файлы, выложенные Khadas - в данном случае в файлы для устройства ba400. В нем код board_late_init() находится в файле board\amlogic\a4_ba400\a4_ba400.c

В начале функции мы видим вызов run_command("run common_dtb_load", 0):

имена функций в левом окне получены скриптом https://github.com/mamaich/uboot-kallsyms
имена функций в левом окне получены скриптом https://github.com/mamaich/uboot-kallsyms

Этой командой загружается DTB, который далее немного обрабатывается u-boot и передается в загружаемый Linux/Android.

В случае, если команда выполнится с ошибкой - на экран просто выводится сообщение и загрузка продолжится дальше. Это первая предпосылка к эксплоиту.

Значение common_dtb_load:

common_dtb_load=imgread dtb _aml_dtb ${dtb_mem_addr}

Команда imgread реализована в файле \cmd\amlogic\imgread.c:

argv[1] у нас "dtb"
argv[1] у нас "dtb"

Загрузка DTB производится функцией do_image_read_dtb:

-3

Так как у нас команда "imgread dtb _aml_dtb", то непосредственно чтение происходит в функции do_image_read_dtb_from_rsv. Данная функция читает DTB с NAND, затем проверяет его подпись/делает расшифровку вызовом secure_image_check(). Функция secure_image_check() делает расшифровку in place (то есть расшифрованные данные перезаписывают зашифрованные), а если расшифровка не удалась - данные остаются без изменений.

CONFIG_SKIP_KERNEL_DTB_SECBOOT_CHECK у нас отключена
CONFIG_SKIP_KERNEL_DTB_SECBOOT_CHECK у нас отключена

Красным выделен интересующий нас кусок исходника.
Здесь видно, что независимо от кода возврата функции secure_image_check вызывается memmove для перемещения данных на sizeof(struct aml_boot_header_t) == 0x300 байт к началу буфера.

Вот этот же код из декомпиляции в IDA:

-5

В случае некорректной подписи, Amlogic в функции secure_image_check ничего не делает с переданными данными (а мог бы забить их нулями или мусором). И это вторая предпосылка к эксплоиту.

То есть, do_image_read_from_rsv читает данные DTB с NAND, пытается их расшифровать/проверить подпись, и в случае, если подпись не сошлась - просто сдвигает прочитанные данные на 0x300 байт к началу буфера. Данные при этом никак не меняются (кроме сдвига). Как было видно выше - код возврата игнорируется, а данные остаются в памяти.

Переходим к эксплуатации.

Для эксплуатации уязвимости нам всего лишь требуется положить незашифрованный DTB со смещения 0x300 относительно начала.

Нам потребуется Яндекс мини 3 про с доступом через ADB.

Сперва получаем текущий DTB файл с устройства. На запущенной Яндекс мини 3 про расшифрованный DTB лежит по пути /sys/firmware/fdt. Требуется его скопировать на ПК и там преобразовать в текстовый формат командой:

dtc -I dtb -O dts ./fdt -o ./fdt.dts
на warning-и не обращаем внимания
на warning-и не обращаем внимания

Далее открываем получившийся fdt.dts и вносим в него правки. Для примера, добавим в раздел chosen в переменную bootargs= свою строку, которая будет добавлена к строке параметров ядра Linux.

Сперва удаляем лишнее:

-7

Следует удалить строку bootloader_build и все параметры bootargs, которые были добавлены u-boot автоматически. Оставляем только нашу добавляемую строку, для примера это будет "androidboot.rh_unlock=1":

-8

Преобразуем текстовый формат в бинарный:

dtc -I dts -O dtb ./fdt.dts -o ./fdt
-9

Добавляем 0x300 (768) байт в начало fdt файла:

(dd if=/dev/zero bs=1 count=768; cat fdt) > fdt.new

И копируем fdt.new на устройство, например, в папку /data.

На Станции выполняем команду:

cat /data/fdt.new >/dev/dtb
если запускать команду не с консоли UART, а из ADB, то строк meson_xxx не будет
если запускать команду не с консоли UART, а из ADB, то строк meson_xxx не будет

После чего перезагружаемся, при загрузке видим ошибку проверки подписи DTB:

в ENV прописано dtb_mem_addr=0x01000000
в ENV прописано dtb_mem_addr=0x01000000

Но при этом u-boot далее корректно принимает наш модифицированный DTB:

-12

И передает добавленную нами строку в конце параметров ядра:

-13

В отличие от метода инъекции параметров командной строки ядра через unifykey, данный метод не переживет сброс устройства до заводских настроек или установки обновления, но именно этим он и может быть полезен - можно тестировать потенциально опасные параметры командной строки без необходимости перепрошивки NAND в случае неуспеха (достаточно сделать сброс до заводских настроек).