Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2026-40003 — Exploit für CVE-2026-40003, eine Schwachstelle für beliebiges Speicherschreiben im BootROM des ZXIC/Sanechips ZX297520V3 SoC, die Codeausführung über den USB-Download-Modus ermöglicht. | Kitploit
Tools/GitHubGitHub/rva3/cve-2026-40003
Embedded-System-SicherheitExploitationReverse EngineeringHardware-HackingPayload-EntwicklungFirmware-AnalyseBinary-Exploitation
GitHubrva3/cve-2026-40003

CVE-2026-40003

Exploit für CVE-2026-40003, eine Schwachstelle für beliebiges Speicherschreiben im BootROM des ZXIC/Sanechips ZX297520V3 SoC, die Codeausführung über den USB-Download-Modus ermöglicht.

Repository anzeigen
1419vor 4 MonatenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen
Webseite

CVE-2026-40003 alias Joselito ist eine Schwachstelle mit beliebigem Speicherschreiben (arbitrary memory write), die im BootROM des ZXIC/Sanechips ZX297520V3-SoCs gefunden wurde.

[!CAUTION] Dieses PoC wurde nur zu Forschungszwecken veröffentlicht. Du darfst diesen Exploit NICHT für böswillige Zwecke verwenden. Verwende den Exploit NICHT auf Geräten, die dir nicht gehören, oder ohne die ausdrückliche Zustimmung des Eigentümers.

Status

Diese Schwachstelle sollte seit März 2026 in neueren SoC-Revisionen behoben sein. Es sind noch keine Produkte bekannt, die gepatchte Chips verwenden. Alle vorhandenen Geräte sind verwundbar.

Wie funktioniert es?

TL;DR: Schau dir den Loader-Code an.

Das BootROM startet bei Adresse 0x4 mit dem Stack-Pointer bei 0x81FF0. Der Code führt eine Modusauswahl basierend auf einem speziellen Testpunkt oder der Fähigkeit, das Image vom Flash zu laden, durch. Wenn das BootROM das Image auf dem Flash nicht laden oder verifizieren kann, fällt es in den USB-Download-Modus zurück.

Wir können dieses Verhalten hier beobachten:

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;

Die Funktion usbdl_init initialisiert die USB-IP und tritt in die Download-Schleife ein:

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

Disassembler-Ausgabe:

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

Die Funktion try_sync_with_host wartet auf das Sync-Byte (0x5A) und tritt in die Hauptschleife ein.

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 sent by the 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);
  }
}

Nach Abschluss des Handshakes tritt das BootROM in die Hauptschleife ein und wartet auf das Stage-1-Binary, das normalerweise zur Wiederherstellung eines gebrickten Geräts verwendet wird. Das BootROM verifiziert jedoch nicht die Zieladresse, was beliebiges Schreiben über den 0x7A-Befehl ermöglicht. Der Status 0xA1 bedeutet, dass die Image-Adresse und -Größe akzeptiert wurden.

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;

Die Funktion verify_and_jump führt eine Image-Verifizierung durch, wenn das Gerät gefused ist.

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

Die Funktion verify verwendet Hardware-MD5- und RSA-Engines, um die Integrität des Images zu verifizieren, und jump setzt den Stack-Pointer auf die Adresse aus dem Image-Header und springt zum Entrypoint.

In der ARM/Thumb-Aufrufkonvention beginnen Funktionsaufrufe normalerweise mit PUSH {regs, LR} und enden mit einem Tail Call, BX LR oder POP {regs, PC}. Die Funktion usbdl_init wurde vom Compiler nicht als Tail Call optimiert und enthält daher PUSH- und POP-Anweisungen.

Hast du die Idee schon verstanden?


Da das BootROM kein Flag für einen einmaligen Download hat, können wir so viele Images senden, wie wir möchten. Der Exploit missbraucht diese Funktion (obwohl sie selbst keine Schwachstelle ist) zusammen mit dem beliebigen Speicherschreiben.

  1. Sende die Payload an die angegebene Adresse
  2. Berechne den Stack-Offset: 6 (entry-Funktion: PUSH {R3-R7,LR}) + 2 (usbdl_init-Funktion: PUSH {R4,LR}). Multipliziere das mit der Registergröße, also 8 * 4 = 32
  3. Sende die Sprungadresse an das gespeicherte LR-Register auf dem Stack (der Einfachheit halber wiederholt das PoC dieselbe Adresse für alle gespeicherten Register)
  4. Sende den Sprungbefehl
  5. Lasse die Image-Verifizierung fehlschlagen (fehlende Header-Magic)
  6. Die Funktion verify_and_jump kehrt zu usbdl_init zurück
  7. usbdl_init gibt FAILED aus und führt die POP {R4, PC}-Anweisung aus, wodurch die Payload ausgeführt wird

Aus dem Quellcode bauen

  • Installiere curl, llvm-objcopy (normalerweise Teil des llvm-Pakets) und gcc. Beispiel für Ubuntu 22.04: sudo apt update && sudo apt install curl llvm gcc
  • Installiere rustup mit curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh oder über den distributionsspezifischen Paketmanager
  • Stelle sicher, dass die Rust-Befehle verfügbar sind: . "$HOME/.cargo/env" (bash) oder source "$HOME/.cargo/env.fish" (fish)
  • Baue die Payload: cd payload && ./build.sh && cd ..
  • Führe das Host-Tool aus: sudo -E cargo r -p loader -- target/thumbv6m-none-eabi/release/payload

Verbinde nun das Gerät mit ausgelöstem USB-Download-Modus (entweder über einen speziellen Testpunkt oder beschädigten Flash) und beobachte die UART-Ausgabe.

Zeitplan

    1. Februar - Erster Bericht
    1. März - Von der ZTE PSIRT bestätigt
    1. Mai - Öffentliche Veröffentlichung

Externe Credits

  • Viel SoC-Reverse-Engineering: Stefan Dösinger
  • Test auf gefusetem Gerät: Mio Naganohara
  • Vorab-Test auf einem anderen gefuseten Gerät: exp-3

Lizenz

AGPLv3

Tool herunterladen