Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2026-40003 — CVE-2026-40003 的漏洞利用程序,针对 ZXIC/Sanechips ZX297520V3 SoC BootROM 中的任意内存写入漏洞,可通过 USB 下载模式实现代码执行。 | Kitploit
工具/GitHubGitHub/rva3/cve-2026-40003
嵌入式系统安全漏洞利用逆向工程硬件黑客Payload 开发固件分析二进制利用
GitHubrva3/cve-2026-40003

CVE-2026-40003

CVE-2026-40003 的漏洞利用程序,针对 ZXIC/Sanechips ZX297520V3 SoC BootROM 中的任意内存写入漏洞,可通过 USB 下载模式实现代码执行。

查看仓库
1413个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
网站

CVE-2026-40003,又名 Joselito,是在 ZXIC/Sanechips ZX297520V3 SoC BootROM 中发现的一个任意内存写入漏洞。

[!CAUTION] 此 PoC 仅用于研究目的发布。您不得将此漏洞利用用于任何恶意目的。请勿在您不拥有的设备上或未经所有者明确同意的情况下使用此漏洞利用。

状态

自 2026 年 3 月起,该漏洞应在较新的 SoC 修订版中得到修复。目前尚无已知产品使用已修补的芯片。所有现有设备均存在漏洞。

它是如何工作的?

TL;DR:查看 loader 代码。

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 = 主机发送的确认
  {
    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
  • 使用 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 或发行版特定的包管理器安装 rustup
  • 确保 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 输出。

时间线

  • 2 月 5 日 - 初始报告
  • 3 月 18 日 - 由 ZTE PSIRT 确认
  • 5 月 7 日 - 公开发布

外部致谢

  • 大量 SoC 逆向工程:Stefan Dösinger
  • 在熔丝设备上的测试:Mio Naganohara
  • 在另一台熔丝设备上的预发布测试:exp-3

许可证

AGPLv3

下载工具