Daily MaverickGROUNDUP: Joburg fails to open public swimming pools — againESPN DeportesA 10 años del Chile 7-0 México, ¿qué ha pasado en el futbol mexicano?RTP DesportoFernando Gomes tem "esperança" que situação de Ronaldo seja resolvida "a bem"The Jerusalem PostA dangerous habit: Legislating hatred one step at a time - opinionPunchINEC seeks release of outstanding funds ahead of 2027 pollsESPNMMA divisional rankings: It's not just fight results that shuffle top 10sBollywood HungamaNeil Bhoopalam wants to work with Rajkumar Hirani to explore family-oriented films: “It would be a good shift for me”VanguardDifficult terrain stalls evacuation of victims 24 hours after Ondo plane crashPopular ScienceAmazon is blowing out portable power stations and solar generators during its Prime Big Deal Days saleSportstarIndia vs West Indies LIVE Score, 1st T20I: IND wins the toss and opts to bowl against WIZDF heuteAktuelle Pressemitteilungen des ZDFХабрC++ в 2026-м: память, прод, игры, Rust и ИИ — зачем учить язык, который невозможно знать целиком
The Daily Newsstand · Free, Always
Tuesday, October 6, 2026

Глитчи глитчами, дамп дампом, но It's Time to End Information Anarchy

Translate

В этом году мы провели уже седьмую конференцию OFFZONE. Кроме того что на площадке можно послушать крутые технические доклады, каждый год здесь проводится множество активностей, в которых посетители могут поучаствовать и выиграть внутреннюю валюту конференции — офкоины. Несколько задач лежат прямо в бейдже каждого участника и доступны в любой момент, достаточно подключить бейдж к ноутбуку по USB. В этом году количество сданных флагов на самый сложный таск превысило наши самые смелые оценки. На это есть две причины: резкий скачок в развитии LLM и уязвимость в сторонней зависимости внутри нашей прошивки — она позволяла прочитать флаги ко всем заданиям без их решения. В этой статье мы раскроем технические детали уязвимости и расскажем, как работал эксплоит участников, который позволил им в сумме получить 91 тысячу офкоинов.

Этот материал мы подготовили еще в конце августа, но опубликовать решили только сейчас. Исследователи Иван Зорин и Евгений Пусан уже выступили с близким к статье докладом «Как отреверсить конференцию: от 11 флагов на бейдже до RCE по USB» на OFFZONE и готовили продолжение к ZeroNights — «Глитчи глитчами, дамп дампом, а все уровни RDP заканчиваются, когда бага на USB-стеке в прошивке попадается». Авторы проделали большую работу, и мы не хотим публиковать свой разбор раньше их доклада, поэтому технический анализ уязвимости в USB-стеке выходит сразу после ZeroNights.  

CVE-2021-38541

В 2025 году мы уже рассказывали, как работает наша прошивка, но повторим здесь основные моменты. Наш бейдж работает на микроконтроллере STM32F103RET6, а для работы с USB мы используем HAL-библиотеку USB CDC, поставляемую вместе с STM32CubeIDE. В процессе анализа эксплуатации уязвимости, которая тогда была нам неизвестна, мы наткнулись на интересный issue в GitHub — репозитории библиотеки от 12 июня 2022 года.

Исследователь szymonh пишет, что в 2021 году обнаружил уязвимость переполнения буфера в USB CDC, для которой зарезервирован номер CVE-2021-38541. Он отрепортил ее в STMicroelectronics, и она была исправлена в версии 2.8.0. Но патч не портировали в библиотеки для микроконтроллеров, поставляемые вместе с STM32CubeIDE. В результате разработчики до сих пор выпускают  уязвимые версии прошивок. С некоторой регулярностью szymonh возвращается в issue и спрашивает, когда же патч наконец портируют. По его словам, на письма с официальной почты служба безопасности продуктов STMicroelectronics не отвечает.

Так продолжалось до 11 мая 2023 года, когда сотрудник службы поддержки клиентов STMicroelectronics Ali Labbene все-таки ответил исследователю. Вендор не планирует переносить патч в библиотеки для STM32Cube, а разработчики, которые хотят выпускать безопасные прошивки, должны сами установить себе последнюю версию из главной ветки репозитория.

Да, вы все верно поняли. В мире сейчас множество микроконтроллеров STM32, использующих уязвимую библиотеку для работы с USB, так как уязвимость обладает свойствами уязвимостей сразу двух типов: нулевого и первого дня. Она одновременно всем известна и при этом почти нигде не запатчена. Кстати, STMicroelectronics все еще не опубликовала CVE-2021-38541: на сайте cve.org она отмечена как зарезервированная. Рассмотрим технические детали этой уязвимости.

Технические детали

При отправке с хоста SETUP-пакета входящий запрос обрабатывает функция USBD_CDC_Setup:

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

static uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev,

                              USBD_SetupReqTypedef *req)

{

  USBD_CDC_HandleTypeDef hcdc = (USBD_CDC_HandleTypeDef )pdev->pClassData;

  uint16_t len;

  uint8_t ifalt = 0U;

  uint16_t status_info = 0U;

  USBD_StatusTypeDef ret = USBD_OK;

  if (hcdc == NULL)

  {

    return (uint8_t)USBD_FAIL;

  }

  switch (req->bmRequest & USB_REQ_TYPE_MASK)

  {

    case USB_REQ_TYPE_CLASS:

      if (req->wLength != 0U)

      {

        if ((req->bmRequest & 0x80U) != 0U)

        {

          ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest,

                                                            (uint8_t *)hcdc->data,

                                                            req->wLength);

          len = MIN(CDC_REQ_MAX_DATA_SIZE, req->wLength);

          (void)USBD_CtlSendData(pdev, (uint8_t *)hcdc->data, len);

        }

        else

        {

          hcdc->CmdOpCode = req->bRequest;

          hcdc->CmdLength = (uint8_t)req->wLength;

          (void)USBD_CtlPrepareRx(pdev, (uint8_t *)hcdc->data, req->wLength);

        }

      }

  ...

Здесь видно, что функциям Control и USBD_CtlPrepareRx передается нигде перед этим не проверенный req->wLength типа uint16_t и указатель на массив hcdc->data. Рассмотрим путь на чтение данных с хоста, функцию USBD_CtlPrepareRx.

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

/**

  * @brief  USBD_CtlPrepareRx

  *         receive data on the ctl pipe

  * @param  pdev: device instance

  * @param  buff: pointer to data buffer

  * @param  len: length of data to be received

  * @retval status

  */

USBD_StatusTypeDef USBD_CtlPrepareRx(USBD_HandleTypeDef *pdev,

                                     uint8_t *pbuf, uint32_t len)

{

  /* Set EP0 State */

  pdev->ep0_state = USBD_EP0_DATA_OUT;

  pdev->ep_out[0].total_length = len;

#ifdef USBD_AVOID_PACKET_SPLIT_MPS

  pdev->ep_out[0].rem_length = 0U;

#else

  pdev->ep_out[0].rem_length = len;

#endif

  /* Start the transfer */

  (void)USBD_LL_PrepareReceive(pdev, 0U, pbuf, len);

  return USBD_OK;

}

Эта функция также не проводит никаких проверок и сразу начинает чтение данных по переданному указателю pbuf. В нашем случае он указывает в поле data структуры USBD_CDC_HandleTypeDef:

#define CDC_DATA_HS_MAX_PACKET_SIZE                 512U  /* Endpoint IN & OUT Packet size */

typedef struct

{

  uint32_t data[CDC_DATA_HS_MAX_PACKET_SIZE / 4U];      /* Force 32bits alignment */

  uint8_t  CmdOpCode;

  uint8_t  CmdLength;

  uint8_t  *RxBuffer;

  uint8_t  *TxBuffer;

  uint32_t RxLength;

  uint32_t TxLength;

  __IO uint32_t TxState;

  __IO uint32_t RxState;

} USBD_CDC_HandleTypeDef;

Таким образом, размер data составляет 512 байт. Однако вспомним, что переданная с хоста длина хранится в переменной типа uint16_t, а это означает, что она может достигать 0xffff (65535). Мы можем передать количество данных, превышающее размер массива, — это уязвимость переполнения буфера.

Эксплуатация уязвимости

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

В структуре USBD_CDC_HandleTypeDef, как указано выше, после поля data лежат указатели на RxBuffer и TxBuffer. В RxBuffer будут записываться данные, полученные после успешно установленного соединения. Мы можем перенаправить этот указатель и таким образом получить примитив arbitrary write. Но куда нам лучше записать данные, чтобы получить исполнение произвольного кода? Здесь мы снова можем обратиться к статье о внутренностях нашего бейджа, которую мы публиковали в 2025 году.

Все CLI-команды в бейдже реализованы в структурах следующего вида:

Мы можем переписать указатель pfnExec для любой из команд на контролируемый нами шелл-код и получить исполнение кода при выполнении этой команды. Здесь очень удобно, что переполняемый нами массив лежит в регионе SRAM, на который установлены права RWX. То есть мы можем прыгнуть ровно на hcdc->data, место которого мы точно знаем, ведь ASLR отсутствует. Осталось только найти функцию, которая расшифрует и выведет нам все флаги, и вызвать ее. По строкам искать ее очень просто:

int __fastcall sub_800E218(int a1)

{

  int v2; // r0

  const char *v3; // r4

  v2 = sub_801D688(128, 1);

  if ( !v2 )

    return sub_8023B90("\r\nError getting flag\r");

  v3 = (const char *)v2;

  sub_8023B90("\r\nPlease wait...\r");

  if ( sub_800E1A8(a1, v3) == 1 )

    sub_8023A48("\r\nHere is your flag: %s\r\n", v3);

  else

    sub_8023B90("\r\nSomething is wrong with flag\r");

  return sub_801D6BC(v3);

}

Эта функция принимает индекс флага. Для эксплуатации нам нужно вызвать ее из шелл-кода в цикле 8 раз — именно столько флагов хранилось в бейдже.

Эксплуатация уязвимости участниками OFFZONE 2026

В первый день несколько участников сдампили прошивку при помощи уязвимости семейства микроконтроллеров STM32F1. Затем они отдали полученную прошивку LLM-агентам Codex и Claude, те обнаружили CVE-2021-38541 и собрали эксплоит для получения всех флагов.

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

При этом дамп лишь способ добраться до содержимого, а ценность представляет найденная внутри уязвимость. Именно она требует ответственного разглашения. Мы выступаем за эту политику и призываем к тому же сообщество исследователей и посетителей OFFZONE. Если бы исследователи обратились к нам с обнаруженной уязвимостью, мы бы, без сомнения, наградили их.

К сожалению, эксплоит разошелся среди участников до окончания дня: достаточно было подключить бейдж к ноутбуку по USB, чтобы получить все флаги уже без глитчинга. Это обрушило экономику первого дня и вынудило нас закрыть прием флагов по бейджу в 15:31:20. Всего самый сложный флаг, недоступный без дампа прошивки, сдал 51 посетитель.

Мы разобрали эту атаку по шагам и учтем выводы в прошивке следующего года. Тем, кто взломает бейдж и ответственно сообщит нам о находке, дадим приятный бонус. До встречи на OFFZONE 2027!

Завершим эту статью так, как в 2001 году свое эссе It's Time to End Information Anarchy об ответственном раскрытии уязвимостей завершил Скотт Калп, сотрудник Microsoft Security Response Center: «It's time for the security community to get on the right side of this issue».

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.