今天分析固件 NAP871。
品牌:Netcore(磊科)
固件名称:_NAP871升级固件.bin(Web 页面标注 Netcore(NAP871)CN-V1.2.31207,2014.11.24,运行时 CGI 上报 V1.2.31209)
下载地址:https://www.netcoretec.com/service-support/download/firmware/312.html
0x00 信息收集:
先解包固件:
binwalk -Me NAP871升级固件.bin


查看 busybox 架构:
file ./squashfs-root/bin/busybox

ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked,
interpreter /lib/ld-uClibc.so.0, no section header
得知架构是 MIPS 32 位大端,uClibc 库,ELF 无节头。
看一下 web 目录和 etc 下的配置:




发现 Web 服务是 boa(/bin/boa,Boa/0.94.14rc21),配置文件 /etc/boa.conf,监听 80 端口,页面根目录 /web。
看启动脚本 /bin/webs,了解 Web 服务是怎么起来的:

#!/bin/sh
...
eval `flash get SUPER_NAME`
eval `flash get SUPER_PASSWORD`
eval `flash get USER_NAME`
eval `flash get USER_PASSWORD`
echo "${USER_NAME}:${USER_PASSWORD}" > /tmp/passwd
echo "${SUPER_NAME}:${SUPER_PASSWORD}" >> /tmp/passwd
...
/bin/boa -p /web -f /etc/boa.conf &
关键信息:boa 是厂商补丁版,用 /tmp/passwd 做 HTTP Basic 认证,账号口令由 webs 脚本从 flash 读出后写入。这个文件后面模拟时要手工 mock。
/web 下只有一个 CGI:/web/cgi-bin/cgitest.cgi,strings 看一下:



导入表里有 apmib_get / apmib_set / system / exec_sh,是典型的 Realtek 方案 AP 配置 CGI,所有业务请求都走它。
前端页面全是预 gzip 的(.htm/.js 都是 gzip 文件,boa 直接返回 Content-encoding: gzip),zcat 解压后分析 JS,在 script/netcore.js 里找到前端调后端的协议:

zcat netcore.js >1.txt

xmlhttp.open("post", $.PATH + url + ".cgi", true);
// $.PATH = "/cgi-bin-igd/" (script/init.js 里定义 cgi_path)
// body 形如:mode_name=<name>&key=val...
也就是说所有业务请求都是 POST /cgi-bin-igd/<name>.cgi,页面加载发 netcore_get,保存配置发 netcore_set。/cgi-bin-igd/ 这个路径在文件系统里不存在,是补丁 boa 内部做的别名。
0x01 固件模拟
架构是 MIPS 大端,所以用 qemu-mips-static。
cd ~/Desktop/NAP871升级固件.bin.extracted/squashfs-root
把 qemu 拷进文件系统,方便 chroot:
cp $(which qemu-mips-static) ./usr/bin

坑 1:binwalk 留下的坏符号链接
binwalk 解压后 tmp、sys、media 是指向 /dev/null 的坏链接,/tmp/passwd 写不进去,直接删掉重建:

sudo rm -f ./tmp ./sys ./media
sudo mkdir tmp sys media

mock 认证文件(默认凭证 guest:guest,后面从 CGI 返回的 JSON 里也能确认):
printf "guest:guest\n" | sudo tee ./tmp/passwd

坑 2:/dev 设备节点缺失(can't open /dev/null)
挂上 /proc,启动 boa:
sudo mount --bind /proc ./proc
sudo chroot . /usr/bin/qemu-mips-static /bin/boa -p /web -f /etc/boa.conf
直接报错退出:
boa.c:196 (main) - can't open /dev/null: No such file or directory

binwalk 用普通用户解压,创建不了设备节点,./dev 里是空的,boa 连 /dev/null 都打不开。也不能 bind-mount 宿主机的 /dev 凑数——后端 CGI 还需要一个可写的假 /dev/mtd(见坑 4),bind-mount 会把它带进宿主机 /dev 造成污染。干脆给 chroot 自建一套 /dev,顺手把 16MB 全零假 flash 也造出来:
sudo bash -c "cd dev && mknod null c 1 3; mknod zero c 1 5; \
mknod console c 5 1; mknod tty c 5 0; mknod urandom c 1 9; chmod 666 null zero urandom"
sudo dd if=/dev/zero of=./dev/mtd bs=1M count=16
sudo chmod 666 ./dev/mtd

坑 3:Can't create PID file!
再次启动 boa,还是报错:

boa 默认要写 /var/run/boa.pid,rootfs 里没有这个目录,补上再启动:
sudo mkdir -p ./var/run ./var/log/boa
sudo chroot . /usr/bin/qemu-mips-static /bin/boa -p /web -f /etc/boa.conf

这次 80 端口正常监听,静态页面能访问了:
坑 4:后端 CGI 段错误
前端通了,轮到后端。如果前面没有造假 /dev/mtd,cgitest.cgi 一跑就崩:
[flash_read:10438] ERROR: open /dev/mtd failed, No such file or directory.
[apmib_init:7134] ERROR: plese restore default(1)
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
原因:apmib 配置库初始化必须读 flash 的硬件配置区。
用 strace 观测到 apmib 读 flash 的三个分区偏移(qemu-user 下 strace 是最可靠的观测手段):
openat(AT_FDCWD, "/dev/mtd", O_RDWR) = 3
lseek(3, 24576, SEEK_SET) # 0x6000 hw setting 硬件配置区
lseek(3, 73728, SEEK_SET) # 0x12000 current setting 当前配置区
lseek(3, 122880, SEEK_SET) # 0x1E000 backup setting 备份配置区
flash 上第一次手动跑 CGI,签名校验失败,自动走 writeDefault() 把出厂默认配置写进假 flash(往 0x6000 写 H601 头、0x12000 写 6G02、0x1E000 写 6g02)。第一次仍以段错误收尾,但配置已经落盘;第二次再跑,签名校验全部通过,CGI 正常输出,不再崩溃:
sudo chroot . /usr/bin/qemu-mips-static /web/cgi-bin/cgitest.cgi # 第 1 次:写默认配置,结尾段错误是预期现象
sudo chroot . /usr/bin/qemu-mips-static /web/cgi-bin/cgitest.cgi # 第 2 次:无任何输出 = 正常
这个假 /dev/mtd 不只是过初始化,还真实承担了配置持久化——后面 netcore_set 的改动都会写进这个文件。
前后端联调验证
带上 guest:guest 访问返回 200,welcome.htm 向导页未授权也可访问

后端读链路(页面加载时前端自动发这个):
curl -u guest:guest -X POST http://127.0.0.1/cgi-bin-igd/netcore_get.cgi \
-d "mode_name=netcore_get&noneed=noneed"
返回完整配置 JSON(型号、版本 V1.2.31209、MAC、SSID NETCORE_2.4G、接口统计、系统日志等),和真机页面数据源一致:

后端写链路,挑 UI 上真实存在、纯配置无副作用的"定时重启"页测(字段名从 web/config/config.js 的 Panels 定义里找):
curl -u guest:guest -X POST http://127.0.0.1/cgi-bin-igd/netcore_set.cgi \
-d "mode_name=netcore_set&reboot_time_enable=1&time_reboot=03&time_reboot2=30"
返回 ["SUCCESS","2"],再发 netcore_get 回读,reboot_time_enable/time_reboot/time_reboot2 都变成了新值,写链路落盘成功:

最终效果
Windows 主机浏览器直接打开 http://127.0.0.1,弹 Basic 认证框,输 guest:guest 进管理界面,菜单、状态页、配置保存全部可用:


0x02 总结
- 这类老 Realtek 方案 AP 是"补丁 boa + 单 CGI"架构,前端协议全在预 gzip 的 JS 里,zcat 后 grep 就能画出调用链。
- CGI 段错误先用 strace -e trace=openat 看缺什么硬件文件;可写的假 flash 文件能让 writeDefault 修复。
- binwalk 解压的 rootfs 常见四个坑:tmp->/dev/null 坏链接、/dev 设备节点全缺(boa 连 /dev/null 都打不开)、缺 /var/run(boa 写不了 PID 文件)、/dev/mtd 缺失(CGI 崩)。
