Как стать автором
Обновить
Positive Technologies
Лидер результативной кибербезопасности

GigaVulnerability: обход механизмов защиты микроконтроллеров GigaDevice GD32

Уровень сложностиСредний
Время на прочтение24 мин
Количество просмотров1.4K

При разработке аппаратных решений на базе микроконтроллеров производители хотят защитить свою прошивку от попадания в руки злоумышленников, так как в ней могут содержаться чувствительная информация, ключи шифрования, уникальные алгоритмы, представляющие ценность, и др. Для этого в большинстве микроконтроллеров реализованы технологии защиты от считывания (readout protection) встроенной флеш-памяти. Но так ли хорошо они защищают?

К сожалению, не все технологии Readout Protection работают так, как задумывалось. Для обхода защиты могут быть использованы уязвимости отладочных интерфейсов, нетривиальные атаки типа fault-injection и даже инвазивное вмешательство.

Мы в Positive Labs занимаемся исследованиями безопасности различного «железа». Поэтому, когда в наши руки попало устройство на базе микроконтроллера семейства GD32 компании GigaDevice, мы сами не заметили, как начали исследовать применяющиеся в этих микроконтроллерах технологии защиты. Данные микроконтроллеры довольно популярны и используются повсеместно, в том числе в качестве замены микроконтроллеров STM32, т.к. зачастую совместимы с ними по выводам и даже по карте адресного пространства. Исследование оказалось довольно увлекательным, а результаты — впечатляющими!

Дисклеймер

Статья носит исключительно информационный характер и не является инструкцией или призывом к совершению противоправных деяний. Наша цель — рассказать о существующих уязвимостях, которыми могут воспользоваться злоумышленники, предостеречь пользователей и дать рекомендации по защите своей личной информации в Интернете. Авторы не несут ответственности за использование информации. Помните, что не стоит забывать о безопасности своих личных данных.


Защита от считывания ПЗУ: что это и как работает

Технологии защиты от считывания могут называться по-разному, например у STMicroelectronics она так и называется Readout Protection, или RDP, у микроконтроллеров nRF — это APPROTECT, а у GigaDevice — Security Protection. Далее в статье речь пойдет именно о технологии Security Protection, но мы будем использовать название Readout Protection (или просто RDP), так как оно более распространено и лучше всего передает суть технологии.

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

  • RDP0 (level 0, no protection). Не накладывает никаких ограничений. Именно в таком виде микроконтроллеры поступают разработчикам, их можно программировать и отлаживать.

  • RDP1 (level 1, low level protection). На этом уровне доступ к флеш-памяти блокируется при подключении отладчика. При этом содержимое SRAM, как правило, остается доступным для чтения и записи через отладчик. Данный уровень можно понизить, однако содержимое флеш-памяти в таком случае будет очищено. Для некоторых микроконтроллеров данный уровень защиты является максимальным.

  • RDP2 (level 2, high level protection). На этом уровне отладочный интерфейс, как правило, безвозвратно отключается, а также выключается возможность загрузки через встроенный бутлоадер (используется для программирования) и SRAM, за счет чего сильно сокращается возможность взаимодействовать с микроконтроллером и как-то влиять на его исполнение. Кроме того, данный уровень нельзя понизить, то есть переиспользовать микроконтроллер для других целей уже не получится.

Обычно текущий уровень защиты определяется значением некоторого байта, который физически хранится в тех же ячейках флеш-памяти (в области Option Bytes), где располагается прошивка микроконтроллера, однако способ программирования этого байта отличается.

Перед тем, как перейти к нашему исследованию, хочется рассказать немного про уже известные техники обхода Readout Protection для различных микроконтроллеров. Это позволит нам понять, какие вообще подходы можно использовать. Кроме того, некоторые известные техники так или иначе оказались полезны, и мы их применили при исследовании. На наш взгляд, для погружения в тему можно ознакомиться с двумя исследованиями Йоханеса Обермеера и его соавторов, а также связанными с ними материалами. Приведем некоторый обзорный материал, опираясь на них.

Shedding too much Light on a Microcontroller’s Firmware Protection

С материалами исследования Йоханеса Обермеера можно ознакомиться по ссылке. Также на Хабре есть перевод. Работа посвящена микроконтроллеру STM32F0. Исследователи демонстрируют три разные техники: Cold-Boot Stepping (CBS), даунгрейд RDP2 и эксплуатацию уязвимостей отладочного интерфейса.

Как было замечено ранее, на первом уровне защиты (RDP1), как правило, доступно содержимое SRAM. При разработке необходимо это учитывать и стараться очищать содержимое SRAM от секретов, как только это становится возможным. Но в некоторых случаях даже это не спасет. Техника Cold-Boot Stepping позволяет получить набор промежуточных состояний SRAM с высокой степенью точности, вплоть до состояния после каждой инструкции.

Стенд для проведения атаки CBS. Источник
Стенд для проведения атаки CBS. Источник

В качестве примера исследователи приводят случай, когда целостность прошивки проверятся с помощью алгоритма CRC32. Перехватив все промежуточные значения CRC32 с помощью техники CBS, можно полностью восстановить содержимое прошивки.

Вторая техника — пример инвазивной атаки, когда необходимо получить доступ к «внутренностям» чипа. Исследователи использовали кислоту, чтобы получить доступ к кристаллу внутри микроконтроллера. Воздействуя на ячейки памяти с помощью ультрафиолетовых лучей определенной длины волны, они смогли изменить байт, отвечающий за уровень защиты. В результате уровень защиты был понижен с RDP2 до RDP1. Данная атака является довольно сложной, так как требует от исследователя определенных знаний в химии, физике ячеек памяти и строении кристалла, а сами работы по травлению корпуса чипа желательно проводить в специальной лаборатории.

Разметка функциональных блоков кристалла. Источник
Разметка функциональных блоков кристалла. Источник

Последняя техника из исследования демонстрирует эксплуатацию уязвимости через отладочный интерфейс на RDP1. Исследователи выяснили, что блокировка флеш-памяти происходит при первом обращении к шине через SWD, и если это было чтение флеш-памяти, то с некоторым шансом можно получить результат чтения. Для эксплуатации исследователи использовали еще один микроконтроллер STM32 в качестве атакующего девайса. В свободном доступе есть портированные проекты на альтернативные микроконтроллеры (например, RP2040). Также добавим, что атака работает и на некоторых других микроконтроллерах (не только STM32). Если под рукой нет отладочной платы для реализации атакующего устройства, можно обойтись J-Link c OpenOCD и программно-управляемым реле.

Таким образом, исследователи показали, что защиту STM32F0 можно полностью обойти независимо от установленного уровня RDP. Атака на RDP1 реализуется очень легко, что нельзя сказать про инвазивную атаку на RDP2.

One Exploit to Rule Them All? On the Security of Drop-in Replacement and Counterfeit Microcontrollers

Само исследование и материалы к нему доступны по ссылке. Основной целью исследования были микроконтроллеры STM32F1 и его клоны, в том числе от компании GigaDevice. В исследовании продемонстрированы различные техники обхода RDP1 через отладочный интерфейс, еще одна инвазивная атака на RDP2 и глитч по напряжению для обхода RDP1. Подробнее о каждой технике можно прочесть в первоисточнике, а сейчас рассмотрим лишь некоторые, наиболее важные для нашего исследования.

Диаграмма блоков GD32F103x4/6/8/B. Исследователь может получить доступ к флеш-памяти различными способами. Источник
Диаграмма блоков GD32F103x4/6/8/B. Исследователь может получить доступ к флеш-памяти различными способами. Источник

Если посмотреть на диаграмму блоков любого микроконтроллера, например, GD32F103 на изображении выше, можно заметить, что доступ к контроллеру флеш-памяти (FMC) может быть осуществлен различными способами: ядро имеет прямой доступ к FMC через шины инструкций и данных. Кроме того, FMC подключен к шине AHB, а мастер-устройства на шине (ядро, DMA-движок) могут быть инициаторами операций c флеш-памятью. Многие известные атаки оказываются возможны, потому что не все эти способы получения доступа к флеш-памяти учитываются при активации блокировки. В некоторых микроконтроллерах в RDP1 не блокируется доступ ядра через шины инструкций и данных, за счет чего содержимое флеш-памяти можно получить с помощью load-инструкций. Иногда оставляют возможным доступ только по шине инструкций, и в таком случае содержимое флеш-памяти исследователь безопасности может получить, манипулируя регистром смещения таблицы векторов прерываний (VTOR) и вручную инициируя различные прерывания, о чем подробно рассказано в исследовании Exception(al) Failure — Breaking the STM32F1 Read-Out Protection. В примере с GD32F103 проиллюстрирована возможность атаки путем копирования содержимого флеш-памяти в SRAM через DMA-движок с помощью отладчика. Идея с использованием DMA-движка пригодилась и в нашем исследовании при раскручивании одной из уязвимостей.

Рассмотрим еще одну инвазивную атаку на чипы GD32F103/GD32F130. В ней исследователи используют многокристальное строение микроконтроллера.

Декапсулированный микроконтроллер GD32F103. Чип состоит из кристалла логики (снизу) и кристалла флеш-памяти (сверху). Источник
Декапсулированный микроконтроллер GD32F103. Чип состоит из кристалла логики (снизу) и кристалла флеш-памяти (сверху). Источник
Строение многокристального микроконтроллера GD32F103/GD32F130. Источник
Строение многокристального микроконтроллера GD32F103/GD32F130. Источник

Как видно из схемы, микроконтроллер состоит из кристалла логики и кристалла флеш-памяти, соединенных друг с другом с помощью соединительных проводов, доступ к которым можно получить с помощью шлифовального листа. На наш взгляд, это проще, чем химическая декапсуляция, описанная ранее, однако всe равно требует некоторых навыков. Исследователи выяснили, что для общения с флеш-памятью используется протокол QSPI, через который после сброса при включении (power-on reset) считываются: заводская конфигурация, встроенный загрузчик, Option Bytes и часть прошивки. Считанные данные, по всей видимости, кэшируются для более быстрого доступа в дальнейшем. Эта информация о процессе первичной инициализации также оказалась очень полезной, и на ее основе мы смогли сделать некоторые предположения о том, как найденные нами уязвимости работают на более низком уровне.

Пример получения доступа к соединительным проводам кристалла флеш-памяти. Источник
Пример получения доступа к соединительным проводам кристалла флеш-памяти. Источник

Напоследок немного затронем тему fault-injection атак. Кажется, что они, наряду с инвазивными атаками, являются последней надеждой потенциального злоумышленника для обхода защиты от считывания, особенно в RDP2, когда недоступен отладочный интерфейс и поверхность атаки значительно сужается. В рассматриваемой работе исследователям с помощью глитча по напряжению удалось сбросить состояние блокировки флеш-памяти после подключения отладчика и при этом не потерять заранее подготовленное для последующих этапов атаки содержимое SRAM. Данная атака нацелена на RDP1, однако существует множество успешных примеров понижения RDP2 и включения отладочного интерфейса с помощью глитча по напряжению:

  • nRF52 Debug Resurrection (APPROTECT Bypass) — пример обхода технологии APPROTECT в nRF52 и получения доступа к отладочному интерфейсу и флеш-памяти.

  • wallet.fail — Hacking the most popular cryptocurrency hardware wallets — в исследовании рассматривается безопасность популярных криптокошельков и различные атаки на них. Один из изучаемых примеров — это кошелек Trezor на базе STM32F2/4. Применяя глитч по напряжению, исследователи смогли успешно понизить RDP2 и получить доступ к отладочному интерфейсу.

  • chip.fail — Glitching the silicon of the Internet-of-Things — исследование посвящено применению глитча по напряжению для различных микроконтроллеров, в том числе для понижения уровня защиты и включения отладочного интерфейса.

Подводя итоги обзорной части статьи, можно выделить три основных подхода в обходе RDP-технологий:

  • Атаки через отладочный интерфейс — актуальны в случае, если отладочный интерфейс вообще доступен (например, в RDP1) или его удалось активировать через другие атаки.

  • Fault-Injection атаки в целом и Voltage Glitch в частности — эффективные атаки, часто могут быть реализованы без использования дорогостоящих инструментов, однако могут потребовать длительного начального исследования для поиска правильных параметров атаки.

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

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

Как всe началось

Исследование началось с изучения сенсора качества воздуха. Устройство было построено на базе ARM-микроконтроллера GigaDevice GD32E230. Нам стало очень интересно, как устроена защита прошивки сенсора и мы решили исследовать еe. На печатной плате разведен отладочный интерфейс SWD и пины NRST, VCC и GND.

Первым делом мы, конечно же, попробовали подключиться к SWD через J-Link — и тут нас ожидал сюрприз. Оказалось, отладка не работает при использовании официальной утилиты J-Link, но как минимум считывается DPIDR — регистр идентификации отладочного порта. OpenOCD с конфигурационным файлом для целевого микроконтроллера не справляется даже с этим. С помощью логического анализатора удалось выяснить, что утилиты J-Link пробуют разные способы подключения к устройству, в том числе подключение в состоянии сброса, то есть при подтягивании сигнала NRST к земле. Обнаружив это, мы модифицировали конфигурацию OpenOCD и в ходе эксперимента смогли добиться такого же результата, как и через утилиты J-Link — получили DPIDR, но отладка при этом нормально не работала. Наблюдаемое поведение было странным, и хотя завести отладку у нас так и не получилось, мы не были до конца уверены, что чип залочен в RDP2. Это могла быть просто переконфигурация пинов SWD.

Мы попытались выяснить, что можно делать через SWD в состоянии сброса микроконтроллера. Для этого мы написали конфигурационный файл OpenOCD, в котором NRST подтягивался к GND при подключении. Кроме того, в дальнейшем сигналом NRST можно было свободно управлять:

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

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

Интереснее обстояли дела с отладочными модулями: они не были в состоянии сброса, их можно было конфигурировать. Например, для установки бита CDEBUG_EN регистра DHCSR (Debug Halting Control and Status Register) нужно было указать правильный ключ в старших битах регистра. Также работала запись в регистры компараторов модуля FPB (Flash Patch and Breakpoint).

Используя отладочные модули, мы изучили возможности для остановки ядра при запуске, но SWD всe равно становился недоступен. Скорее всего, это RDP2, но для того, чтобы убедиться в этом до конца, было решено взять точно такой же, но чистый чип, записать в него свою прошивку и проверить, можно ли залочить в RDP2.

В результате проверки оказалось, что поведение нашего залоченного в RDP2 чипа полностью совпадает с поведением целевого девайса. Можно точно сказать, что на нем активирован RDP2, а значит, достать прошивку через SWD не получится. Тем не менее доступность отладочного интерфейса в состоянии сброса показалась нам очень странной. Если обратиться к документации микроконтроллеров STM32, там будет указано, что при включенном RDP2 отладочный интерфейс отключается даже в состоянии сброса. Для GD32 это не так, и мы решили исследовать эту особенность микроконтроллеров.

GigaVulnerability #1

Идея для первой возможной уязвимости лежала на поверхности. Как мы выяснили, в состоянии сброса SWD интерфейс доступен. Возможно, при выходе из состояния сброса потребуется некоторое время для того, чтобы отключить SWD, и это окно гонки (момент, когда SWD доступен после изменения состояния сигнала сброса) можно будет использовать для обращения к шине. Схематично это можно изобразить следующим образом:

Идея уязвимости в виде схемы
Идея уязвимости в виде схемы

Чтобы проверить возможность реализации данной уязвимости, сначала необходимо немного разобраться в том, как устроен SWD. Его подробное описание есть в спецификации Arm Debug Interface Architecture Specification. В статье приведем сжатое описание основных моментов, необходимых для проверки уязвимости. В протоколе используется два сигнала:

  • SWCLK — сигнал тактирования,

  • SWDIO — сигнал ввода-вывода данных.

С помощью этих сигналов можно передавать определенные пакеты для чтения или записи регистров DP (Debug Port) или AP (Access Port).

Запись регистра через SWD. Источник
Запись регистра через SWD. Источник
Чтение регистра через SWD. Источник
Чтение регистра через SWD. Источник

Физически отладчик подключается к DP. Через него может быть получен доступ к разным банкам памяти в составе одного или нескольких AP.

Взаимодействие отладчика, AP, DP и отлаживаемой системы. Источник
Взаимодействие отладчика, AP, DP и отлаживаемой системы. Источник

В DP можно обратиться к четырем регистрам по их адресам, но в зависимости от типа операции по одному и тому же адресу могут быть разные регистры.

Адрес

Чтение

Запись

0x00

IDCODE

ABORT

0x04

CTRL/STAT

CTRL/STAT

0x08

 

SELECT

0x0c

RDBUFF

 

Регистр IDCODE — это идентификатор DP, то самое значение, которое мы пытались получить через J-Link и OpenOCD. Как было замечено выше, AP может быть несколько, а регистров в них может быть больше четырех. Регистр SELECT DP необходим для выбора нужного AP и банка регистров внутри него. Чтение регистров AP происходит с задержкой в одну операцию, то есть при первом чтении получается неопределенное значение, при втором чтении — результат первой операции и т. д. Регистр RDBUFF DP необходим для получения результата последней операции чтения регистра AP без инициации новой операции.

Спецификация ADI определяет три домена питания (множества компонентов чипа, электрическим питанием которых можно управлять независимо от других компонентов):

  • Always-on power domain — состоит из компонентов, которые всегда должны быть включены, например DP;

  • System power domain — состоит из системных компонентов;

  • Debug power domain — состоит из отладочных компонентов.

Управление системным и отладочным доменами питания осуществляется с помощью четырех старших битов регистра CTRL/STAT: с помощью CTRL-битов запрашивается включение домена питания, ACK-биты подтверждают включение выбранного домена.

CTRL/STAT. Источник
CTRL/STAT. Источник
Домены питания
Домены питания

В качестве AP нас будет интересовать только MEM-AP, необходимый для работы с памятью. У него много регистров, но нам необходимы только три из них:

  • Control/Status Word Register (CSW, 0x00) — необходим для настройки параметров обращения к памяти (ширины доступа, необходимость инкремента адреса и т. д.)

  • Transfer Address Register (TAR, 0x04) — задает адрес обращения к памяти.

  • Data Read/Write (DRW, 0x0c) — необходим непосредственно для совершения операции чтения или записи.

Алгоритм чтения памяти через SWD выглядит следующим образом:

  1. Сброс интерфейса для привода его к предсказуемому состоянию с последующим выбором соответствующего режима работы с помощью специальных последовательностей на линии данных. Дело в том, что некоторые DP могут работать как с протоколом SWD, так и с JTAG.

  2. Чтение регистра IDCODE DP. В некоторых случаях это может потребоваться для того, чтобы DP вообще включился и начал отвечать, но кажется, что это зависит от реализации.

  3. Включение системного и отладочного доменов питания через регистр CTRL/STAT.

  4. Выбор нулевого банка регистров MEM-AP через регистр SELECT DP.

  5. Конфигурация регистра CSW AP (например, для 32-битного доступа к памяти без инкремента адреса).

  6. Конфигурация регистра TAR AP и указание адреса, который нужно считать.

  7. Чтение регистра DRW AP для выполнения операции чтения.

  8. Чтение регистра RDBUFF DP для получения результата операции чтения.

Теперь вернемся к проверке возможной уязвимости. Подготовительные этапы (1-6) могут быть выполнены в состоянии сброса, так как SWD доступен, а DP и MEM-AP работают нормально. После этого необходимо изменить состояние сигнала сброса и как можно скорее совершить операцию чтения. Через некоторое время (чтобы операция чтения успела завершиться) сигнал сброса снова можно изменить и получить результат чтения через регистр RDBUFF DP.

Для проверки уязвимости изначально планировалось использование J-Link, так как он работает быстро и казалось, что реализовать описанный алгоритм с помощью libjaylink не составит труда. Однако J-Link не позволяет управлять сигналом сброса синхронно с линиями SWD. Из-за этого между изменением сигнала сброса и следующим пакетом по SWD проходило значительное время, и в момент чтения регистра DRW SWD уже не работал, то есть попасть в окно гонки было невозможно. Путем отправки через J-Link множества операций подряд и ручного управления состоянием сигнала NRST было установлено, что некоторое время после изменения сигнала интерфейс SWD продолжает функционировать, что сохраняет вероятность успеха.

Состояние гонки — оно такое
Состояние гонки — оно такое

Стало ясно, что вместо J-Link необходимо использовать микроконтроллер. Мы выбрали RP2040. Данный контроллер имеет два ядра ARM Cortex-M0+ и может работать на частоте до 133 MHz. Но его можно разогнать и до 250 MHz, и он всe еще будет справляться с большинством задач. Однако самой крутой особенностью RP2040 является наличие блоков PIO — программируемого ввода-вывода. В микроконтроллере имеется два таких блока, и каждый из них содержит четыре конечных автомата, которые одновременно и независимо от основных ядер микроконтроллера могут исполнять программу, состоящую из инструкций, нацеленных на работу с вводом-выводом. С помощью PIO можно реализовать распространенные и не очень протоколы. В некоторых случаях PIO может даже заменить FPGA.

За счет PIO, RP2040 широко применяется в аппаратной безопасности, особенно в различных fault-injection атаках. Вот лишь несколько примеров его использования:

  • PicoFly — мод-чип для Nintendo Switch, совершающий глитч по линиям eMMC для обхода безопасной загрузки. Это как раз пример, когда RP2040 заменяет FPGA, так как его аналог HWFLY использовал FPGA Lattice для совершения глитча;

  • ChipSHOUTER-PicoEMP — дешевое устройство для проведения электромагнитных fault-injection атак;

  • Starlink User Terminal Modchip — мод-чип для терминала Starlink, с помощью которого можно осуществить глитч по напряжению и тем самым обойти безопасную загрузку терминала.

Приятным бонусом использования RP2040 является то, что под него уже реализован SWD-отладчик Picoprobe, и для выполнения нашего алгоритма достаточно было взять готовые PIO-программы и C-функции для работы с ними. Получившийся стенд продемонстрирован ниже, его работа контролируется через UART по USB.

Стенд для проверки уязвимости
Стенд для проверки уязвимости

В результате проверки сразу же получаем положительный результат: подтвердилась возможность чтения SRAM! Ниже представлены скриншоты, демонстрирующие успех.

Шаги 6-7 описанного алгоритма: запись адреса 0x20000008 в TAR, выход из состояния сброса и чтение DRW
Шаги 6-7 описанного алгоритма: запись адреса 0x20000008 в TAR, выход из состояния сброса и чтение DRW
Повторный сброс и получение результата чтения (0x0800186c) через регистр RDBUFF
Повторный сброс и получение результата чтения (0x0800186c) через регистр RDBUFF

Похожим образом можно добиться и записи в память, но из-за сброса после записи, пользы от этого мало. Мы изучили возможность чтения SRAM целиком и убедились, что данные корректны. В дампе выделяются блоки инициализированных данных, содержимое стека с корректными адресами возврата и т. д. Кроме SRAM, удалось подтвердить возможность чтения и периферии. Также смогли подтвердить возможность считывания OptionBytes и еще раз убедились, что на чипе RDP2 лок.

Доступ к содержимому флеш-памяти через эту уязвимость получить не удалось, однако под нее вполне можно адаптировать технику Cold-Boot Stepping, о которой мы упоминали ранее. В некоторых случаях необходимости в CBS нет, и достаточно получить дамп SRAM в определенный момент, когда в SRAM микроконтроллера находятся интересующие нас данные. Именно так удалось подтвердить возможность расшифровки зашифрованного файла обновления исследуемого устройства, который был получен с помощью отправки зашифрованных фрагментов и извлечения расшифрованных фрагментов из SRAM перед их записью в ПЗУ. Кроме того, в ходе работы над этим было установлено, что блокировка флеш-памяти происходит при включении через SWD отладочного домена питания (бит CDBGPWRUPREQ регистра CTRL/STAT DP), а при выключении блокировка снимается. Подробнее о данной особенности расскажем при описании исследования возможности применения следующей уязвимости. Это сильно упрощает атаку, так как больше не было необходимости в переподключении питания микроконтроллера для разблокировки флеш-памяти на очередной итерации расшифровки блока. Таким образом, была достигнута первоначальная цель и получена возможность исследования полученной прошивки сенсора. Но исследование самих микроконтроллеров GD32 на этом не закончилось.

Мы решили проверить наличие уязвимости в других семействах микроконтроллеров GD32. Однако их очень много, и нужно было как-то оптимизировать задачу. Мы решили брать только микроконтроллеры общего назначения и сгруппировали некоторые их семейства по наличию общего пользовательского руководства. Также были исключены микроконтроллеры GD32F10x, так как на них нет RDP2, а RDP1 успешно обошли в исследовании, о котором было рассказано во введении. В итоге из каждой группы было приобретено несколько одинаковых микроконтроллеров (запасные и на случай тестирования более низких уровней защиты).

Группировка микроконтроллеров. Источник
Группировка микроконтроллеров. Источник
Приобретенные микроконтроллеры
Приобретенные микроконтроллеры
Распаянные микроконтроллеры. Микроконтроллеры с RDP2 идут парами
Распаянные микроконтроллеры. Микроконтроллеры с RDP2 идут парами

Сводная таблица исследуемых микроконтроллеров и результаты проверки первой уязвимости:

Family

MCU

Release

RDP2

GigaVulnerability #1

GD32F1x0

GD32F130C8T6

AJ2139

Yes

No

GD32F3x0

GD32F330C8T6

PJ2146

Yes

No

GD32F4xx

GD32F405RGT6

JJ2239

Yes

No

GD32L23x

GD32L233RCT6

MJ2306

Yes

Yes

GD32E23x

GD32E230K8T6

JJ2125

Yes

Yes

GD32E50x

GD32E503VCT6

MJ2119

Yes

Yes

В таблице также приведена информация о том, когда чип был выпущен. Например, AJ2139 означает, что чип был выпущен в 39-ю неделю 2021 года. Более свежие чипы могут оказаться неуязвимыми. Из таблицы видно, что уязвимыми оказались семейства E и L. Эксперименты с семействами F показали, что SWD всe еще доступен в состоянии сброса, но становится недоступным сразу после выхода из него. Уязвимость актуальна только для RDP2, поэтому не все микроконтроллеры представлены в таблице. Результаты были неудовлетворительными, поэтому исследование продолжилось, а впереди нас ждали еще несколько интересных результатов.

GigaVulnerability #2

Ранее было замечено, что на исследуемом микроконтроллере GD32E230 блокировка флеш-памяти происходит при включении отладочного домена питания, то есть при установке бита CDBGPWRUPREQ в регистре CTRL/STAT DP, а сбросив бит, блокировка деактивировалась после сброса всего микроконтроллера. Пришла идея проверить, как включение и отключение отладочного домена питания влияет на блокировку флеш-памяти прямо во время исполнения прошивки, например, из SRAM. Оказалось, что блокировку таким образом можно снять, и это еще одна уязвимость, позволяющая обойти ограничения RDP1. Алгоритм состоит из следующих этапов:

  1. Загрузка прошивки в SRAM с помощью отладчика и последующий ее запуск. Прошивка будет ожидать получения сигнала для снятия дампа флеш-памяти. В качестве канала связи может быть использован, например, UART.

  2. Сброс бита CDBGPWRUPREQ регистра CTRL/STAT DP. Это может быть сделано с помощью команды chip.dap dpreg 0x4 0x0 в OpenOCD.

  3. Подача загруженной прошивке сигнала для снятия дампа флеш-памяти через выбранный канал связи.

  4. Чтение содержимого флеш-памяти.

Family

MCU

Release

RDP2

GigaVulnerability #2

GD32F1x0

GD32F130C8T6

AJ2139

Yes

Yes

GD32F3x0

GD32F330C8T6

PJ2146

Yes

No

GD32F4xx

GD32F405RGT6

JJ2239

Yes

Yes

GD32L23x

GD32L233RCT6

MJ2306

Yes

No

GD32E23x

GD32E230K8T6

JJ2125

Yes

Yes

GD32E50x

GD32E503VCT6

MJ2119

Yes

Yes

GD32C10x

GD32C103CBT6

JJ2232

No

Yes

GD32E10x

GD32E103CBT6

JJ2153

No

Yes

GD32F20x

GD32F205VCT6

AJ2139

No

Yes

GD32F30x

GD32F303CGT6

JJ2121

No

Yes

GD32F403

GD32F403RGT6

JJ2117

No

Yes

Из таблицы видно, что практически все приобретенные микроконтроллеры оказались уязвимы к данной атаке на RDP1.

GigaVulnerability #3

Предыдущие уязвимости покрыли практически все исследуемые микроконтроллеры, кроме семейства F с блокировкой RDP2. Как отмечалось ранее, в случае с семействами F доступ к SWD есть в состоянии сброса и блокируется сразу после выхода из него. Однако случайно мы обнаружили, что уязвимость все-таки работает после выхода из сброса при включении (POR, power on reset). То есть если отключить питание микроконтроллера, подтянуть сигнал NRST к земле и снова включить питание, после чего попробовать повторить алгоритм из первой уязвимости, то он сработает. Более того, окно гонки значительно больше, чем в семействах E и L: на GD32F130 это около 1600 мкс против примерно 20 мкс на GD32E230. Более того, если микроконтроллер не выходит из состояния сброса дольше этого времени, то SWD не будет заблокирован и можно повторить атаку после очередного сброса без переподключения питания. Однако данная находка казалась довольно бесполезной, так как момент чтения следует после сброса при включении, SRAM и периферия в этот момент еще не инициализированы кодом прошивки.

Возможно, доступ к инициализированному содержимому SRAM можно получить, если повторить технику глитча по питанию из этой статьи. Напомним, что там исследователям удалось сбросить состояние блокировки RDP1, но при этом сохранить заранее подготовленное содержимое SRAM. Существует вероятность, что в нашем случае с семействами микроконтроллеров GD32F также можно отключить питание на короткий промежуток времени, и состояние блокировки при этом будет сброшено, но инициализированное содержимое SRAM со всеми секретами и прочей полезной информацией сохранится, и его можно будет прочитать с помощью эксплуатации найденной уязвимости. Мы не стали проверять это предположение и в своем исследовании пошли другим путем.

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

Начали мы, конечно же, с анализа по напряжению и смотрели момент после сброса при включении. Анализ проводился осциллографом DSCope U3P100.

Полученная осциллограмма напряжения при запуске микроконтроллера GD32F130. Желтый сигнал — NRST, красный — VCC.
Полученная осциллограмма напряжения при запуске микроконтроллера GD32F130. Желтый сигнал — NRST, красный — VCC.

На осциллограмме видно, что сразу после сброса при включении в течение примерно 18.5 мс напряжение сильно зашумлено, после чего стабилизируется. Экспериментально было проверено, что сама прошивка микроконтроллера начинает исполняться после этого наблюдаемого шума. То есть, скорее всего, этот шум соответствует некоторой начальной инициализации микроконтроллера, которая происходит при включении. Среди шума заметны повторяющиеся скачки, отмеченные стрелками и образующие 32 промежутка примерно одинаковой длины.

Начало осциллограммы
Начало осциллограммы

Если внимательно посмотреть на самое начало осциллограммы, то видно, что практически сразу после включения подряд идут первые два скачка, а следующие два скачка происходят примерно через 1.6 мс — примерно столько времени и длится окно гонки, в котором доступен SWD. Более того, экспериментальным путем мы проверили, что SWD отключится на последующих сбросах, если процесс инициализации дойдет до этого момента на осциллограмме. После этого идут 32 блока, разделенных скачками напряжения, о которых мы говорили ранее. Давайте посмотрим на них внимательнее.

Первый из повторяющихся блоков
Первый из повторяющихся блоков

Внутри блока заметны более маленькие, но различимые скачки напряжения, и если посчитать промежутки между ними, то их выйдет 64. Промежутки подобной формы встречаются и в других блоках с шумом. Полученные нами данные можно интерпретировать, обратившись к исследованиям из вводной части, где с помощью инвазивной атаки был получен доступ к линиям QSPI и обнаружено, что по ним читается при включении микроконтроллера. Если предположить, что самый маленький повторяющийся 64 раза блок соответствует считыванию 16 байтов из флеш-памяти через QSPI, то получится следующая интерпретация:

Интерпретация полученных данных
Интерпретация полученных данных

Считываются следующие данные:

  1. Заводская конфигурация (0x20 байт).

  2. Загрузчик (0xC00 байт).

  3. Option Bytes (0x10 байт).

  4. Первые 32 страницы прошивки из флеш-памяти (каждая по 0x400 байт), то есть половина всех страниц, что соответствует данным другого исследования.

Интерпретировав данные, мы приступили к проверке возможности глитча по напряжению. В качестве цели был выбран момент считывания Option Bytes. Сам глитчер построен на базе того же микроконтроллера RP2040, о котором мы говорили ранее. К нему были добавлены два MOSFET — для управления питанием и для самого глитча. Стенд представлен на фотографии ниже. Также представляем скриншоты осциллографа во время глитча:

Слева — наш глитчер. По центру — глитч во время считывания Option Bytes (красный сигнал — VCC, желтый — пульс глитчера). Справа — форма того же глитча.
Слева — наш глитчер. По центру — глитч во время считывания Option Bytes (красный сигнал — VCC, желтый — пульс глитчера). Справа — форма того же глитча.

К сожалению, попытки глитча не увенчались успехом. Микроконтроллер оказался очень чувствительным к питанию, и при слишком маленькой ширине глитча его поведение никак не менялось, а при небольшом увеличении ширины пульса, он уже переставал отвечать. Для восстановления его работоспособности приходилось переподключать питание. Нащупать золотую середину, при которой чип остался бы работоспособным, и при этом стал бы доступен отладочный интерфейс, не удалось. Возможно, стоило поэкспериментировать с начальным напряжением или параметрами компонентов глитчера. Несмотря на это, нам удалось многое выяснить о первоначальной инициализации. Кроме того, созданный глитчер в дальнейшем оказался полезен и помог в обходе APPROTECT на nRF52.

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

Это сработало, и значение сигнала на пине A9 микроконтроллера менялось в соответствии с тем, какие значения вывода мы писали в регистры GPIO:

С помощью логического анализатора видим изменение сигнала на пине A9
С помощью логического анализатора видим изменение сигнала на пине A9

Затем проверили модуль USART:

Как и хотели, в результате такой инициализации в UART была выведена буква F:

С помощью логического анализатора видим вывод буквы F в UART
С помощью логического анализатора видим вывод буквы F в UART

Затем было решено проверить работоспособность DMA-движка. Как мы знаем из других исследований, данный модуль может быть полезен для обхода RDP.

Во время окна доступности SWD мы инициализировали всю необходимую периферию, записали в SRAM определенное значение и попытались передать его в UART через DMA, и это сработало:

С помощью логического анализатора видим содержимое памяти в UART через DMA
С помощью логического анализатора видим содержимое памяти в UART через DMA

Следующим шагом, конечно же, был эксперимент по возможности замены адреса SRAM (0x20000000) на адрес флеш-памяти (0x08000000). Его успех казался сомнительным, т.к. на момент конфигурации через SWD флеш-память еще не инициализирована до конца, да и вообще у нас активирован RDP2 лок, это не должно было сработать. Но мы попробовали:

Какие-то данные появляются в UART спустя 18.5 мс, то есть после начальной инициализации микроконтроллера, когда SWD уже недоступен
Какие-то данные появляются в UART спустя 18.5 мс, то есть после начальной инициализации микроконтроллера, когда SWD уже недоступен

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

Это прошивка из флеш-памяти!
Это прошивка из флеш-памяти!

Это оказалась прошивка микроконтроллера! То есть мы подтвердили возможность обхода RDP2 микроконтроллера через эксплуатацию уязвимости отладочного интерфейса SWD.

Family

MCU

Release

RDP2

GigaVulnerability #3

GD32F1x0

GD32F130C8T6

AJ2139

Yes

Yes

GD32F3x0

GD32F330C8T6

PJ2146

Yes

Yes

GD32F4xx

GD32F405RGT6

JJ2239

Yes

Yes

GD32L23x

GD32L233RCT6

MJ2306

Yes

No

GD32E23x

GD32E230K8T6

JJ2125

Yes

No

GD32E50x

GD32E503VCT6

MJ2119

Yes

No

По результатам проверки уязвимости подвержены все исследуемые микроконтроллеры семейства F c RDP2. Итоговая таблица со всеми уязвимостями:

Как видно, все приобретенные микроконтроллеры так или иначе оказались уязвимыми.

Сравнение семейств E/L и F

Анализируя полученные результаты и обратившись к пользовательским руководствам соответствующих семейств контроллеров, мы пришли к некоторым выводам о различиях в эксплуатации семейств микроконтроллеров E/L и F.

В описании контроллера флеш-памяти для семейств E/L есть пункт о времени ожидания при исполнении инструкций:

  • 0~2 waiting time within 64K bytes when CPU executes an instruction

Вероятно, это связано с тем, что частоты работы этих микроконтроллеров настолько низкие (или наоборот, скорость работы памяти очень высокая), что такое время ожидания при получении инструкций из флеш-памяти является приемлемым. В связи с этим нет необходимости в использовании кэш-памяти. Вместе с тем не кэшируется и значение Option Bytes и, в частности, байта, отвечающего за уровень защиты. После каждого сброса он повторно считывается из флеш-памяти, за счет этого у нас есть небольшое окно (20 мкс) после каждого сброса.

Описание для семейств F несколько отличается. Исполнение инструкций из первой части памяти происходит без ожидания, а оставшаяся часть — с большой задержкой:

  • No waiting time within 32K bytes when CPU executes an instruction

  • A long delay when fetching 32K ~ 64K bytes data from flash

Это достигается использованием кэша, который инициализируется при первом запуске после подачи питания. Процесс этой инициализации мы даже наблюдали с помощью анализа по напряжению. Кроме того, экспериментально было проверено, что при обращении за пределы «быстрой» части флеш-памяти действительно будет большая задержка, сопровождаемая шумом при анализе напряжения. Этот шум аналогичен тому, что мы видели при загрузке страниц флеш-памяти во время начальной инициализации. Весь процесс начальной инициализации занимает заметное время — 18.5 мс. Если бы это происходило на каждом сбросе, было бы не очень приятно, поэтому уже считанный кэш переживает последующие сбросы. Как мы выяснили, значение Option Bytes также кэшируется. Поэтому получается большое окно, во время которого SWD доступен — 1.6 мс, до момента считывания Option Bytes. Но если Option Bytes хотя бы раз будут считаны и закэшированы, то и значение уровня RDP закэшируется, и при последующих выходах из состояния сброса SWD сразу же будет отключаться.

Скорее всего, это отличие связано с использованием дополнительного кристалла флеш-памяти, подключенного через QSPI. С помощью рентгена мы сделали снимки исследуемых микроконтроллеров. Хотя на них и не видно дополнительного кристалла, но по наличию дополнительных соединительных проводников в области основного кристалла можно сделать вывод, что дополнительный кристалл флеш-памяти используется во всех семействах F.

Рентгеновские снимки микроконтроллеров семейств F. Прямоугольниками выделены места соединения с QSPI флеш-памятью
Рентгеновские снимки микроконтроллеров семейств F. Прямоугольниками выделены места соединения с QSPI флеш-памятью

В остальных семействах микроконтроллеров это не наблюдается:

Рентгеновские снимки микроконтроллеров остальных семейств
Рентгеновские снимки микроконтроллеров остальных семейств

Результаты и выводы

По полученным результатам несложно догадаться, что некоторые реализации технологии защиты от считывания далеки от совершенства. На первый взгляд может показаться, что доступность отладочного интерфейса в течение нескольких микросекунд — это не проблема. Мы проверили и поняли, что это не так: даже 20 микросекунд достаточно для извлечения прошивки и загрузчика реального потребительского устройства, и это только один пример. Такие уязвимости присутствуют во многих семействах микроконтроллеров GD32, а значит, и уязвимых конечных устройств может быть значительно больше.

В результате исследования мы подтвердили возможность реализации новых техник обхода технологий защиты от считывания. Кроме того, с этим исследованием мы выступили на OFFZONE 2023. С презентацией можно ознакомиться по ссылке. Все уязвимости были зарепорчены в GigaDevice в рамках политики ответственного разглашения. Вендор планирует устранить их в новых ревизиях микроконтроллеров.

Теги:
Хабы:
+35
Комментарии2

Публикации

Информация

Сайт
www.ptsecurity.com
Дата регистрации
Дата основания
2002
Численность
1 001–5 000 человек
Местоположение
Россия