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

Обход Rabbit Hole и Secure Boot на Яндекс мини 3 про

Это отложенная статья, так как информация передана в Яндекс Вug Bounty. Как я писал ранее, в Мини 3 про Яндекс хорошо подкрутил вопросы безопасности. В частности, запрещено изменять значения любых переменных u-boot environment кроме indication_brightness и silent: Изменения других переменных правкой их значений в разделе ENV не приводят к какому-либо результату. Однако, так было не всегда. Заводской u-boot защиты не содержит и вполне успешно загружает значения отредактированных переменных раздела ENV. Тем самым он позволяет выполнять свои команды, прописав их в bootcmd. Эксплуатировать данный баг можно двумя способами: сделать сброс до заводских настроек и "сломать" возможность обновления устройства (чтобы оно не затёрло заводской u-boot), либо заставить устройство со свежей прошивкой использовать старую версию бутлоадеров для загрузки. Опишу второй вариант подробнее. Для надежности, флеш память, с которой загружается устройство (NAND, eMMC или SD карта) содержит несколько копий загру

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

Как я писал ранее, в Мини 3 про Яндекс хорошо подкрутил вопросы безопасности. В частности, запрещено изменять значения любых переменных u-boot environment кроме indication_brightness и silent:

Изменения других переменных правкой их значений в разделе ENV не приводят к какому-либо результату.

Однако, так было не всегда. Заводской u-boot защиты не содержит и вполне успешно загружает значения отредактированных переменных раздела ENV. Тем самым он позволяет выполнять свои команды, прописав их в bootcmd.

Эксплуатировать данный баг можно двумя способами: сделать сброс до заводских настроек и "сломать" возможность обновления устройства (чтобы оно не затёрло заводской u-boot), либо заставить устройство со свежей прошивкой использовать старую версию бутлоадеров для загрузки.

Опишу второй вариант подробнее.

Для надежности, флеш память, с которой загружается устройство (NAND, eMMC или SD карта) содержит несколько копий загрузчиков. BL1 проверяет их хеш и подпись перед запуском. Если проверка не прошла - переходит к следующей копии. А следующая копия является заводской, так как swupdate, который Яндекс использует, умеет обновлять только первую копию бутлоадеров.

Другими словами, чтобы воспользоваться багом - нам надо "сломать" первую копию BL2 (иногда ее называют BB1ST), загружаемую с NAND.

Для этого можно воспользоваться таким скриптом, который нужно запустить на Мини 3 про:

#!/bin/sh
MTD="/dev/mtd0"
BS=262144 # 256 KiB — размер erase-блока
# Проверка root и устройства
[ "$(id -u)" -ne 0 ] && { echo "ERROR: Run as root"; exit 1; }
[ ! -e "$MTD" ] && { echo "ERROR: $MTD not found"; exit 1; }
# Проверка наличия cli_loop.elf
if [ ! -e "cli_loop.elf" ]; then
echo "ERROR: cli_loop.elf not found"
exit 1
fi
echo "Erasing first block and writing zeros (block size $BS bytes)..."
flash_erase "$MTD" 0 1 || { echo "ERROR: flash_erase failed"; exit 1; }
dd if=/dev/zero of="$MTD" bs="$BS" count=1 conv=sync 2>/dev/null || {
echo "ERROR: Failed to write zeros"
exit 1
}
echo "DONE! First erase block is completely zeroed."
echo "Setting ENV vars."
# Записываем команду запуска интерактивного режима u-boot в bootcmd
fw_setenv bootcmd "ubi part data;ubifsmount ubi0;ubifsload \${loadaddr} cli_loop.elf;bootelf;run storeboot"
# и просто на всякий случай silent=0
fw_setenv silent 0
echo
echo
echo Done. You should reboot and have interactive u-boot on uart.
exit 0

Скрипт форматирует первый блок NAND и записывает в него нули. После форматирования блока обязательно туда что-то записать, иначе код в BL1 при чтении получит ошибку ECC, пойдет по другой ветке исполнения и баг не выстрелит.

Также скрипт прописывает в bootcmd нужные команды для запуска интерактивного режима консоли u-boot на UART с помощью моей утилиты cli_loop.elf. Бинарник и исходники лежат тут. Как именно работает утилита я описал ранее.

Файл скрипта и cli_loop.elf следует скопировать на устройство, например, подключившись по ADB, сделать скрипт исполняемым и запустить:

пример запуска скрипта
пример запуска скрипта
Внимание!
Описанный выше скрипт вносит изменение в системную область прошивки. При неудачном стечении обстоятельств или при ошибке в его тексте можно получить нерабочее устройство, восстановление которого потребует выпаивание NAND и работу с программатором!

После перезагрузки - получим консоль u-boot без запроса кода Rabbit Hole:

-3

Чтобы вернуть загрузку Linux, необходимо в u-boot выполнить такие команды:

setenv bootcmd "run storeboot"
saveenv
reboot

При желании, можно записать в /data своё ядро Linux (файл Image) и выполнить в u-boot команды:

ubifsload ${loadaddr_kernel} Image
booti ${loadaddr_kernel}

Загрузка пойдет успешно:

-4

Тем самым обходится и Secure Boot.

Непосредственно к багу написанное ниже отношения не имеет - но при желании теперь можно "защитить" от изменения некоторые переменные ENV. В частности, переменную rh_unlock, которая устанавливается в 1 в случае успешного ввода кода Rabbit Hole и сбрасывается в 0 при обычной загрузке:

Выдержка из скрипта в ENV
Выдержка из скрипта в ENV

Для этого в консоли Linux на устройстве выполняем такие команды:

fw_setenv rh_unlock 1
fw_setenv ".flags" "rh_unlock:br"

Первой строкой устанавливаем переменную в нужное нам значение. Вторая строка устанавливает ей тип boolean и флаг read only, тем самым скрипты u-boot потеряют возможность ее изменения. Сама переменная rh_unlock на Яндекс мини 3 про делает только одно - включает консоль Linux на UART.

Обнаружил я данный баг совершенно случайно, без целенаправленного анализа - но в любом случае, теперь, имея возможность запуска своего кода, открыт большой простор для дальнейших исследований.