Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-40003 — Exploit per CVE-2026-40003, una vulnerabilità di scrittura arbitraria in memoria nel BootROM del SoC ZXIC/Sanechips ZX297520V3, che consente l'esecuzione di codice tramite la modalità di download USB. | Kitploit
Strumenti/GitHubGitHub/rva3/cve-2026-40003
Sicurezza Sistemi EmbeddedExploitReverse EngineeringHacking HardwareSviluppo PayloadAnalisi del FirmwareBinary Exploitation
GitHubrva3/cve-2026-40003

CVE-2026-40003

Exploit per CVE-2026-40003, una vulnerabilità di scrittura arbitraria in memoria nel BootROM del SoC ZXIC/Sanechips ZX297520V3, che consente l'esecuzione di codice tramite la modalità di download USB.

Vedi Repository
1413 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
Sito web

CVE-2026-40003, noto anche come Joselito, è una vulnerabilità di scrittura arbitraria in memoria trovata nel BootROM del SoC ZXIC/Sanechips ZX297520V3.

[!CAUTION] Questo PoC è stato pubblicato solo a scopo di ricerca. NON devi usare questo exploit per scopi dannosi. NON usare l'exploit su dispositivi che non possiedi o senza il consenso esplicito del proprietario.

Stato

Questa vulnerabilità dovrebbe essere corretta da marzo 2026 nelle revisioni più recenti del SoC. Non ci sono ancora prodotti noti che utilizzano chip patchati. Tutti i dispositivi esistenti sono vulnerabili.

Come funziona?

TL;DR: dai un'occhiata al codice del loader.

Il BootROM inizia all'indirizzo 0x4 con lo stack pointer a 0x81FF0. Il codice esegue la selezione della modalità in base a uno speciale test point, o alla capacità di caricare l'immagine dalla flash. Se il BootROM non riesce a caricare o verificare l'immagine sulla flash, ripiega sulla modalità di download USB.

Possiamo osservare questo comportamento qui:

root@kitploit:~
v0 = MEMORY[0x13B004] & 7; // hardware pin
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 funzione usbdl_init inizializza l'IP USB ed entra nel loop di download:

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

Output del disassembler:

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 funzione try_sync_with_host attende il byte di sincronizzazione (0x5A) ed entra nel loop principale.

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 inviato dall'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);
  }
}

Dopo che l'handshake è completato, il BootROM entra nel loop principale, in attesa del binario di stage 1, che viene tipicamente usato per recuperare un dispositivo brickato. Tuttavia, il BootROM non verifica l'indirizzo di destinazione, il che consente la scrittura arbitraria tramite il comando 0x7A. Lo stato 0xA1 significa che l'indirizzo e la dimensione dell'immagine sono stati accettati.

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 funzione verify_and_jump eseguirà la verifica dell'immagine se il dispositivo è fuso.

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 funzione verify userà i motori hardware MD5 e RSA per verificare l'integrità dell'immagine, e jump imposterà lo stack pointer all'indirizzo dall'header dell'immagine e salterà all'entrypoint.

Nella convenzione di chiamata ARM/Thumb, le chiamate di funzione di solito iniziano con PUSH {regs, LR} e terminano con una tail call, BX LR o POP {regs, PC}. La funzione usbdl_init non è stata ottimizzata dal compilatore come tail call, quindi contiene istruzioni PUSH e POP.

Hai già capito l'idea?


Poiché il BootROM non ha un flag per il download una tantum, possiamo inviare quante immagini vogliamo. L'exploit abusa di questa funzionalità (anche se di per sé non è una vulnerabilità) con la scrittura arbitraria in memoria.

  1. Invia il payload all'indirizzo specificato
  2. Calcola l'offset dello stack: 6 (funzione entry: PUSH {R3-R7,LR}) + 2 (funzione usbdl_init: PUSH {R4,LR}). Moltiplica per la dimensione del registro, quindi 8 * 4 = 32
  3. Invia l'indirizzo di salto al registro LR salvato sullo stack (per semplicità il PoC ripete lo stesso indirizzo per tutti i registri salvati)
  4. Invia il comando di salto
  5. Lascia fallire la verifica dell'immagine (manca il magic dell'header)
  6. La funzione verify_and_jump ritorna a usbdl_init
  7. usbdl_init stampa FAILED ed esegue l'istruzione POP {R4, PC}, eseguendo il payload

Per compilare dal sorgente

  • Installa curl, llvm-objcopy (di solito parte del pacchetto llvm) e gcc. Esempio per Ubuntu 22.04: sudo apt update && sudo apt install curl llvm gcc
  • Installa rustup usando curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh o il gestore pacchetti specifico della distribuzione
  • Assicurati che i comandi rust siano disponibili: . "$HOME/.cargo/env" (bash) o source "$HOME/.cargo/env.fish" (fish)
  • Compila il payload: cd payload && ./build.sh && cd ..
  • Esegui lo strumento host: sudo -E cargo r -p loader -- target/thumbv6m-none-eabi/release/payload

Ora collega il dispositivo con la modalità di download USB attivata (tramite speciale test point o flash corrotta) e osserva l'output UART.

Cronologia

  • 5 feb - Report iniziale
  • 18 mar - Confermato dal ZTE PSIRT
  • 7 mag - Rilascio pubblico

Crediti esterni

  • Molta reverse engineering del SoC: Stefan Dösinger
  • Test su dispositivo fuso: Mio Naganohara
  • Test pre-rilascio su un altro dispositivo fuso: exp-3

Licenza

AGPLv3

Scarica lo strumento