Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-40003 — Эксплойт для CVE-2026-40003 — уязвимости произвольной записи в память в BootROM SoC ZXIC/Sanechips ZX297520V3, позволяющей выполнение кода через USB-режим загрузки. | Kitploit
Инструменты/GitHubGitHub/rva3/cve-2026-40003
Безопасность встроенных системЭксплуатацияОбратная инженерияАппаратный ХакингРазработка Полезной НагрузкиАнализ ПрошивокЭксплуатация Бинарных Файлов
GitHubrva3/cve-2026-40003

CVE-2026-40003

Эксплойт для CVE-2026-40003 — уязвимости произвольной записи в память в BootROM SoC ZXIC/Sanechips ZX297520V3, позволяющей выполнение кода через USB-режим загрузки.

Репозиторий
14194 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться
Сайт

CVE-2026-40003, также известная как Joselito, — это уязвимость произвольной записи в память, обнаруженная в BootROM SoC ZXIC/Sanechips ZX297520V3.

[!CAUTION] Этот PoC опубликован исключительно в исследовательских целях. Вы НЕ должны использовать этот эксплойт в каких-либо вредоносных целях. НЕ используйте эксплойт на устройствах, которые вам не принадлежат, или без явного согласия владельца.

Статус

Эта уязвимость должна быть исправлена с марта 2026 года в более новых ревизиях SoC. Известных продуктов с исправленными чипами пока нет. Все существующие устройства уязвимы.

Как это работает?

TL;DR: смотрите код загрузчика.

BootROM начинается с адреса 0x4 с указателем стека на 0x81FF0. Код выполняет выбор режима на основе специальной тестовой точки или возможности загрузить образ из флеш-памяти. Если BootROM не может загрузить или проверить образ во флеш-памяти, он переключается в режим загрузки через USB.

Мы можем наблюдать это поведение здесь:

root@kitploit:~
v0 = MEMORY[0x13B004] & 7; // аппаратный пин
switch ( (char)v0 )
{
  case 0:
    if ( nand_init() )
      goto LABEL_12;
    parse_header((Header *)0x8A000);
    v4 = "\nMODE:NAND\n";
    goto LABEL_11;
  case 1:
    if ( !nand_init() )
    {
      parse_header((Header *)0x8A000);
      uart_puts("\nMODE:USB-N\n");
    }
    usbdl_init(0x1500000);
    v5 = "TURN TO NAND\n";
    goto LABEL_16;
/* ... */
LABEL_16:
  uart_puts(v5);
LABEL_7:
  verify_and_jump((Header *)0x8A000, 0, 1);
  break;

Функция usbdl_init инициализирует USB-IP и входит в цикл загрузки:

root@kitploit:~
void __fastcall usbdl_init(int usb_base)
{
  char *v1; // r0

  if ( usb_base == 0x1500000 )
  {
    alt_usb_base = 0;
    usb_init();
  }
  else
  {
    alt_usb_base = 1;
    noidea();
  }
  if ( usb_setup() )
  {
    try_sync_with_host();
    v1 = "FAILED\n";
  }
  else
  {
    v1 = "\nNOLINK\n";
  }
  uart_puts(v1);
}

Вывод дизассемблера:

root@kitploit:~
ROM:00001F68 ; void __fastcall usbdl_init(int usb_base)
ROM:00001F68 usbdl_init                              ; CODE XREF: entry+5E↑p
ROM:00001F68                                         ; entry+7C↑p ...
ROM:00001F68                 PUSH            {R4,LR}
ROM:00001F6A                 MOVS            R2, #0x1500000
ROM:00001F6E                 LDR             R1, =dword_8014C
ROM:00001F70                 MOV             R4, R0
ROM:00001F72                 CMP             R0, R2
ROM:00001F74                 BNE             loc_1F80
ROM:00001F76                 MOVS            R0, #0
ROM:00001F78                 STRB            R0, [R1,#(alt_usb_base - 0x8014C)]
ROM:00001F7A                 BL              usb_init
ROM:00001F7E                 B               loc_1F88
ROM:00001F80 ; ---------------------------------------------------------------------------
ROM:00001F80
ROM:00001F80 loc_1F80                                ; CODE XREF: usbdl_init+C↑j
ROM:00001F80                 MOVS            R0, #1
ROM:00001F82                 STRB            R0, [R1,#(alt_usb_base - 0x8014C)]
ROM:00001F84                 BL              noidea
ROM:00001F88
ROM:00001F88 loc_1F88                                ; CODE XREF: usbdl_init+16↑j
ROM:00001F88                 MOV             R0, R4
ROM:00001F8A                 BL              usb_setup
ROM:00001F8E                 CMP             R0, #0
ROM:00001F90                 BEQ             loc_1F9E
ROM:00001F92                 BL              try_sync_with_host
ROM:00001F96                 ADR             R0, aFailed ; "FAILED\n"
ROM:00001F98
ROM:00001F98 loc_1F98                                ; CODE XREF: usbdl_init+38↓j
ROM:00001F98                 BL              uart_puts
ROM:00001F9C                 POP             {R4,PC}
ROM:00001F9E ; ---------------------------------------------------------------------------
ROM:00001F9E
ROM:00001F9E loc_1F9E                                ; CODE XREF: usbdl_init+28↑j
ROM:00001F9E                 ADR             R0, aNolink ; "\nNOLINK\n"
ROM:00001FA0                 B               loc_1F98
ROM:00001FA0 ; End of function usbdl_init

Функция try_sync_with_host ожидает синхронизирующий байт (0x5A) и входит в основной цикл.

root@kitploit:~
void try_sync_with_host()
{
  int v0; // r4
  unsigned int i; // r0
  int v2; // r5
  int v3; // r2
  int v4; // r3
  int v5; // r2
  int v6; // r3
  int v7; // r0

  v0 = 0x805A0;
  time_to_boot = 0;
  for ( i = 0; i < 0x40; ++i )
    *(_BYTE *)(i + 0x805A0) = 0;
  g_state = 0;
  v2 = 0x200;
  if ( usb_recv(byte_805A0, 0x200, 0x5A) == 1 ) // 0x5a = ack, отправленный хостом
  {
    uart_puts("TIMEOUT\n");
  }
  else
  {
    resp[0] = 0xA5;
    usb_send((int)resp, 1, v3, v4);
    g_state = 1;
    while ( !time_to_boot )
    {
      v7 = sub_11CA(v0, v2, v5, v6);
      handler(v0, v7);
      if ( g_state == 4 )
      {
        v2 = size;
        v0 = start_addr;
      }
      else
      {
        v0 = 0x805A0;
        v2 = 0x200;
      }
    }
    verify_and_jump(unk_80160, 0, 1);
  }
}

После завершения рукопожатия BootROM входит в основной цикл, ожидая бинарный файл стадии 1, который обычно используется для восстановления «окирпиченного» устройства. Однако BootROM не проверяет адрес назначения, что позволяет выполнять произвольную запись через команду 0x7A. Статус 0xA1 означает, что адрес и размер образа были приняты.

root@kitploit:~
usb_send:
        resp[0] = status;
        v5 = 1;
        v6 = resp;
        return usb_send((int)v6, v5, state, v3);
      case 3u: // 0x7A
        if ( *(int *)&resp[4] >= 4 )
        {
          if ( *(int *)&resp[4] < 8 )
            *(_DWORD *)&resp[12] = (*(_DWORD *)&resp[12] << 8) | result;
        }
        else
        {
          *(_DWORD *)&resp[8] = (*(_DWORD *)&resp[8] << 8) | result;
        }
        v3 = *(_DWORD *)&resp[4] + 1;
        *(_DWORD *)&resp[4] = v3;
        if ( v3 != 8 )
          continue;
        *(_DWORD *)&resp[4] = 0;
        start_addr = *(_DWORD *)&resp[8];
        size = *(_DWORD *)&resp[12];
        g_state = 4;
        status = 0xA1;
        goto usb_send;

Функция verify_and_jump выполнит проверку образа, если устройство имеет предохранители.

root@kitploit:~
int __fastcall verify_and_jump(Header *a1, int zero, int must_verify)
{
  int *p_sp; // r6
  int result; // r0

  p_sp = (int *)&a1->sp;
  result = memcmp((int)a1->magic, 0x80008, 2u);
  if ( !result )
  {
    if ( !zero || (result = a1->usbdl_en, result == 0x5A) )
    {
      if ( !must_verify )
        return jump(p_sp);
      result = verify(a1);
      if ( !result )
        return jump(p_sp);
    }
  }
  return result;
}

Функция verify использует аппаратные движки MD5 и RSA для проверки целостности образа, а jump установит указатель стека на адрес из заголовка образа и перейдёт к точке входа.

В соглашении о вызовах ARM/Thumb вызовы функций обычно начинаются с PUSH {regs, LR} и заканчиваются хвостовым вызовом, BX LR или POP {regs, PC}. usbdl_init не был оптимизирован компилятором как хвостовой вызов, поэтому содержит инструкции PUSH и POP.

Уже поняли идею?


Поскольку BootROM не имеет флага для однократной загрузки, мы можем отправлять сколько угодно образов. Эксплойт злоупотребляет этой функцией (хотя сама по себе она не является уязвимостью) вместе с произвольной записью в память.

  1. Отправьте полезную нагрузку по указанному адресу
  2. Вычислите смещение стека: 6 (функция entry: PUSH {R3-R7,LR}) + 2 (функция usbdl_init: PUSH {R4,LR}). Умножьте на размер регистра: 8 * 4 = 32
  3. Отправьте адрес перехода в сохранённый регистр LR в стеке (для простоты PoC повторяет один и тот же адрес для всех сохранённых регистров)
  4. Отправьте команду перехода
  5. Дайте проверке образа завершиться неудачей (отсутствует магический заголовок)
  6. Функция verify_and_jump возвращается в usbdl_init
  7. usbdl_init выводит FAILED и выполняет инструкцию POP {R4, PC}, запуская полезную нагрузку

Сборка из исходников

  • Установите curl, llvm-objcopy (обычно входит в пакет llvm) и gcc. Пример для Ubuntu 22.04: sudo apt update && sudo apt install curl llvm gcc
  • Установите rustup с помощью curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh или через пакетный менеджер вашего дистрибутива
  • Убедитесь, что команды rust доступны: . "$HOME/.cargo/env" (bash) или source "$HOME/.cargo/env.fish" (fish)
  • Соберите полезную нагрузку: cd payload && ./build.sh && cd ..
  • Запустите инструмент хоста: sudo -E cargo r -p loader -- target/thumbv6m-none-eabi/release/payload

Теперь подключите устройство с активированным режимом загрузки через USB (либо специальная тестовая точка, либо повреждённая флеш-память) и наблюдайте за выводом UART.

Хронология

  • 5 февраля — Первоначальное сообщение
  • 18 марта — Подтверждено ZTE PSIRT
  • 7 мая — Публичный релиз

Внешние благодарности

  • Большой объём реверс-инжиниринга SoC: Stefan Dösinger
  • Тест на устройстве с предохранителями: Mio Naganohara
  • Предрелизный тест на другом устройстве с предохранителями: exp-3

Лицензия

AGPLv3

Скачать инструмент