Раз, два, три — ёлочка, ГОРИ

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

«А давайте вы сделаете какой-нибудь новогодний рисёч?» — примерно такую задачу поставили нашей Positive Labs этим летом. Поскольку мы любим изучать разнообразные железки, первым делом подумали про умные устройства. А что у нас с Новым Годом обычно ассоциируется? Правильно — новогодняя ёлка. Быстрый поиск в сети показал, что smart-ёлки существуют, и даже не от дядюшки Ляо, а вполне себе серьезной компании. На том и порешили — берем Twinkly Light Tree и смотрим, что там с безопасностью.

Прошлые исследования

До нас серию Twinkly исследовала компания MWR InfoSecurity (что интересно, с той же самой целью). В результате были обнаружены инфраструктурные ошибки, используя которые можно было выключить вообще все гирлянды этого бренда в мире:

Так исследователи иллюстрировали кнопку по отмене Рождества
Так исследователи иллюстрировали кнопку по отмене Рождества

Ошибка заключалась в протоколе MQTT и отсутствии ограничений доступа при подключении к чужим устройствам. Поскольку исследование было в далеком 2018, уязвимость давно исправлена. Но то были инфраструктурные косяки. Мы же решили изучить само устройство и его прошивку, в идеале — добиться удалённого запуска кода (спойлер — всё получилось).

А что, собственно, внутри?

Как становится понятно из названия, это не просто гирлянда, а целая светящаяся ель, состоящая из каркаса, светодиодных рядов и блока управления. Блок умеет подключаться к сетям Wi-Fi (а также играть роль точки доступа), при этом для запуска конфигурации сети сверху есть большая кнопка управления со светодиодом рядом:

Рисунок 1. Блок управления Twinkly Light Tree
Блок управления Twinkly Light Tree

Внутри корпуса блока управления можно обнаружить саму кнопку, трехцветный светодиод и модуль ESP32-WROOM.

Рисунок 2. Печатная плата блока управления Twinkly
Печатная плата блока управления Twinkly

Справа на плате можно заметить набор контактов, подозрительно похожих на отладочные. Здесь установлен стандартный модуль ESP32, его распиновка известна, поэтому назначение контактов легко определяется прозвонкой:

Рисунок 3. Назначение отладочных выводов платы управления Twinkly
Назначение отладочных выводов платы управления Twinkly

Эти контакты предназначены для первоначальной прошивки модуля ESP32. Если производитель не заблокировал интерфейс, их также можно использовать для считывания содержимого флэш-памяти и e-Fuses. Для этого существует удобный официальный инструмент esptool:

Рисунок 4. Считывание содержимого ПЗУ через утилиту esptool
Считывание содержимого ПЗУ через утилиту esptool

Чтение e-Fuses выполняем схожим образом, но уже через утилиту espefuse:

Рисунок 5. Считывание e-fuses через утилиту esefuse
Считывание e-fuses через утилиту esefuse

Нам интересны следующие e-Fuses:

  • FLASH_CRYPT_CNT         = 127 (0b1111111)

  • JTAG_DISABLE              = True (0b1)

  • DISABLE_DL_DECRYPT  = True (0b1)

  • ABS_DONE_0                 = True (0b1)

  • ABS_DONE_1                    = False (0b0)

Остановимся на каждом подробнее:

  • Нечетное значение FLASH_CRYPT_CNT говорит о том, что на микроконтроллере активен Flash Encryption. Значит код в полученном дампе флешки зашифрован и просто так исследовать его не получится.

  • Активные JTAG_DISABLE и DISABLE_DL_DECRPYT означают, что отладка заблокирована и средствами самого контроллера расшифровать данные не выйдет.

  • Наконец, ABS_DONE_0 и ABS_DONE_1 показывают, что включен Secure Boot v1, поэтому запускать свой код запрещено (даже при наличии ключа шифрования флешки).

Вердикт: перед нами релизный вариант конфигурации, включена максимальная защита.

Differential Power Analysis

Воспользуемся самым простым методом получения прошивки из контроллера ESP32-D0WD-v3 —  Differential Power Analysis (DPA). Суть в том, что по потреблению питания микроконтроллера можно косвенно вычислить значения, которыми он оперирует в регистрах. Самый банальный пример — когда в сигнале, полученном с линии питания микросхемы, можно отчетливо определить состояния некоторой внутренней линии:

Рисунок 6. Каждый прирост потребления соответствует логической единице протокола передачи
Каждый прирост потребления соответствует логической единице протокола передачи

В случае с ESP32 можно «подслушать» процесс шифрования AES. Конечно, настолько очевидно, как в примере на рисунке, увидеть ключ или данные не получится — здесь все 128 бит обрабатываются за один единственный «тик» процессора. Поэтому для вычисления значения ключа нужно использовать метод Correlation Power Analysis (CPA): выполняем десятки тысяч измерений с разными данными на входе, сравниваем с поведением предполагаемой модели и побайтно подбираем подходящий ключ.

Очень детально процесс извлечения ключа описан в статье за авторством Kévin Courdesses. На базе его же проекта esp-cpa получился вот такой девайс (который вы ещё встретите в других статьях про приключения с ESP32):

Рисунок 7. ESP-CPA с подключенным анализируемым чипом
ESP-CPA с подключенным чипом из другого исследования

Этот девайс одновременно эмулирует SPI-флешку и выполняет замеры питания в момент расшифровки блока данных (а ещё умеет гличить импульсами питания, нагревать чип с высокой точностью, записывать UART, отлаживать по JTAG...). То есть эмулятор каждый раз посылает новый случайный блок данных, выжидает момент расшифровки и сохраняет замеры тока на линии:

Рисунок 8. Записанный график потребления питания (меньшее значение соответствует большему току
Записанный график потребления питания (меньшее значение соответствует большему току)

Затем по накопленным данным с десятков и сотен тысяч замеров делается анализ корреляции — насколько реальные графики совпадают с «моделью» — предполагаемым поведением питания для конкретного значения части ключа. Сильно вдаваться в детали процесса не буду, на эту тему выйдет отдельная статья, пока что сосредоточимся на результате.

Результат анализа корреляции выглядит как 16 графиков (по одному на байт раунд-ключа) с 256 линиями (по одной на каждый вариант байта). Та линия, что сильно выделяется на фоне других, — верно угаданное значение.

Рисунок 9. Анализ корреляции для блока 0x1000, раунда 0
Анализ корреляции для блока 0x1000, раунда 0

Из графиков получаем ключ 8aef836729ebf14d4f17a88cdb2d69ce. С его помощью повторяем анализ уже для раунда 1 (поскольку в ESP32 применяется AES-256 и части ключа только от первого раунда недостаточно).

Рисунок 10. Анализ корреляции для блока 0x1000, раунда 1
Анализ корреляции для блока 0x1000, раунда 1

Таким образом получаем вторую половину ключа — 397f9cb7d00dd312d45fc7f1884661a8. Но и это еще не все!

В ESP32 ключ модифицируется в зависимости от смещения, поэтому путем анализа питания мы получили ключ уже после модификации (для смещения 0x1000). За то, как именно модифицируется ключ, отвечает e-Fuse FLASH_CRYPT_CONFIG (по умолчанию выставляется в максимальные 0xF). Алгоритм можно взять из официальной утилиты espsecure:

def flashencryption_tweak_key(key, offset, tweak_range):
	addr = offset >> 5
	key ^= ((mul1  addr) | ((mul2  addr) & mul2_mask)) & tweak_range
	return int.to_bytes(key, length=32, byteorder="big", signed=False)

Алгоритм симметричный, поэтому его применение к полученному ключу обращает модификации. В результате получаем исходный ключ шифрования, который уже можно использовать в утилите espsecure:

8A FF 83 65 29 EB B1 5D 4F 15 A8 8C 9B 2D 61 C6
39 7E 9C B7 F0 0D D7 12 D4 5D C7 F1 C8 46 69 A8

Важный момент: ключ уникален для каждого устройства, так что если вам нужно расшифровать другой девайс (пусть даже той же модели и с такой же прошивкой), все придется проделывать заново ;)

Краткий обзор прошивки

У считанного дампа стандартный вид для проектов на основе ESP32: загрузчик, два OTA-слота, прошивка на базе FreeRTOS и шифрованный Non-Volatile Storage с отдельно сохраненным ключом. Нестандартными можно назвать только разделы с заводской конфигурацией и огромный раздел movie для хранения пользовательского видеоролика.

Смещение

Размер

Назначение

0x0000

0xC0

Подпись для Secure boot

0x1000

0x5700

Bootloader

0x8000

0x1000

Таблица разделов

0x9000

0x4000

NVS (Non-Volatile Storage), зашифровано keys

0xD000

0x20

otadata, информация о текущем используемом слоте прошивки

0x10000

0x200000

ota_1, слот прошивки №1

0x210000

0x200000

ota_2, слот прошивки №2

0x410000

0x4000

settings

0x414000

0x4000

data, информация об устройстве

0x418000

0x3e0000

movie

0x7FF000

0x1000

keys, ключи шифрования для NVS

Неожиданностью стал включенный Stack Smash Protection: в начале каждой функции на верхушку стека записывалось случайное значение, а в конце проверялось, не изменилось ли оно.

Рисунок 11. Одна из самых маленьких функций, где используется Stack Smash Protection
Одна из самых маленьких функций, где используется Stack Smash Protection

В прошивке мы обнаружили многочисленные обработчики HTTP-протокола. Это неудивительно, ведь основной способ взаимодействия с устройством — по Wi-Fi через HTTP-запросы вида GET /xled/v1/…. При этом девайс подключается к существующей Wi-Fi сети, либо сам раздает ее. Помимо HTTP имеется возможность удаленного управления по протоколу MQTT (в том числе есть поддержка Apple Homekit).

В онлайн-источниках есть много информации о протоколе этих гирлянд, в том числе готовые проекты на Python. С их помощью можно управлять девайсом, причем если вы уже в Wi-Fi сети, никакого логина не требуется  — даже знать IP-адрес не обязательно. Все гирлянды дружно отвечают на широковещательный UDP-запрос.

Уязвимости!

Конечно, полный доступ к управлению устройством из локальной сети — это не так интересно (для этого нужен пароль от Wi-Fi), как удаленный запуск своего кода на самом устройстве. Чтобы решить эту задачу, немного поковыряемся в прошивке и найдем интересную библиотеку blufi:

Рисунок 12. Так-так, что тут у нас?
Так-так, что тут у нас?

По сути, перед нами пример от разработчика SDK, как можно выполнить начальную настройку по Bluetooth LE (задать пароль для Wi-Fi). Интересна blufi тем, что около года назад в ней находили критические уязвимости, в том числе возможность записи произвольных данных по конкретному адресу:

Рисунок 13. Уязвимость в механизме установления защищенного канала
Уязвимость в механизме установления защищенного канала

Если кратко, протокол формирования секретного ключа принимает почти все параметры от клиента, при этом можно передать ключ размером до 8192 бит (хотя в коде библиотеки место предусмотрено только для 1024 бит). В результате получаем возможность перезаписи указателя на буфер и размера данных. А следующим запросом эти данные можно записать куда угодно. Самое странное, что разработчик микроконтроллера (Espressif) тогда не признал уязвимость и отказался создавать CVE, но обновил код примера-библиотеки:

Источник: https://habr.com/ru/companies/pt/articles/978514/


Интересные статьи

Интересные статьи

Сомневаюсь что вы не знаете что это такое, но если не поняли, то это когда в js пишешь log, нажимаешь tab, и появляется console.log();Все ими пользуются, но не все пишут. Так зачем о них говорить? Пот...
МТС уже несколько лет ведёт облачный бизнес на базе вендорских решений. В отличие от него, новая платформа — наша собственная разработка. Это позволяет обеспечить полный контроль над развитием платфор...
Конструктор сайтов Тильда давно зарекомендовал себя как удобный инструмент для создания сайтов — на его базе реализованы десятки тысяч проектов. Некоторые из них размещены на серверах самой платформы,...
Многие из вас хотели бы реализовать какую-то часть компьютерных игр в реальности: системы прокачки, уровни репутации, борьба добра со злом — не зря же мы проходим через геймификацию всего на свете.Мое...
В марте 2025 года «1С-Битрикс» выпустил обновление Виртуальной машины до версии 9.0.6, которое исправляет уязвимость, связанную с повышением привилегий bitrix->root (оценка CVSS: 8.0). «1С-Бит...