Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
Herramientas/GitHubGitHub/rva3/cve-2026-40003
Seguridad de Sistemas EmbebidosExplotaciónIngeniería InversaHacking de HardwareDesarrollo de PayloadsAnálisis de FirmwareExplotación de Binarios
GitHubrva3/cve-2026-40003

CVE-2026-40003

Exploit para CVE-2026-40003, una vulnerabilidad de escritura arbitraria de memoria en el BootROM del SoC ZXIC/Sanechips ZX297520V3, que permite la ejecución de código a través del modo de descarga USB.

Ver RepositorioSitio web
141hace 3 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2026-40003, también conocido como Joselito, es una vulnerabilidad de escritura arbitraria en memoria encontrada en el BootROM del SoC ZXIC/Sanechips ZX297520V3.

[!CAUTION] Este PoC ha sido publicado únicamente con fines de investigación. NO debe utilizar este exploit con fines maliciosos. NO utilice el exploit en dispositivos que no sean de su propiedad o sin el consentimiento explícito del propietario.

Estado

Esta vulnerabilidad debería estar corregida desde marzo de 2026 en las revisiones más recientes del SoC. Aún no se conocen productos que utilicen chips parcheados. Todos los dispositivos existentes son vulnerables.

¿Cómo funciona?

TL;DR: consulte el código del loader.

El BootROM comienza en la dirección 0x4 con el puntero de pila en 0x81FF0. El código realiza la selección de modo basándose en un punto de prueba especial o en la capacidad de cargar la imagen desde la flash. Si el BootROM no puede cargar o verificar la imagen en la flash, recurrirá al modo de descarga por USB.

Podemos observar este comportamiento aquí:

root@kitploit:~
v0 = MEMORY[0x13B004] & 7; // pin de hardware
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;

La función usbdl_init inicializa el IP USB y entra en el bucle de descarga:

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);
}

Salida del desensamblador:

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

La función try_sync_with_host espera el byte de sincronización (0x5A) y entra en el bucle principal.

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 enviado por el host
  {
    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);
  }
}

Una vez completado el handshake, el BootROM entra en el bucle principal, esperando el binario de la etapa 1, que normalmente se utiliza para recuperar un dispositivo bricked. Sin embargo, el BootROM no verifica la dirección de destino, lo que permite la escritura arbitraria mediante el comando 0x7A. El estado 0xA1 significa que la dirección y el tamaño de la imagen fueron aceptados.

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;

La función verify_and_jump realizará la verificación de la imagen si el dispositivo está fusionado.

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;
}

La función verify utilizará los motores de hardware MD5 y RSA para verificar la integridad de la imagen, y jump establecerá el puntero de pila en la dirección del encabezado de la imagen y saltará al punto de entrada.

En la convención de llamadas ARM/Thumb, las llamadas a funciones suelen comenzar con PUSH {regs, LR} y terminan con una llamada tail, BX LR o POP {regs, PC}. La función usbdl_init no fue optimizada por el compilador como llamada tail, por lo que contiene instrucciones PUSH y POP.

¿Ya lo has entendido?


Dado que el BootROM no tiene un indicador para la descarga única, podemos enviar tantas imágenes como queramos. El exploit abusa de esta característica (aunque no es una vulnerabilidad en sí misma) junto con la escritura arbitraria en memoria.

  1. Enviar el payload a la dirección especificada
  2. Calcular el desplazamiento de pila: 6 (función entry: PUSH {R3-R7,LR}) + 2 (función usbdl_init: PUSH {R4,LR}). Multiplicar por el tamaño del registro, es decir, 8 * 4 = 32
  3. Enviar la dirección de salto al registro LR guardado en la pila (por simplicidad, el PoC repite la misma dirección para todos los registros guardados)
  4. Enviar el comando de salto
  5. Dejar que la verificación de la imagen falle (falta el magic del encabezado)
  6. La función verify_and_jump retorna a usbdl_init
  7. usbdl_init imprime FAILED y ejecuta la instrucción POP {R4, PC}, ejecutando el payload

Para compilar desde el código fuente

  • Instale curl, llvm-objcopy (normalmente parte del paquete llvm) y gcc. Ejemplo para Ubuntu 22.04: sudo apt update && sudo apt install curl llvm gcc
  • Instale rustup usando curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh o el gestor de paquetes específico de su distribución
  • Asegúrese de que los comandos de rust estén disponibles: . "$HOME/.cargo/env" (bash) o source "$HOME/.cargo/env.fish" (fish)
  • Compile el payload: cd payload && ./build.sh && cd ..
  • Ejecute la herramienta host: sudo -E cargo r -p loader -- target/thumbv6m-none-eabi/release/payload

Ahora conecte el dispositivo con el modo de descarga USB activado (ya sea mediante el punto de prueba especial o una flash corrupta) y observe la salida UART.

Cronología

  • 5 de febrero - Informe inicial
  • 18 de marzo - Confirmado por el ZTE PSIRT
  • 7 de mayo - Publicación pública

Créditos externos

  • Gran parte de la ingeniería inversa del SoC: Stefan Dösinger
  • Prueba en dispositivo fusionado: Mio Naganohara
  • Prueba previa al lanzamiento en otro dispositivo fusionado: exp-3

Licencia

AGPLv3

Descargar herramienta