Уже несколько лет в Positive Labs мы исследуем механизмы безопасности в SoC RK35xx от Rockchip. За это время нам и другим исследователям безопасности удалось весьма неплохо проанализировать их. В результате было обнаружено несколько уязвимостей, две из которых были интересны нам, как людям, причастным к “железу”.

В первом случае нам пришлось разработать эмулятор eMMC памяти, чтобы эксплуатировать TOC-TOU уязвимость в момент загрузки прошивки.

Во втором — мы атаковали SoC с помощью Power Glitch, воздействуя на процесс проверки прошивки.

В обоих случаях нам удалось обойти механизм Secure Boot, а платформа RK35xx стала поистине хрестоматийным примером с точки зрения аппаратного хакинга, поэтому мы решили поделиться техническими деталями. 

Предыстория

Orange Pi 5 на базе RK3588 попала в поле нашего зрения два года назад. Нам, как исследователям безопасности, в первую очередь было интересно включить защиты на данном чипе. Популярная история, что защита в чипе есть, но воспользоваться ей, как и протестировать, могут не все. В данном случае все было именно так, потому что защита в виде Secure Boot была заявлена производителем, но информации о том, как её включать, у нас не было.

Мы решили исправить эту ситуацию. Так у коллеги родилась подборка статей и выступлений на тему того, как устроен Secure & Encrypted boot у Rockchip RK35xx. Конечно, попутно мы немного смотрели и в сторону обхода этих же защит. Одним из интересных результатов исследования стало получение BootROM. И не только его. Но для начала немного напомню, как загружается RK35xx.

У чипа есть BootROM, который стартует после подачи питания, а после этого, в зависимости от конфигурации в OTP, запускается код с одного из носителей: SPI Flash, eMMC, SD-card.

Процесс загрузки SoC
Процесс загрузки SoC

Интересно, что если Secure Boot включен, то вместо штатного BootROM стартует Secure BootROM. Так вот, его тоже удалось достать! Все это подогрело комьюнити исследователей безопасности. Ведь имея Secure BootROM для RK3588, внутри которого реализована логика механизмов защиты, появилась возможность реверсить самую интересную часть загрузки… и тут понеслось)

P.S: Кстати, два BootROM есть только у RK3588, в то время как у RK3566 и RK3576 загрузчик один и Secure Boot реализован в нем.

Немного про TOC-TOU

В ноябре 2024 года “девочки на форуме” писали, что независимый исследователь безопасности обнаружил уязвимость типа TOC-TOU (Time-of-Check to Time-of-Use) в Secure BootROM от RK3588S. Также в мае 2025 другой исследователь выступал на нашем треке Positive Labs на Positive Hack Days с этой же уязвимостью. Оба выступления проходили в узких кругах и не записывались. По этой же причине мы не можем на них сослаться. Так или иначе про уязвимость было доложено в Rockchip и багу поправили в новой версии чипа RK3576.

Но на тот момент важным для нас было другое. Интересно было то, что эксплуатация уязвимости требует довольно хитрого устройства - eMMC эмулятора. Мы о нем давно говорили внутри своего коллектива, а тут появился такой повод все-таки приступить к разработке. Про разработку и технические особенности можете прочитать статью “Проект eMMC-emu” моего коллеги Димы. Первой целью для нашего эмулятора, как для инструмента хакера, стал RK3566. 

Обход Secure Boot при помощи eMMC эмулятора

Когда информация об уязвимости TOC-TOU стала публичной, мы сразу проверили имеющиеся у нас BootROM для RK3566 и RK3588. Оказалось, что оба чипа содержат эту ошибку.

Чтобы понять, как она эксплуатируется, сначала разберем процесс загрузки этих SoC. 

Схема загрузки SoC RK35xx
Схема загрузки SoC RK35xx

При загрузке Secure BootROM считывает данные с внешнего носителя — SPI Flash, eMMC или SD-карты. Сначала он ищет заголовок SPL по нескольким фиксированным смещениям. Для чтения используется буфер размером 0x200 байт, а наличие заголовка определяется по сигнатуре RKNS или RKSS в первых четырех байтах.

Структура заголовка
Структура заголовка

Когда заголовок найден, BootROM повторно считывает его уже в другой буфер и проверяет цифровую подпись, при этом hash заголовка зашит в OTP. Если проверка прошла успешно, то едем дальше. Но есть один нюанс! 

К этому моменту заголовок уже был прочитан дважды — в разные буферы. Данные во втором буфере проверены и считаются доверенными, а в первом по-прежнему находится лишь ранее прочитанная копия. Именно данные из первого буфера используются для дальнейшей загрузки бинарных файлов. И это проблема!

__int64 __fastcall load_from_storage(load_st_ctx *ctx) 
{
  // some lines omitted for clarity
  err = ctx->read_page(i, &fw_header_1, &page_addr); // read header to 0xff00_0040
  // some lines omitted for clarity
  if ( fw_header_1.magic == "RKSS" || fw_header_1.magic == "RKNS") 
  {
    // some lines omitted for clarity
    ctx->read(page_addr, data_buff, (v5 << 0xB) + 0x800); // read header to 0xff00_1000
    err = verify(data_buff);                // check signature of header at 0xff00_1000
    if ( !err ) 
    {
      // read binary to 0xff00_1000
      err = (ctx->read)( page_addr + fw_header_1.binaries[0].off,
                         data_buff,
                         fw_header_1.binaries[0].data_size << 9);
      if ( !err ) 
      {
	 // use header at 0xff00_0040
        err = load_binary__(&fw_header_1, data_buff, fw_header_1.binaries);   
        // some lines omitted for clarity
      }
    }
  }
}

Казалось бы, все в порядке: оба чтения выполняются с одного и того же адреса, а значит данные должны быть одинаковыми. Но в мире hardware hacking это предположение уже не работает. Если злоумышленник способен подменять данные «на лету» во время чтения с внешнего носителя, вполне корректная с точки зрения логики программа становится уязвимой.

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

Автор, обнаруживший уязвимость, предложил эксплуатировать ее через подмену данных с SD-карты. Мы же решили реализовать аналогичную атаку для eMMC. Для этого сначала разберем, как BootROM загружает и запускает DDR модуль.

Процесс загрузки кода из внешнего носителя RK35xx
Процесс загрузки кода из внешнего носителя RK35xx

Из схемы видно, что в первых 0x200 байтах заголовка находятся хеши двух исполняемых файлов: DDR и SPL. Именно их и нужно подменить, чтобы загрузить собственный код и обойти Secure Boot.

Идея проста: пока SoC проверяет корректный, подписанный заголовок, мы подменяем данные первого чтения. В результате BootROM продолжает считать, что использует доверенные хеши, хотя на самом деле получает наши, заранее вычисленные для модифицированного DDR, записанного в eMMC.

Модификация eMMC эмулятора для подмены данных

Описанная уязвимость в RK35xx стала для нас толчком к разработке эмулятора eMMC — устройства, способного полностью заменить штатную микросхему памяти. Такое устройство могло бы быть полезным для:

  • быстрого обновления и тестирования прошивок, хранящихся в eMMC;

  • обеспечения полного и быстрого контроля над данными в eMMC во время работы устройства.

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

Первично нужно было сделать просто эмулятор, а уже потом наградить его дополнительными возможностями. Когда мой коллега Дима закончил устройство на базе FPGA, которое позволяло загружать Orange Pi до стадии U-Boot, настало время модификаций.

Проведение TOC-TOU атаки через eMMC эмулятор предполагает следующие условия:

  • наличие устройства с RK3566 с включенным Secure Boot;

  • оригинальный образ, содержащий правильно подписанный заголовок;

  • наличие eMMC эмулятора, протестированного с BootROM, SPL, U-Boot и kernel на аналогичной плате.

Так как эмулятор построен на FPGA, лучше всего разбить задачу на базовые логические составляющие, так проще представлять архитектуру. Атака TOC-TOU предполагает два действия на уровне эмулятора:

  • выбор обращений к определенному адресу памяти, где расположен заголовок - 0х8000;

  • перенаправление этих обращений на один адрес во время check и на другой - во время usage.

То есть, первое чтение адреса 0x8000 должно быть перенаправлено в дополнительную область памяти с нашими hash. Второе чтение штатное. При этом предполагается нормальная работа с остальным массивом памяти. 

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

Название регистра

Назначение

Значение для RK3566 TOC-TOU

hack_count

Номер обращения к адресу, по которому будет производиться замена. Т.е. значение 2 обеспечит замену при каждом втором обращении.

0x2

hack_dstaddr

Адрес на который будет производиться замена

0xFFF00000

hack_opcode

Включение механизма замены

0x1

hack_srcaddr

Адрес который подлежит замене

0x00008000

Такая настройка готовит эмулятор к обходу Secure Boot. При этом образ, загруженный в эмулятор, также модифицируется:

1) в оригинальном образе модифицируется код, например, модуля SPL;

2) заголовок с hash значениями для модифицированного SPL отправляется в неразмеченную часть эмулируемой памяти.

Эти два изменения - всё, что нам нужно от образа. Вот как изменения в образе и изменения в логике работы eMMC эмулятора выглядят на одной схеме:

Логика подмены данных с eMMC-emu
Логика подмены данных с eMMC-emu

Обход Secure Boot

Как всегда, подготовка это 90% работы, далее дело за малым. В качестве подопытного для атаки и отладки мы выбрали одноплатник Orange Pi 3B на базе SoC RK3566. Такой выбор был сделан потому, что на данной плате есть разъем для внешних eMMC модулей. Это упрощает подключение нашего устройства.

Orange Pi 3B и внешний eMMC модуль
Orange Pi 3B и внешний eMMC модуль

В итоге получился вот такой стенд:

eMMC эмулятора подключенный к Orange Pi 3B через eMMC разъем
eMMC эмулятора подключенный к Orange Pi 3B через eMMC разъем

Про отладку тут речь не пойдет, поэтому просто сделаем вид, что все запустилось с первого раза. Мы “белые хакеры” и поэтому запускать вредоносный код, который сожжет SoC не станем. Просто поменяем строчки, которые выводятся в UART во время исполнения SPL.

Запуск неподписанного кода на RK3566
Запуск неподписанного кода на RK3566

В результате работы нам удалось обойти Secure Boot на RK3566. На RK3588 эта уязвимость также присутствует. А вот в RK3576 после проверки подписи заголовка первые 0x200 байт из data_buff копируются в header_struct, тем самым устраняя уязвимость.

На этой победной ноте мы закрыли тему с эмулятором, но не с Rockchip.

Just for fun: закроем багу сами

Так как patchROM функционала в RK3588/RK3566 похоже нет, можно считать, что эта уязвимость не может быть исправлена в уже выпущенных чипах. Никогда не попадать в функцию load_from_storage - единственный вариант избежать проблемы.

Для этого можно прошить в OTP (fuses) конфигурацию, позволяющую загружаться только по USB. Для этого нужно во фьюзы, отвечающие за выбор источника загрузки, записать значение 0xA, соответствующее загрузке сервисными командами по USB.

После этого при загрузке SoC будет попадать в функцию boot_USB (сюда не положили уязвимость). Так можно загрузить SPL сервисными командами с другого устройства.

void __noreturn init()
{
  // some lines omitted for clarity
  if ( !get_boot_cfg() )
  {
    // some lines omitted for clarity
    boot_storage();
    boot_USB();
  }
  while ( 1 )
    ;
}

__int64 get_boot_cfg() 
{
  // some lines omitted for clarity
  err = OTP_Read(&otp_val, 0x44, 1u, 1u);
  if ( !err )
  {
    otp_val &= 0xFu;
    if ( otp_val && otp_val < 6 || otp_val == 0xA )
      BOOT_CTX.boot_src_code = otp_val;
    return 0;
  }
  return err;
}

__int64 boot_storage() 
{
  // some lines omitted for clarity
  if ( !BOOT_CTX.boot_src_code )
  {
   // some lines omitted for clarity
  }
  i=0
  while ( boot_srcs[i]->code != BOOT_CTX.boot_src_code ) // boot_srcs[i]->code is 1..5
  {
    if ( ++i >= 5 )
      return 0xFFFFFFFF; // so with boot_src_code == 0xA we go here and avoid vulnerable function
  }
  err = (boot_srcs[i]->check)();
  if ( err )
    return err;
  return load_from_storage(i); 
}

Немного про Power Glitch

Про уязвимость, которую будем использовать ниже, историй нет. Только одна заметка без подробностей от ребят из BI.ZONE Hardware Lab о том, что им удалось обойти Secure Boot на Rockchip при помощи атаки по питанию. Поэтому, раз уж публичных деталей нет, давайте разбираться.

Для начала про fault injection (частным случаем которого является Power Glitch) нужно знать следующее: если система подвержена атаке, то дело за малым. 

Для этого анализируют код и ищут места, где кратковременный сбой может изменить логику выполнения. Обычно таких мест немало: пропуск инструкции, ошибочный результат сравнения или нарушение последовательности исполнения могут привести к обходу проверки. Поэтому в подобных атаках речь чаще идет не об одной уязвимости, а о совокупности небольших логических особенностей, которые становятся проблемой только в мире hardware hacking.

Конечно, существуют аппаратные и программные механизмы защиты от power fault injection: мониторы питания, повторные проверки, избыточные вычисления и другие. Их реализация требует дополнительных аппаратных ресурсов, усложняет программную логику и увеличивает стоимость разработки.

Вот Rockchip не делали этого, поэтому расскажем, чем это обернулось.

Обход Secure Boot при помощи Power Glitch

Атаку по питанию мы покажем на примере Orange Pi 5 Plus на базе RK3588. 

Логика работы BootROM ранее упомянутого RK3566 и RK3588 практически идентична. Но чтобы понять, как использовать fault injection для обхода Secure Boot, нам придется немного детальнее рассмотреть один момент. А именно проверку hash для SPL или DDR бинарей и их дальнейшую загрузку.

__int64 __fastcall load_from_storage(load_st_ctx *ctx)
{
  // some lines omitted for clarity
  err = verify(data_buff);              // check header signature
  if ( !err )
  {
    boot_stage = use_RKPK ? 3 : 2;
    err = check_boot_flow(0, boot_stage);
    if ( !err )
    {
      err = load_binary__(&fw_header_1, data_buff, fw_header_1.binaries); // CHECK DDR Training
      if ( !err )
      {
        boot_stage = use_RKPK ? 4 : 3;
        err = check_boot_flow(0, boot_stage);
        if ( !err )
        {
          // some lines omitted for clarity
          err = run_binary(data_buff);
            if ( !err )
            {
              err = load_binary__(&fw_header_1, 0, &fw_header_1.binaries[1]); // CHECK SPL
              if ( !err )
              {
                boot_stage = use_RKPK ? 5 : 4;
                err = check_boot_flow(0, boot_stage);
                if ( !err )
                {
                    // some lines omitted for clarity
                    err = run_binary(0);
  // some lines omitted for clarity
}

Из предыдущей главы мы помним, что заголовок содержит hash каждого бинарного файла и подписан. По сути, это и есть реализация Secure Boot: заголовок проверяется, данные из него используются для верификации кода, которому будет передано управление.

С точки зрения обычной логики все выглядит корректно. Но в мире аппаратной безопасности снова появляются нюансы. Используя power fault injection можно влиять на поток исполнения программы. Конечно, не напрямую, а косвенно. Например, сбой по питанию может привести к неправильному исполнению инструкции. 

Ремарка от автора

Тут у всех разное представление из-за различного уровня абстракции.

Для программиста на С это значит, что if в такой конструкции может работать неправильно.

if (protection_check_OK)
	edem_dalshe();
else
	ne_edem_nikuda();

Для разработчика на assembler эта конструкция будет выглядеть более многостадийной:

MOV             W23, W0
CBNZ            W23, ERR
	bl edem_dalshe	
ERR:
	bl ne_edem_nikuda

Тут уже и вычитка из памяти, и инструкция сравнения может отработать неверно.

А если на это посмотрит RTL разработчик, то вариантов, где сбойнуть систему найдет еще больше.

В функцию load_binary передается указатель на hash из заголовка, а также указатель на вычитанный модуль DDR. Внутри нее происходит подсчет реального hash для данных, а затем сравнение посчитанного и переданного hash значения. В случае успеха возвращается 0, если же сравнение показало разницу в hash, то 1.

Посмотрим, что внутри:

__int64 __fastcall load_binary(header_struct *header, char *data, header_2_struct *fw_desc)
{
  // some lines omitted for clarity
  size = fw_desc->data_size << 9;
  if ( (header->boot_flag & ENCRYPTED) != 0 )
  {
    // some lines omitted for clarity
    err = decrypt_and_hash(0, iv, data, size, calced_hash);
  }
  else
  {
    err = hash(data, size, calced_hash);
  }
  if ( !err )
  {
    err = sec_memcmp(fw_desc->hash, calced_hash, 0x20); // TRY FAULT INJECTION here or inside
    nex_boot_stage(boot_stage + 1, ret);
  }
  return err;
}

По сути всего две функции: hash и sec_memcmp. Посчитали hash и сравнили с оригиналом. Теперь можно взглянуть на assembler:

Обработка результата выполнения функции load_binary
Обработка результата выполнения функции load_binary
Конец функции load_binary
Конец функции load_binary
Конец функции sec_memcmp
Конец функции sec_memcmp

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

Неправильное выполнение одной из этих инструкций может привести к обходу Secure Boot. 

Идея атаки такая: необходимо, при помощи просадки напряжения на линии питания ядра вызвать сбой в работе чипа. Сбой следует проводить после чтения DDR из SPI Flash в память SoC. Именно тогда считается его hash и сравнивается с оригиналом в заголовке. Это позволит загрузить модуль DDR даже, если hash не сойдется.

Подготовка платы Orange Pi 5 Plus для атаки

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

Аддоны для других МК. Маленькие платы, минимальная обвязка.
Аддоны для других МК. Маленькие платы, минимальная обвязка.

Это существенно упрощает настройку параметров атаки и делает её воспроизводимой для других подобных МК. А в качестве управляющего контроллера для атаки и устройства, взаимодействующего с целевым чипом, мы используем платформу Chipolino.

В данном случае SoC крупный и сложный с точки зрения изготовления платы под него. Хотелось провернуть это факультативное дело по-быстрому. Поэтому, впервые за долгое время, мы немного отступили от наших принципов и решили провести атаку прямо на устройстве, а именно на Orange Pi плате.

Оценив документацию на SoC и схемотехнику для Orange Pi, стало ясно, что атаковать скорее всего нужно по линии питания VDD_CPU_LIT_S0. LIT от слова little, то есть little core. Именно на этом маленьком ядре исполняется код BootROM, который и нужно сбоить.

Еще одним доводом в пользу того, что BootROM работает на “мелком” ядре номер 0, является первая команда в нем. MRS X0, MPIDR_EL1 - команда, читающая системный регистр MPIDR_EL1 в регистр Х0. Далее идет проверка, что исполнение происходит именно на ядре 0, если нет, то зависаем в while (true).

Старт BootROM
Старт BootROM

Кроме того, анализ схемы показал еще несколько важных вещей, а именно:

  • наличие конденсаторов (не то чтоб мы их не ждали) и их количество на линии питания ядра

  • максимальный ток, который может выдавать источник питания ядра - 5А

Схема питания Orange Pi 5 Plus
Схема питания Orange Pi 5 Plus

Все конденсаторы на линии VDD_CPU_LIT_S0 были найдены и удалены с платы. SoC нормально стартует и без них, хотя так бывает не всегда. Зачастую для нормальной работы чипа приходится оставлять несколько конденсаторов, хотя это и мешает резко опускать питание ядра. Тогда приходится искать золотую середину.

Удаленные конденсаторы на нижнем слое
Удаленные конденсаторы на нижнем слое
Удаленные конденсаторы на верхнем слое
Удаленные конденсаторы на верхнем слое

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

В итоге, в дополнение к Chipolino изготовили внешний транзисторный модуль по такой схеме и вот что получилось:

Схема и плата модуля. Кстати, первый образец платы за вечер изготовил на фрезере еще один наш коллега.
Схема и плата модуля. Кстати, первый образец платы за вечер изготовил на фрезере еще один наш коллега.

Для работы ему нужно питание 5V и подключение к общему GND, а управляющий вход нужно подключить к Chipolino. 

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

Транзисторный модуль, установленный на Orange Pi
Транзисторный модуль, установленный на Orange Pi

Кстати, такое количество конденсаторов большой ёмкости в схеме не просто так. Модуль то подключает Vcore к GND через транзистор, то Vcore к этим емкостям. При переключении от GND в нормальное состояние DC/DC преобразователь пытается компенсировать низкое напряжение, что приводит к скачку напряжения. От такого скачка чип может перезагружаться. Дополнительные емкости, подключаемые при переключении, решают эту проблему.

Glitch RK35xx

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

Что такое логика атаки?

Включить устройство и спустя некоторое время замкнуть питание ядра на GND — самый простой вариант атаки. Но на практике логика часто оказывается значительно сложнее.

Почти всегда необходимо взаимодействовать с целевым чипом по UART, SPI или SWD, дождаться определённого этапа работы и только после этого кратковременно замкнуть питание ядра на GND.

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

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

Стенд для обхода Secure Boot на RK3588 при помощи Power FI. Транзисторный модуль подключен снизу.
Стенд для обхода Secure Boot на RK3588 при помощи Power FI. Транзисторный модуль подключен снизу.
Таблица соединений
Таблица соединений

Кстати, данная плата стартует с SPI Flash. То есть BootROM ищет заголовок и SPL именно там. По этой причине мы выстроили логику атаки с привязкой к обмену данными на SPI интерфейсе. Если посмотреть на лог загрузки:

Чтение данных по SPI и вывод UART
Чтение данных по SPI и вывод UART

то можно отметить следующее:

  • Примерный момент передачи управления модулю DDR можно определить по сообщениям, которые он начинает посылать в UART

  • DDR модуль стартует через ~550 мкс после окончания его чтения из SPI Flash

  • 17 раз дергается CS (chip select) от подачи reset и до конца чтения DDR модуля

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

Таким образом мы и логику сформировали, и сократили временное окно перебора до 550 мкс (что по опыту очень мало). Итак план:

  1. Запуск Orange Pi по сигналу reset (заведен на Chipolino)

  2. Ждем 17 фронтов на линии CS (заведен на GP28 Chipolino)

  3. Атакуем - кратковременно подтягиваем линию питания ядра к GND

  4. Ждем сообщений в UART от модифицированного модуля DDR

  5. Если сообщения получены, то атака успешна, код запустился. Если нет - повторяем п.1-5 меняя место атаки

  6. The End

Итак, включаем Secure Boot, запускаем корректно подписанный образ для исследования поведения системы. И после этого патчим DDR модуль. Не важно было, что менять, и поэтому для начала был изменен один байт в строке DDR. Поменяли ее на DDA. Как только этот образ был записан на SPI Flash, SoC перестал нормально загружаться, сообщения в UART пропали, а значит система была готова к проведению Power Glitch атаки.

Придерживаясь плана выше, мы реализовали алгоритм атаки в Chipolino и даже выложили его на GitHub. По сути, результатом наших изысканий стали несколько параметров успешной атаки. Вот они:

  • ~1 us ширина импульса

  • ~540 us от последнего фронта на линии CS при чтении DDR модуля

В результате получили:

  • запуск патченного DDR модуля

  • строку DDA в UART

Картина успешной атаки
Картина успешной атаки
Картина успешного импульса
Картина успешного импульса

Таким образом, с использованием глича по питанию удалось обойти Secure Boot на RK3588 и запустить собственный код. Стоит отметить, что, хотя речь в статье идёт о RK3588, эту атаку также можно воспроизвести на RK3566 и более новом RK3576.

Заключение

Короткая сводка для тех, кто путается как я:

Чип

ROM version

TOC-TOU

FI

Secondary Secure BootROM

RK3588

350B 20210512 V100

уязвим

уязвим

+

RK3566

350A 20210322 V300

уязвим

уязвим

-

RK3576

350E 20231110 V100

исправлен

уязвим

-

RK35xx оказался отличной платформой для демонстрации интересных техник, которые мы используем в лаборатории, а также устройств, которые разрабатываем для подобных исследований. Нам хотелось показать, что такое аппаратные атаки, hardware hacking и исследования в области аппаратной безопасности на практике. Надеюсь у нас получилось, а вам понравилось.

Статья является результатом работы коллектива Positive Labs.

Комментарии (0)