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

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

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

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

工具目录

分类

查看所有分类
Loading categories
Tenda-Router-VR-and-Exploit — 针对Tenda AC15路由器固件重托管和远程命令执行(CVE-2020-10987)漏洞利用复现的编写指南。 | Kitploit
工具/GitHubGitHub/jaden-bowers/tenda-router-vr-and-exploit
嵌入式系统安全物联网安全漏洞分析漏洞利用逆向工程渗透测试硬件安全学习与教育固件分析

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
二进制利用
实验室与实践
GitHubjaden-bowers/tenda-router-vr-and-exploit

Tenda-Router-VR-and-Exploit

针对Tenda AC15路由器固件重托管和远程命令执行(CVE-2020-10987)漏洞利用复现的编写指南。

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

Tenda-Router-VR-and-Exploit

这篇说明详细展示了我如何仿真AC15 (V15.03.05.19)固件的Web服务器,使用QEMU,使其可以从主机浏览器访问,并利用存在漏洞的/goform/setUsbUnload处理程序(CVE-2020-10987)在仿真的根文件系统中执行命令。


概述

  • 从固件镜像中提取(在我们的案例中是获取)squashfs文件系统并开始逆向分析
  • 构建一个在qemu-system-arm下运行的Debian armhf虚拟机
  • 设置虚拟机的网络和端口转发路径到仿真的路由器
  • 构建我们的启动脚本
  • 攻击仿真的路由器

提取文件系统

首先我们需要获取固件镜像。我未能找到AC15 V15.03.05.19的固件镜像下载链接,但我找到了一个GitHub仓库,其中包含了已提取的squashfs文件系统。 如果我们有正确的镜像文件,我们可以使用binwalk像这样提取它,``` user@computer $ binwalk -e AC15_V15.03.05.19.bin

DECIMAL HEXADECIMAL DESCRIPTION

64 0x40 TRX firmware header, little endian, image size: 6778880 bytes, CRC32: 0x80AD82D6, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x1A488C, rootfs offset: 0x0 92 0x5C LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4177792 bytes 1722572 0x1A48CC Squashfs filesystem, little endian, version 4.0, compression:xz, size: 5052332 bytes, 848 inodes, blocksize: 131072 bytes, created: 2017-04-19 16:18:08

user@computer $ cd _AC15_V15.03.05.19.bin.extracted

由于我们没有正确的镜像文件,但有一个已经提取了文件系统的仓库,我们可以直接克隆该仓库。```
git clone https://github.com/lapinpt/Tenda-AC15-Firmware-V15.03.05.19-9061

我创建了一个名为 VR 的目录,并将此仓库克隆到其中。我的 rootfs 位于 $HOME/VR/Tenda-AC15-Firmware-V15.03.05.19-9061/rootfs

很好,现在我们有了 AC15 V15.03.05.19 固件。接下来进行一些逆向分析,看看情况如何。

逆向工程

我将使用 Ghidra 11.4.2 进行逆向分析。 让我们导航到目标二进制文件 rootfs/bin/httpd,并将其加载到 Ghidra 中。 如果我们转到 formsetUsbUnload 函数,可以看到,```C uVar1 = FUN_0002bd4c(param_1,"deviceName",&DAT_000f4bdc); doSystemCmd("cfm post netctrl %d?op=%d,string_info=%s",0x33,3,uVar1); FUN_0002c6cc(param_1,"HTTP/1.0 200 OK\r\n\r\n"); FUN_0002c6cc(param_1,"{"errCode":0}"); FUN_0002cc14(param_1,200); return;

这是漏洞所在。`deviceName`参数被直接传递给`doSystemCmd`,从而允许我们发送任意命令。

既然我们要使用qemu重放此固件,而非原始路由器硬件,一些程序会尝试访问不存在的设备,导致我们的启动过程崩溃。
由于我们的目标是利用Web服务器(`httpd`),我只专注于重放该二进制文件,而不是整个启动过程(`/rootfs/etc_ro/init.d/rcS`)。_事后看来,我不确定这是否是正确做法_

因此,在查看`rcS`时,我想找出`rcS`可能做的、而`httpd`可能需要的内容。在文件末尾附近,我们可以看到:```bash
cfmd &
echo '' > /proc/sys/kernel/hotplug
udevd &
logserver &

rcS 在其余堆栈启动之前,在后台启动 cfmd。 经过更多研究并查看易受攻击的函数如何发送命令后,我得出的结论是:

  • httpd 构建一个 cfm post
  • 然后 cfm 客户端通过 UNIX 域套接字(例如 /var/cfm_socket)与 cfmd 通信

在 cfmd 的 InitServer 例程中,我们可以看到```C unlink("/var/cfm_socket"); strncpy(sa_unix.sun_path, "/var/cfm_socket", ...); bind(fd, (sockaddr*)&sa_unix, 0x6e); listen(fd, 5);

它创建一个 UIX 套接字并进行监听。
总体工作方式是,处理程序从每个客户端读取一个固定的 0x7e0 字节帧(`RecvMsg`/`SendMsg`)。前 4 个字节是命令代码;然后是一个 512 字节的键缓冲区和 1500 字节的值缓冲区(你可以在处理程序中看到栈对象的大小)。它根据操作码进行切换并回复一个 ACK 代码:
- `2` -> Get: `GetCfmValue(key, value)` 然后回复代码 `3`
- `0` -> Set: `SetCfmValue(key, value)` 然后回复代码 `1`
- `0x11` -> Unset: `UnSetCfmValue(key)` 然后回复代码 `0x12`
- `10` -> Commit: `SaveCfm2Flash()` 然后回复 `0x10`(成功)或 `0xB`(错误)

因此,为了模拟 `cfmd`,我创建了一个简短的脚本 `cfm_stub````C
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>

#define SOCK_PATH "/var/cfm_socket"

// minimal UNIX-domain server that httpd expects.
// Replies with an IP string when it sees the key it asks for.
int main(void) {
  int s = socket(AF_UNIX, SOCK_STREAM, 0);
  struct sockaddr_un addr = {0};
  if (s < 0) { perror("socket"); return 1; }
  unlink(SOCK_PATH);
  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path)-1);
  if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind"); return 1; }
  if (listen(s, 5) < 0) { perror("listen"); return 1; }

  for (;;) {
    int c = accept(s, NULL, NULL);
    if (c < 0) { if (errno==EINTR) continue; perror("accept"); break; }
    char buf[1024]; ssize_t n = read(c, buf, sizeof(buf));
    if (n > 0) {
      // In some builds httpd asks for "lan.webiplansslen" etc.
      // Any non-empty reply that looks like an IP keeps init happy.
      const char *reply = "192.168.0.1";
      write(c, reply, strlen(reply));
    }
    close(c);
  }
  close(s);
  return 0;
}

我将在下一部分之后展示如何编译它。

下一个辅助文件是 hooks.so。有几个在 httpd 和 cfm 中使用的函数尝试与不存在的硬件交互。

以下是我们的程序启动时假设的内容:

  • Flash + MTD 分区存在并且可挂载。
  • 存在 NVRAM 设备 (/dev/nvram) 并返回合理的默认值。
  • 杂项平台例程成功(Layer-7 设置加载器、射频功率恢复等)。

以下函数成为问题,因此我们必须使用 LD_PRELOAD=/hooks.so 进行修补。

  • get_flash_type() -> 如果返回 4,代码将采用基于文件的路径 (cfm_file_init),否则它将尝试与 MTD 通信(而我们没有 MTD)。
  • get_cfm_blk_size_from_cache()(或其变体 j_get_cfm_blk_size_from_cache)用于咨询配置块大小。
  • 对 Broadcom NVRAM 垫片 (bcm_nvram_get) 的调用不得失败,否则堆栈会假设“NVRAM 已损坏”并进入恢复/重启逻辑。
  • 其他例程如 load_l7setting_file() 和 restore_power() 预期会成功,但它们会触及不存在的硬件/文件。

这是我们的 hooks.c。感谢 azeria-labs 提供原始代码。```C #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <dlfcn.h> #include <string.h>

int j_get_cfm_blk_size_from_cache(const int i) { puts("j_get_cfm_blk_size_from_cache called....\n"); return 0x20000; // 128 KiB block — what the file path expects }

int get_flash_type() { puts("get_flash_type called....\n"); return 4; // force file-backed CFM init, not MTD }

int load_l7setting_file() { puts("load_l7setting_file called....\n"); return 1; // pretend Layer-7 settings loaded OK }

int restore_power(int a, int b) { puts("restore_power called....\n"); return 0; // success (don’t touch RF/power hardware) }

char *bcm_nvram_get(char *key) { char *value = NULL;

if (strcmp(key, "et0macaddr") == 0) { value = strdup("DE:AD:BE:EF:CA:FE"); // any valid MAC works } if (strcmp(key, "sb/1/macaddr") == 0) { value = strdup("DE:AD:BE:EF:CA:FD"); } if (strcmp(key, "default_nvram") == 0) { value = strdup("default_nvram"); // signals “nvram is OK” }

printf("bcm_nvram_get(%s) == %s\n", key, value); return value; }

现在,让我们编译这些文件并将它们放入我们的固件中。
要进行交叉编译,我们可以使用 Bootlin 的预构建 uClibc 工具链。```bash
wget https://toolchains.bootlin.com/downloads/releases/toolchains/armv5-eabi/tarballs/armv5-eabi--uclibc--stable-2020.08-1.tar.bz2
tar xjf armv5-eabi--uclibc--stable-2020.08-1.tar.bz2
export PATH="$PWD/armv5-eabi--uclibc--stable-2020.08-1/bin:$PATH"
ls armv5-eabi--uclibc--stable-2020.08-1/bin | grep gcc

您应该看到类似以下内容:```bash arm-buildroot-linux-uclibcgnueabi-gcc arm-buildroot-linux-uclibcgnueabi-gcc-9.3.0 arm-buildroot-linux-uclibcgnueabi-gcc-9.3.0.br_real arm-buildroot-linux-uclibcgnueabi-gcc-ar arm-buildroot-linux-uclibcgnueabi-gcc.br_real arm-buildroot-linux-uclibcgnueabi-gcc-nm arm-buildroot-linux-uclibcgnueabi-gcc-ranlib arm-linux-gcc arm-linux-gcc-9.3.0 arm-linux-gcc-9.3.0.br_real arm-linux-gcc-ar arm-linux-gcc.br_real arm-linux-gcc-nm arm-linux-gcc-ranlib

现在我们可以用```bash
arm-buildroot-linux-uclibcgnueabi-gcc -shared -fPIC -Os -ldl -Wl,-soname,hooks.so -o hooks.so hooks.c
arm-buildroot-linux-uclibcgnueabi-gcc -Os -s -o cfm_stub cfm_stub.c

然后最后将它们安装到固件中。``` $FIRM = "$HOME/VR/Tenda-AC15-Firmware-V15.03.05.19-9061/rootfs" install -m 0644 ./hooks.so "$FIRM/hooks.so" install -D -m 0755 ./cfm_stub "$FIRM/usr/sbin/cfm_stub"

## 构建 ARM 客户系统
让我们使用 qemu 完整系统设置我们的 arm 客户机。
我在 `~/qsys` 创建了一个目录来存放这个客户系统。

现在在这个目录中,让我们通过以下步骤来设置我们的客户机:```bash
sudo apt-get install -y qemu-system-arm qemu-utils debootstrap qemu-user-static binfmt-support
mkdir -p ~/qsys/rootfs-armhf

sudo debootstrap --arch=armhf --foreign bookworm ~/qsys/rootfs-armhf http://deb.debian.org/debian
sudo cp /usr/bin/qemu-arm-static ~/qsys/rootfs-armhf/usr/bin/
sudo chroot ~/qsys/rootfs-armhf /debootstrap/debootstrap --second-stage

cat | sudo tee ~/qsys/rootfs-armhf/etc/apt/sources.list >/dev/null <<'EOF'
deb http://deb.debian.org/debian bookworm main
EOF

sudo chroot ~/qsys/rootfs-armhf apt-get update

sudo chroot ~/qsys/rootfs-armhf apt-get install -y \
  net-tools iproute2 iputils-ping python3 busybox-syslogd openssh-server \
  ifupdown curl ca-certificates
下载工具